author | Ed Morley <emorley@mozilla.com> |
Thu, 26 Jul 2012 13:25:04 +0100 | |
changeset 100568 | f50ebe5069160cbc6d6eb7dd74c39c7ec4dcbb86 |
parent 100567 | 29874fe36f9097a2ad100f1905e50c3ad2afcada (current diff) |
parent 100555 | 20db7c6d82cc5bcb0e916bfe72d1ab04d3c3be2d (diff) |
child 100569 | b571e6fddb8fd7abf4b0bdd2cddc93880ab12dab |
push id | 23185 |
push user | mbrubeck@mozilla.com |
push date | Thu, 26 Jul 2012 20:58:28 +0000 |
treeherder | mozilla-central@8a7ad0adcccf [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 17.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
b2g/chrome/content/shell.js | file | annotate | diff | comparison | revisions | |
content/events/src/nsDOMApplicationEvent.cpp | file | annotate | diff | comparison | revisions | |
content/events/src/nsDOMApplicationEvent.h | file | annotate | diff | comparison | revisions | |
content/events/src/nsDOMDeviceLightEvent.cpp | file | annotate | diff | comparison | revisions | |
content/events/src/nsDOMDeviceLightEvent.h | file | annotate | diff | comparison | revisions | |
content/events/src/nsDOMDeviceOrientationEvent.cpp | file | annotate | diff | comparison | revisions | |
content/events/src/nsDOMDeviceOrientationEvent.h | file | annotate | diff | comparison | revisions |
--- a/b2g/chrome/content/dbg-browser-actors.js +++ b/b2g/chrome/content/dbg-browser-actors.js @@ -1,15 +1,19 @@ /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ 'use strict'; +/** + * B2G-specific actors that extend BrowserRootActor and BrowserTabActor, + * overriding some of their methods. + */ /** * The function that creates the root actor. DebuggerServer expects to find this * function in the loaded actors in order to initialize properly. */ function createRootActor(connection) { return new DeviceRootActor(connection); } @@ -19,77 +23,62 @@ function createRootActor(connection) { * The root actor is responsible for the initial 'hello' packet and for * responding to a 'listTabs' request that produces the list of currently open * tabs. * * @param connection DebuggerServerConnection * The conection to the client. */ function DeviceRootActor(connection) { - this.conn = connection; - this._tabActors = new WeakMap(); - this._tabActorPool = null; - this._actorFactories = null; + BrowserRootActor.call(this, connection); this.browser = Services.wm.getMostRecentWindow('navigator:browser'); } -DeviceRootActor.prototype = { - /** - * Return a 'hello' packet as specified by the Remote Debugging Protocol. - */ - sayHello: function DRA_sayHello() { - return { - from: 'root', - applicationType: 'browser', - traits: [] - }; - }, +DeviceRootActor.prototype = new BrowserRootActor(); - /** - * Disconnects the actor from the browser window. - */ - disconnect: function DRA_disconnect() { - let actor = this._tabActors.get(this.browser); - if (actor) { - actor.exit(); - } - }, +/** + * Disconnects the actor from the browser window. + */ +DeviceRootActor.prototype.disconnect = function DRA_disconnect() { + let actor = this._tabActors.get(this.browser); + if (actor) { + actor.exit(); + } +}; - /** - * Handles the listTabs request. Builds a list of actors for the single - * tab (window) running in the process. The actors will survive - * until at least the next listTabs request. - */ - onListTabs: function DRA_onListTabs() { - let actor = this._tabActors.get(this.browser); - if (!actor) { - actor = new DeviceTabActor(this.conn, this.browser); - // this.actorID is set by ActorPool when an actor is put into one. - actor.parentID = this.actorID; - this._tabActors.set(this.browser, actor); - } - - let actorPool = new ActorPool(this.conn); - actorPool.addActor(actor); - - // Now drop the old actorID -> actor map. Actors that still mattered were - // added to the new map, others will go away. - if (this._tabActorPool) { - this.conn.removeActorPool(this._tabActorPool); - } - this._tabActorPool = actorPool; - this.conn.addActorPool(this._tabActorPool); - - return { - 'from': 'root', - 'selected': 0, - 'tabs': [actor.grip()] - }; +/** + * Handles the listTabs request. Builds a list of actors for the single + * tab (window) running in the process. The actors will survive + * until at least the next listTabs request. + */ +DeviceRootActor.prototype.onListTabs = function DRA_onListTabs() { + let actor = this._tabActors.get(this.browser); + if (!actor) { + actor = new DeviceTabActor(this.conn, this.browser); + // this.actorID is set by ActorPool when an actor is put into one. + actor.parentID = this.actorID; + this._tabActors.set(this.browser, actor); } + let actorPool = new ActorPool(this.conn); + actorPool.addActor(actor); + + // Now drop the old actorID -> actor map. Actors that still mattered were + // added to the new map, others will go away. + if (this._tabActorPool) { + this.conn.removeActorPool(this._tabActorPool); + } + this._tabActorPool = actorPool; + this.conn.addActorPool(this._tabActorPool); + + return { + 'from': 'root', + 'selected': 0, + 'tabs': [actor.grip()] + }; }; /** * The request types this actor can handle. */ DeviceRootActor.prototype.requestTypes = { 'listTabs': DeviceRootActor.prototype.onListTabs }; @@ -99,224 +88,63 @@ DeviceRootActor.prototype.requestTypes = * and detaching. * * @param connection DebuggerServerConnection * The connection to the client. * @param browser browser * The browser instance that contains this tab. */ function DeviceTabActor(connection, browser) { - this.conn = connection; - this._browser = browser; + BrowserTabActor.call(this, connection, browser); } -DeviceTabActor.prototype = { - get browser() { - return this._browser; - }, - - get exited() { - return !this.browser; - }, - - get attached() { - return !!this._attached - }, - - _tabPool: null, - get tabActorPool() { - return this._tabPool; - }, - - _contextPool: null, - get contextActorPool() { - return this._contextPool; - }, - - /** - * Add the specified breakpoint to the default actor pool connection, in order - * to be alive as long as the server is. - * - * @param BreakpointActor actor - * The actor object. - */ - addToBreakpointPool: function DTA_addToBreakpointPool(actor) { - this.conn.addActor(actor); - }, - - /** - * Remove the specified breakpint from the default actor pool. - * - * @param string actor - * The actor ID. - */ - removeFromBreakpointPool: function DTA_removeFromBreakpointPool(actor) { - this.conn.removeActor(actor); - }, - - actorPrefix: 'tab', - - grip: function DTA_grip() { - dbg_assert(!this.exited, - 'grip() should not be called on exited browser actor.'); - dbg_assert(this.actorID, - 'tab should have an actorID.'); - return { - 'actor': this.actorID, - 'title': this.browser.title, - 'url': this.browser.document.documentURI - } - }, - - /** - * Called when the actor is removed from the connection. - */ - disconnect: function DTA_disconnect() { - this._detach(); - }, - - /** - * Called by the root actor when the underlying tab is closed. - */ - exit: function DTA_exit() { - if (this.exited) { - return; - } - - if (this.attached) { - this._detach(); - this.conn.send({ - 'from': this.actorID, - 'type': 'tabDetached' - }); - } - - this._browser = null; - }, - - /** - * Does the actual work of attaching to a tab. - */ - _attach: function DTA_attach() { - if (this._attached) { - return; - } - - // Create a pool for tab-lifetime actors. - dbg_assert(!this._tabPool, 'Should not have a tab pool if we were not attached.'); - this._tabPool = new ActorPool(this.conn); - this.conn.addActorPool(this._tabPool); - - // ... and a pool for context-lifetime actors. - this._pushContext(); +DeviceTabActor.prototype = new BrowserTabActor(); - this._attached = true; - }, - - /** - * Creates a thread actor and a pool for context-lifetime actors. It then sets - * up the content window for debugging. - */ - _pushContext: function DTA_pushContext() { - dbg_assert(!this._contextPool, "Can't push multiple contexts"); - - this._contextPool = new ActorPool(this.conn); - this.conn.addActorPool(this._contextPool); - - this.threadActor = new ThreadActor(this); - this._addDebuggees(this.browser.wrappedJSObject); - this._contextPool.addActor(this.threadActor); - }, - - /** - * Add the provided window and all windows in its frame tree as debuggees. - */ - _addDebuggees: function DTA__addDebuggees(content) { - this.threadActor.addDebuggee(content); - let frames = content.frames; - for (let i = 0; i < frames.length; i++) { - this._addDebuggees(frames[i]); - } - }, - - /** - * Exits the current thread actor and removes the context-lifetime actor pool. - * The content window is no longer being debugged after this call. - */ - _popContext: function DTA_popContext() { - dbg_assert(!!this._contextPool, 'No context to pop.'); - - this.conn.removeActorPool(this._contextPool); - this._contextPool = null; - this.threadActor.exit(); - this.threadActor = null; - }, - - /** - * Does the actual work of detaching from a tab. - */ - _detach: function DTA_detach() { - if (!this.attached) { - return; - } - - this._popContext(); - - // Shut down actors that belong to this tab's pool. - this.conn.removeActorPool(this._tabPool); - this._tabPool = null; - - this._attached = false; - }, - - // Protocol Request Handlers - - onAttach: function DTA_onAttach(aRequest) { - if (this.exited) { - return { type: 'exited' }; - } - - this._attach(); - - return { type: 'tabAttached', threadActor: this.threadActor.actorID }; - }, - - onDetach: function DTA_onDetach(aRequest) { - if (!this.attached) { - return { error: 'wrongState' }; - } - - this._detach(); - - return { type: 'detached' }; - }, - - /** - * Prepare to enter a nested event loop by disabling debuggee events. - */ - preNest: function DTA_preNest() { - let windowUtils = this.browser - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - windowUtils.suppressEventHandling(true); - windowUtils.suspendTimeouts(); - }, - - /** - * Prepare to exit a nested event loop by enabling debuggee events. - */ - postNest: function DTA_postNest(aNestData) { - let windowUtils = this.browser - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - windowUtils.resumeTimeouts(); - windowUtils.suppressEventHandling(false); +DeviceTabActor.prototype.grip = function DTA_grip() { + dbg_assert(!this.exited, + 'grip() should not be called on exited browser actor.'); + dbg_assert(this.actorID, + 'tab should have an actorID.'); + return { + 'actor': this.actorID, + 'title': this.browser.title, + 'url': this.browser.document.documentURI } - }; /** - * The request types this actor can handle. + * Creates a thread actor and a pool for context-lifetime actors. It then sets + * up the content window for debugging. + */ +DeviceTabActor.prototype._pushContext = function DTA_pushContext() { + dbg_assert(!this._contextPool, "Can't push multiple contexts"); + + this._contextPool = new ActorPool(this.conn); + this.conn.addActorPool(this._contextPool); + + this.threadActor = new ThreadActor(this); + this._addDebuggees(this.browser.wrappedJSObject); + this._contextPool.addActor(this.threadActor); +}; + +// Protocol Request Handlers + +/** + * Prepare to enter a nested event loop by disabling debuggee events. */ -DeviceTabActor.prototype.requestTypes = { - 'attach': DeviceTabActor.prototype.onAttach, - 'detach': DeviceTabActor.prototype.onDetach +DeviceTabActor.prototype.preNest = function DTA_preNest() { + let windowUtils = this.browser + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + windowUtils.suppressEventHandling(true); + windowUtils.suspendTimeouts(); }; + +/** + * Prepare to exit a nested event loop by enabling debuggee events. + */ +DeviceTabActor.prototype.postNest = function DTA_postNest(aNestData) { + let windowUtils = this.browser + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + windowUtils.resumeTimeouts(); + windowUtils.suppressEventHandling(false); +};
--- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -539,16 +539,17 @@ var WebappsHelper = { } } // Start the debugger server. function startDebugger() { if (!DebuggerServer.initialized) { // Allow remote connections. DebuggerServer.init(function () { return true; }); + DebuggerServer.addBrowserActors(); DebuggerServer.addActors('chrome://browser/content/dbg-browser-actors.js'); } let port = Services.prefs.getIntPref('devtools.debugger.remote-port') || 6000; try { DebuggerServer.openListener(port); } catch (e) { dump('Unable to start debugger server: ' + e + '\n');
--- a/browser/base/content/browser-sets.inc +++ b/browser/base/content/browser-sets.inc @@ -83,27 +83,28 @@ <command id="cmd_fullZoomReduce" oncommand="FullZoom.reduce()"/> <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/> <command id="cmd_fullZoomReset" oncommand="FullZoom.reset()"/> <command id="cmd_fullZoomToggle" oncommand="ZoomManager.toggleZoom();"/> <command id="Browser:OpenLocation" oncommand="openLocation();"/> <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/> <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/> - <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();"/> + <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/> <command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focus();" disabled="true"/> <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/> - <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/> - <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();"/> - <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();"/> - <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();"/> - <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();"/> - <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();"/> - <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();"/> + <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true" hidden="true"/> + <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true" hidden="true"/> + <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true" hidden="true"/> + <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/> + <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/> + <command id="Tools:StyleEditor" oncommand="StyleEditor.toggle();" disabled="true" hidden="true"/> + <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/> <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/> + <command id="Tools:ErrorConsole" oncommand="toJavaScriptConsole()" disabled="true" hidden="true"/> <command id="Tools:Sanitize" oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/> <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/> <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/> <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/> <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/> <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();"/> <command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/> @@ -185,69 +186,60 @@ <broadcaster id="workOfflineMenuitemState"/> <broadcaster id="socialSidebarBroadcaster" hidden="true"/> <!-- DevTools broadcasters --> <broadcaster id="devtoolsMenuBroadcaster_DevToolbar" label="&devToolbarMenu.label;" type="checkbox" autocheck="false" command="Tools:DevToolbar" - key="key_devToolbar" - disabled="true" hidden="true"/> + key="key_devToolbar"/> <broadcaster id="devtoolsMenuBroadcaster_WebConsole" label="&webConsoleCmd.label;" type="checkbox" autocheck="false" key="key_webConsole" command="Tools:WebConsole"/> <broadcaster id="devtoolsMenuBroadcaster_Inspect" label="&inspectMenu.label;" type="checkbox" autocheck="false" command="Tools:Inspect" - key="key_inspect" - disabled="true" hidden="true"/> + key="key_inspect"/> <broadcaster id="devtoolsMenuBroadcaster_Debugger" label="&debuggerMenu.label2;" type="checkbox" autocheck="false" command="Tools:Debugger" - key="key_debugger" - disabled="true" hidden="true"/> + key="key_debugger"/> <broadcaster id="devtoolsMenuBroadcaster_RemoteDebugger" label="&remoteDebuggerMenu.label;" - command="Tools:RemoteDebugger" - disabled="true" hidden="true"/> + command="Tools:RemoteDebugger"/> <broadcaster id="devtoolsMenuBroadcaster_ChromeDebugger" label="&chromeDebuggerMenu.label;" - command="Tools:ChromeDebugger" - disabled="true" hidden="true"/> + command="Tools:ChromeDebugger"/> <broadcaster id="devtoolsMenuBroadcaster_Scratchpad" label="&scratchpad.label;" command="Tools:Scratchpad" - key="key_scratchpad" - disabled="true" hidden="true"/> + key="key_scratchpad"/> <broadcaster id="devtoolsMenuBroadcaster_StyleEditor" label="&styleeditor.label;" type="checkbox" autocheck="false" command="Tools:StyleEditor" - key="key_styleeditor" - disabled="true" hidden="true"/> + key="key_styleeditor"/> <broadcaster id="devtoolsMenuBroadcaster_ResponsiveUI" label="&responsiveDesignTool.label;" type="checkbox" autocheck="false" command="Tools:ResponsiveUI" - key="key_responsiveUI" - disabled="true" hidden="true"/> + key="key_responsiveUI"/> <broadcaster id="devtoolsMenuBroadcaster_PageSource" label="&pageSourceCmd.label;" key="key_viewSource" command="View:PageSource"/> <broadcaster id="devtoolsMenuBroadcaster_ErrorConsole" - hidden="true" label="&errorConsoleCmd.label;" key="key_errorConsole" - oncommand="toJavaScriptConsole();"/> + command="Tools:ErrorConsole"/> <broadcaster id="devtoolsMenuBroadcaster_GetMoreTools" label="&getMoreDevtoolsCmd.label;" oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/> </broadcasterset> <keyset id="mainKeyset"> <key id="key_newNavigator" key="&newNavigatorCmd.key;" @@ -287,17 +279,17 @@ #endif #ifdef XP_GNOME <key id="key_search2" key="&searchFocusUnix.commandkey;" command="Tools:Search" modifiers="accel"/> <key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/> #else <key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/> #endif <key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/> - <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/> + <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" command="Tools:ErrorConsole" modifiers="accel,shift"/> <key id="key_devToolbar" keycode="&devToolbar.keycode;" modifiers="shift" keytext="&devToolbar.keytext;" command="Tools:DevToolbarFocus"/> <key id="key_webConsole" key="&webConsoleCmd.commandkey;" oncommand="HUDConsoleUI.toggleHUD();" #ifdef XP_MACOSX modifiers="accel,alt" #else modifiers="accel,shift" #endif
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1397,101 +1397,101 @@ var gBrowserInit = { window.addEventListener("resize", function resizeHandler(event) { if (event.target == window) setUrlAndSearchBarWidthForConditionalForwardButton(); }); // Enable developer toolbar? let devToolbarEnabled = gPrefService.getBoolPref("devtools.toolbar.enabled"); if (devToolbarEnabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_DevToolbar"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:DevToolbar"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); document.getElementById("Tools:DevToolbarFocus").removeAttribute("disabled"); // Show the toolbar if it was previously visible if (gPrefService.getBoolPref("devtools.toolbar.visible")) { DeveloperToolbar.show(false); } } // Enable Inspector? let enabled = gPrefService.getBoolPref("devtools.inspector.enabled"); if (enabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Inspect"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:Inspect"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } // Enable Debugger? let enabled = gPrefService.getBoolPref("devtools.debugger.enabled"); if (enabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Debugger"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:Debugger"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } // Enable Remote Debugger? let enabled = gPrefService.getBoolPref("devtools.debugger.remote-enabled"); if (enabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_RemoteDebugger"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:RemoteDebugger"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } // Enable Chrome Debugger? let enabled = gPrefService.getBoolPref("devtools.chrome.enabled") && gPrefService.getBoolPref("devtools.debugger.chrome-enabled") && gPrefService.getBoolPref("devtools.debugger.remote-enabled"); if (enabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ChromeDebugger"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:ChromeDebugger"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } // Enable Error Console? // XXX Temporarily always-enabled, see bug 601201 let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled"); if (consoleEnabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ErrorConsole"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:ErrorConsole"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } // Enable Scratchpad in the UI, if the preference allows this. let scratchpadEnabled = gPrefService.getBoolPref(Scratchpad.prefEnabledName); if (scratchpadEnabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_Scratchpad"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:Scratchpad"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } // Enable Style Editor? let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName); if (styleEditorEnabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_StyleEditor"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:StyleEditor"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } #ifdef MENUBAR_CAN_AUTOHIDE // If the user (or the locale) hasn't enabled the top-level "Character // Encoding" menu via the "browser.menu.showCharacterEncoding" preference, // hide it. if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data) document.getElementById("appmenu_charsetMenu").hidden = true; #endif // Enable Responsive UI? let responsiveUIEnabled = gPrefService.getBoolPref("devtools.responsiveUI.enabled"); if (responsiveUIEnabled) { - let broadcaster = document.getElementById("devtoolsMenuBroadcaster_ResponsiveUI"); - broadcaster.removeAttribute("disabled"); - broadcaster.removeAttribute("hidden"); + let cmd = document.getElementById("Tools:ResponsiveUI"); + cmd.removeAttribute("disabled"); + cmd.removeAttribute("hidden"); } let appMenuButton = document.getElementById("appmenu-button"); let appMenuPopup = document.getElementById("appmenu-popup"); if (appMenuButton && appMenuPopup) { let appMenuOpening = null; appMenuButton.addEventListener("mousedown", function(event) { if (event.button == 0)
--- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -1065,35 +1065,35 @@ <tabbrowser id="content" disablehistory="true" flex="1" contenttooltip="aHTMLTooltip" tabcontainer="tabbrowser-tabs" contentcontextmenu="contentAreaContextMenu" autocompletepopup="PopupAutoComplete" onclick="contentAreaClick(event, false);"/> <statuspanel id="statusbar-display" inactive="true"/> </vbox> + <splitter id="devtools-side-splitter" hidden="true"/> + <vbox id="devtools-sidebar-box" hidden="true" + style="min-width: 18em; width: 22em; max-width: 42em;" persist="width"> + <toolbar id="devtools-sidebar-toolbar" + class="devtools-toolbar" + nowindowdrag="true"/> + <deck id="devtools-sidebar-deck" flex="1"/> + </vbox> <splitter id="social-sidebar-splitter" class="chromeclass-extrachrome" observes="socialSidebarBroadcaster"/> <vbox id="social-sidebar-box" class="chromeclass-extrachrome" observes="socialSidebarBroadcaster"> <browser id="social-sidebar-browser" type="content" flex="1" style="min-width: 14em; width: 18em; max-width: 36em;"/> </vbox> - <splitter id="devtools-side-splitter" hidden="true"/> - <vbox id="devtools-sidebar-box" hidden="true" - style="min-width: 18em; width: 22em; max-width: 42em;" persist="width"> - <toolbar id="devtools-sidebar-toolbar" - class="devtools-toolbar" - nowindowdrag="true"/> - <deck id="devtools-sidebar-deck" flex="1"/> - </vbox> <vbox id="browser-border-end" hidden="true" layer="true"/> </hbox> <hbox id="full-screen-warning-container" hidden="true" fadeout="true"> <hbox style="width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. --> <vbox id="full-screen-warning-message" align="center"> <description id="full-screen-domain-text"/> <description class="full-screen-description" value="&fullscreenExitHint.value;"/>
--- a/browser/devtools/tilt/test/Makefile.in +++ b/browser/devtools/tilt/test/Makefile.in @@ -5,21 +5,18 @@ DEPTH = ../../../.. topsrcdir = @top_srcdir@ srcdir = @srcdir@ VPATH = @srcdir@ relativesrcdir = browser/devtools/tilt/test include $(DEPTH)/config/autoconf.mk -MOCHITEST_BROWSER_FILES = head.js - -# browser_tilt* disabled on Linux due to bug 759157 -ifneq (gtk2,$(MOZ_WIDGET_TOOLKIT)) -MOCHITEST_BROWSER_FILES += \ +MOCHITEST_BROWSER_FILES = \ + head.js \ browser_tilt_01_lazy_getter.js \ browser_tilt_02_notifications-seq.js \ browser_tilt_02_notifications.js \ browser_tilt_03_tab_switch.js \ browser_tilt_04_initialization-key.js \ browser_tilt_04_initialization.js \ browser_tilt_05_destruction-esc.js \ browser_tilt_05_destruction-url.js \ @@ -55,11 +52,10 @@ MOCHITEST_BROWSER_FILES += \ browser_tilt_utils03.js \ browser_tilt_utils04.js \ browser_tilt_utils05.js \ browser_tilt_utils06.js \ browser_tilt_utils07.js \ browser_tilt_visualizer.js \ browser_tilt_zoom.js \ $(NULL) -endif include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/webconsole/WebConsoleUtils.jsm +++ b/browser/devtools/webconsole/WebConsoleUtils.jsm @@ -533,17 +533,17 @@ var WebConsoleUtils = { value = ""; presentable = {type: TYPES.GETTER, display: "Getter"}; } else { try { value = aObject[propName]; presentable = this.presentableValueFor(value); } - catch (ex) { + catch (ex) { continue; } } let pair = {}; pair.name = propName; pair.value = presentable.display; pair.inspectable = false; @@ -730,16 +730,18 @@ const STATE_DQUOTE = 3; const OPEN_BODY = "{[(".split(""); const CLOSE_BODY = "}])".split(""); const OPEN_CLOSE_BODY = { "{": "}", "[": "]", "(": ")", }; +const MAX_COMPLETIONS = 256; + /** * Analyses a given string to find the last statement that is interesting for * later completion. * * @param string aStr * A string to analyse. * * @returns object @@ -890,19 +892,19 @@ function JSPropertyProvider(aScope, aInp if (properties.length > 1) { matchProp = properties.pop().trimLeft(); for (let i = 0; i < properties.length; i++) { let prop = properties[i].trim(); if (!prop) { return null; } - // If obj is undefined or null, then there is no chance to run completion - // on it. Exit here. - if (typeof obj === "undefined" || obj === null) { + // If obj is undefined or null (which is what "== null" does), + // then there is no chance to run completion on it. Exit here. + if (obj == null) { return null; } // Check if prop is a getter function on obj. Functions can change other // stuff so we can't execute them to get the next object. Stop here. if (WCU.isNonNativeGetter(obj, prop)) { return null; } @@ -913,34 +915,79 @@ function JSPropertyProvider(aScope, aInp return null; } } } else { matchProp = properties[0].trimLeft(); } - // If obj is undefined or null, then there is no chance to run - // completion on it. Exit here. - if (typeof obj === "undefined" || obj === null) { + // If obj is undefined or null (which is what "== null" does), + // then there is no chance to run completion on it. Exit here. + if (obj == null) { return null; } // Skip Iterators and Generators. if (WCU.isIteratorOrGenerator(obj)) { return null; } - let matches = []; - for (let prop in obj) { - if (prop.indexOf(matchProp) == 0) { - matches.push(prop); - } - } + let matches = Object.keys(getMatchedProps(obj, matchProp)); return { matchProp: matchProp, matches: matches.sort(), }; } +/** + * Get all accessible properties on this object. + * Filter those properties by name. + * Take only a certain number of those. + * + * @param object obj + * Object whose properties we want to collect. + * + * @param string matchProp + * Filter for properties that match this one. + * Defaults to the empty string (which always matches). + * + * @return object + * Object whose keys are all accessible properties on the object. + */ +function getMatchedProps(aObj, aMatchProp = "") +{ + let c = MAX_COMPLETIONS; + let names = {}; // Using an Object to avoid duplicates. + let ownNames = Object.getOwnPropertyNames(aObj); + for (let i = 0; i < ownNames.length; i++) { + if (ownNames[i].indexOf(aMatchProp) == 0) { + if (names[ownNames[i]] != true) { + c--; + if (c < 0) { + return names; + } + names[ownNames[i]] = true; + } + } + } + + // We need to recursively go up the prototype chain. + aObj = Object.getPrototypeOf(aObj); + if (aObj !== null) { + let parentScope = getMatchedProps(aObj, aMatchProp); + for (let name in parentScope) { + if (names[name] != true) { + c--; + if (c < 0) { + return names; + } + names[name] = true; + } + } + } + return names; +} + + return JSPropertyProvider; })(WebConsoleUtils);
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_585991_autocomplete_keys.js @@ -30,43 +30,68 @@ function consoleOpened(aHud) { ok(!popup.isOpen, "popup is not open"); popup._panel.addEventListener("popupshown", function onShown() { popup._panel.removeEventListener("popupshown", onShown, false); ok(popup.isOpen, "popup is open"); - is(popup.itemCount, 4, "popup.itemCount is correct"); + // 4 values, and the following properties: + // __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__ + // hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString + // toSource unwatch valueOf watch constructor. + is(popup.itemCount, 18, "popup.itemCount is correct"); - let sameItems = popup.getItems(); - is(sameItems.every(function(aItem, aIndex) { - return aItem.label == "item" + aIndex; - }), true, "getItems returns back the same items"); + let sameItems = popup.getItems().map(function(e) {return e.label;}); + ok(sameItems.every(function(prop, index) { + return [ + "__defineGetter__", + "__defineSetter__", + "__lookupGetter__", + "__lookupSetter__", + "constructor", + "hasOwnProperty", + "isPrototypeOf", + "item0", + "item1", + "item2", + "item3", + "propertyIsEnumerable", + "toLocaleString", + "toSource", + "toString", + "unwatch", + "valueOf", + "watch", + ][index] === prop}), "getItems returns the items we expect"); is(popup.selectedIndex, -1, "no index is selected"); EventUtils.synthesizeKey("VK_DOWN", {}); - + let prefix = jsterm.inputNode.value.replace(/[\S]/g, " "); is(popup.selectedIndex, 0, "index 0 is selected"); - is(popup.selectedItem.label, "item0", "item0 is selected"); - is(completeNode.value, prefix + "item0", "completeNode.value holds item0"); + is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected"); + is(completeNode.value, prefix + "__defineGetter__", + "completeNode.value holds __defineGetter__"); EventUtils.synthesizeKey("VK_DOWN", {}); is(popup.selectedIndex, 1, "index 1 is selected"); - is(popup.selectedItem.label, "item1", "item1 is selected"); - is(completeNode.value, prefix + "item1", "completeNode.value holds item1"); + is(popup.selectedItem.label, "__defineSetter__", "__defineSetter__ is selected"); + is(completeNode.value, prefix + "__defineSetter__", + "completeNode.value holds __defineSetter__"); EventUtils.synthesizeKey("VK_UP", {}); is(popup.selectedIndex, 0, "index 0 is selected"); - is(popup.selectedItem.label, "item0", "item0 is selected"); - is(completeNode.value, prefix + "item0", "completeNode.value holds item0"); + is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected"); + is(completeNode.value, prefix + "__defineGetter__", + "completeNode.value holds __defineGetter__"); popup._panel.addEventListener("popuphidden", autocompletePopupHidden, false); EventUtils.synthesizeKey("VK_TAB", {}); }, false); jsterm.setInputValue("window.foobarBug585991"); EventUtils.synthesizeKey(".", {}); @@ -78,36 +103,37 @@ function autocompletePopupHidden() let popup = jsterm.autocompletePopup; let completeNode = jsterm.completeNode; let inputNode = jsterm.inputNode; popup._panel.removeEventListener("popuphidden", autocompletePopupHidden, false); ok(!popup.isOpen, "popup is not open"); - is(inputNode.value, "window.foobarBug585991.item0", + is(inputNode.value, "window.foobarBug585991.__defineGetter__", "completion was successful after VK_TAB"); ok(!completeNode.value, "completeNode is empty"); popup._panel.addEventListener("popupshown", function onShown() { popup._panel.removeEventListener("popupshown", onShown, false); ok(popup.isOpen, "popup is open"); - is(popup.itemCount, 4, "popup.itemCount is correct"); + is(popup.itemCount, 18, "popup.itemCount is correct"); is(popup.selectedIndex, -1, "no index is selected"); EventUtils.synthesizeKey("VK_DOWN", {}); - + let prefix = jsterm.inputNode.value.replace(/[\S]/g, " "); is(popup.selectedIndex, 0, "index 0 is selected"); - is(popup.selectedItem.label, "item0", "item0 is selected"); - is(completeNode.value, prefix + "item0", "completeNode.value holds item0"); + is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected"); + is(completeNode.value, prefix + "__defineGetter__", + "completeNode.value holds __defineGetter__"); popup._panel.addEventListener("popuphidden", function onHidden() { popup._panel.removeEventListener("popuphidden", onHidden, false); ok(!popup.isOpen, "popup is not open after VK_ESCAPE"); is(inputNode.value, "window.foobarBug585991.", "completion was cancelled"); @@ -135,39 +161,41 @@ function testReturnKey() let completeNode = jsterm.completeNode; let inputNode = jsterm.inputNode; popup._panel.addEventListener("popupshown", function onShown() { popup._panel.removeEventListener("popupshown", onShown, false); ok(popup.isOpen, "popup is open"); - is(popup.itemCount, 4, "popup.itemCount is correct"); - + is(popup.itemCount, 18, "popup.itemCount is correct"); + is(popup.selectedIndex, -1, "no index is selected"); EventUtils.synthesizeKey("VK_DOWN", {}); let prefix = jsterm.inputNode.value.replace(/[\S]/g, " "); is(popup.selectedIndex, 0, "index 0 is selected"); - is(popup.selectedItem.label, "item0", "item0 is selected"); - is(completeNode.value, prefix + "item0", "completeNode.value holds item0"); + is(popup.selectedItem.label, "__defineGetter__", "__defineGetter__ is selected"); + is(completeNode.value, prefix + "__defineGetter__", + "completeNode.value holds __defineGetter__"); EventUtils.synthesizeKey("VK_DOWN", {}); is(popup.selectedIndex, 1, "index 1 is selected"); - is(popup.selectedItem.label, "item1", "item1 is selected"); - is(completeNode.value, prefix + "item1", "completeNode.value holds item1"); + is(popup.selectedItem.label, "__defineSetter__", "__defineSetter__ is selected"); + is(completeNode.value, prefix + "__defineSetter__", + "completeNode.value holds __defineSetter__"); popup._panel.addEventListener("popuphidden", function onHidden() { popup._panel.removeEventListener("popuphidden", onHidden, false); ok(!popup.isOpen, "popup is not open after VK_RETURN"); - is(inputNode.value, "window.foobarBug585991.item1", + is(inputNode.value, "window.foobarBug585991.__defineSetter__", "completion was successful after VK_RETURN"); ok(!completeNode.value, "completeNode is empty"); executeSoon(finishTest); }, false); EventUtils.synthesizeKey("VK_RETURN", {});
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_651501_document_body_autocomplete.js @@ -29,18 +29,22 @@ function consoleOpened(aHud) { ok(!popup.isOpen, "popup is not open"); popup._panel.addEventListener("popupshown", function onShown() { popup._panel.removeEventListener("popupshown", onShown, false); ok(popup.isOpen, "popup is open"); + // |props| values, and the following properties: + // __defineGetter__ __defineSetter__ __lookupGetter__ __lookupSetter__ + // constructor hasOwnProperty isPrototypeOf propertyIsEnumerable + // toLocaleString toSource toString unwatch valueOf watch. let props = WCU.namesAndValuesOf(content.wrappedJSObject.document.body); - is(popup.itemCount, props.length, "popup.itemCount is correct"); + is(popup.itemCount, 14 + props.length, "popup.itemCount is correct"); popup._panel.addEventListener("popuphidden", autocompletePopupHidden, false); EventUtils.synthesizeKey("VK_ESCAPE", {}); }, false); jsterm.setInputValue("document.body"); EventUtils.synthesizeKey(".", {});
--- a/browser/devtools/webconsole/test/browser_webconsole_completion.js +++ b/browser/devtools/webconsole/test/browser_webconsole_completion.js @@ -45,16 +45,24 @@ function testCompletion(hud) { jsterm.complete(jsterm.COMPLETE_FORWARD, testNext); yield; is(input.value, "document", "'docu' tab completion"); is(input.selectionStart, 8, "start selection is alright"); is(input.selectionEnd, 8, "end selection is alright"); is(jsterm.completeNode.value.replace(/ /g, ""), "", "'docu' completed"); + // Test typing 'window.O' and press tab. + input.value = "window.O"; + input.setSelectionRange(8, 8); + jsterm.complete(jsterm.COMPLETE_FORWARD, testNext); + yield; + + is(input.value, "window.Object", "'window.O' tab completion"); + // Test typing 'document.getElem'. input.value = "document.getElem"; input.setSelectionRange(16, 16); jsterm.complete(jsterm.COMPLETE_FORWARD, testNext); yield; is(input.value, "document.getElem", "'document.getElem' completion"); is(jsterm.completeNode.value, " entById", "'document.getElem' completion");
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties +++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties @@ -449,66 +449,66 @@ addonDesc=Manipulate add-ons # LOCALIZATION NOTE (addonListDesc) A very short description of the 'addon list' # command. This string is designed to be shown in a menu alongside the command # name, which is why it should be as short as possible. addonListDesc=List installed add-ons # LOCALIZATION NOTE (addonListTypeDesc) A very short description of the # 'addon list <type>' command. This string is designed to be shown in a menu # alongside the command name, which is why it should be as short as possible. -addonListTypeDesc=Select an addon type +addonListTypeDesc=Select an add-on type # LOCALIZATION NOTE (addonListDictionaryHeading, addonListExtensionHeading, # addonListLocaleHeading, addonListPluginHeading, addonListThemeHeading, # addonListUnknownHeading) Used in the output of the 'addon list' command as the # first line of output. addonListDictionaryHeading=The following dictionaries are currently installed: addonListExtensionHeading=The following extensions are currently installed: addonListLocaleHeading=The following locales are currently installed: addonListPluginHeading=The following plugins are currently installed: addonListThemeHeading=The following themes are currently installed: -addonListAllHeading=The following addons are currently installed: -addonListUnknownHeading=The following addons of the selected type are currently installed: +addonListAllHeading=The following add-ons are currently installed: +addonListUnknownHeading=The following add-ons of the selected type are currently installed: # LOCALIZATION NOTE (addonNameDesc) A very short description of the -# name parameter of numerous addon commands. This string is designed to be shown +# name parameter of numerous add-on commands. This string is designed to be shown # in a menu alongside the command name, which is why it should be as short as # possible. addonNameDesc=The name of the add-on # LOCALIZATION NOTE (addonNoneOfType) Used in the output of the 'addon list' -# command when a search for addons of a particular type were not found. -addonNoneOfType=There are no addons of that type installed. +# command when a search for add-ons of a particular type were not found. +addonNoneOfType=There are no add-ons of that type installed. # LOCALIZATION NOTE (addonEnableDesc) A very short description of the # 'addon enable <type>' command. This string is designed to be shown in a menu # alongside the command name, which is why it should be as short as possible. addonEnableDesc=Enable the specified add-on # LOCALIZATION NOTE (addonAlreadyEnabled) Used in the output of the -# 'addon enable' command when an attempt is made to enable an addon is already +# 'addon enable' command when an attempt is made to enable an add-on is already # enabled. addonAlreadyEnabled=%S is already enabled. # LOCALIZATION NOTE (addonEnabled) Used in the output of the 'addon enable' -# command when an addon is enabled. +# command when an add-on is enabled. addonEnabled=%S enabled. # LOCALIZATION NOTE (addonDisableDesc) A very short description of the # 'addon disable <type>' command. This string is designed to be shown in a menu # alongside the command name, which is why it should be as short as possible. addonDisableDesc=Disable the specified add-on # LOCALIZATION NOTE (addonAlreadyDisabled) Used in the output of the -# 'addon disable' command when an attempt is made to disable an addon is already +# 'addon disable' command when an attempt is made to disable an add-on is already # disabled. addonAlreadyDisabled=%S is already disabled. # LOCALIZATION NOTE (addonDisabled) Used in the output of the 'addon disable' -# command when an addon is disabled. +# command when an add-on is disabled. addonDisabled=%S disabled. # LOCALIZATION NOTE (exportDesc) A very short description of the 'export' # command. This string is designed to be shown in a menu alongside the command # name, which is why it should be as short as possible. exportDesc=Export resources # LOCALIZATION NOTE (exportHtmlDesc) A very short description of the 'export
--- a/content/events/src/Makefile.in +++ b/content/events/src/Makefile.in @@ -31,18 +31,16 @@ CPPSRCS = \ nsDOMUIEvent.cpp \ nsDOMKeyboardEvent.cpp \ nsDOMTextEvent.cpp \ nsDOMMouseEvent.cpp \ nsDOMMouseScrollEvent.cpp \ nsDOMDragEvent.cpp \ nsDOMMutationEvent.cpp \ nsDOMPopupBlockedEvent.cpp \ - nsDOMDeviceLightEvent.cpp \ - nsDOMDeviceOrientationEvent.cpp \ nsDOMDeviceMotionEvent.cpp \ nsDOMBeforeUnloadEvent.cpp \ nsDOMXULCommandEvent.cpp \ nsDOMCommandEvent.cpp \ nsDOMMessageEvent.cpp \ nsPaintRequest.cpp \ nsPrivateTextRange.cpp \ nsXMLEventsManager.cpp \ @@ -60,17 +58,16 @@ CPPSRCS = \ nsDOMMozTouchEvent.cpp \ nsDOMEventTargetHelper.cpp \ nsDOMScrollAreaEvent.cpp \ nsDOMTransitionEvent.cpp \ nsDOMAnimationEvent.cpp \ nsDOMSettingsEvent.cpp \ nsDOMTouchEvent.cpp \ nsDOMCompositionEvent.cpp \ - nsDOMApplicationEvent.cpp \ $(NULL) ifdef MOZ_B2G_RIL CPPSRCS += \ nsDOMWifiEvent.cpp \ $(NULL) endif
deleted file mode 100644 --- a/content/events/src/nsDOMApplicationEvent.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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 "nsDOMApplicationEvent.h" -#include "nsContentUtils.h" -#include "DictionaryHelpers.h" -#include "nsDOMClassInfoID.h" - -DOMCI_DATA(MozApplicationEvent, nsDOMMozApplicationEvent) - -NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMMozApplicationEvent) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mApplication) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mApplication) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMMozApplicationEvent) - NS_INTERFACE_MAP_ENTRY(nsIDOMMozApplicationEvent) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozApplicationEvent) -NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent) - -NS_IMPL_ADDREF_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent) -NS_IMPL_RELEASE_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent) - -NS_IMETHODIMP -nsDOMMozApplicationEvent::GetApplication(mozIDOMApplication** aApplication) -{ - NS_IF_ADDREF(*aApplication = mApplication); - return NS_OK; -} - -NS_IMETHODIMP -nsDOMMozApplicationEvent::InitMozApplicationEvent(const nsAString& aType, - bool aCanBubble, - bool aCancelable, - mozIDOMApplication* aApplication) -{ - nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable); - NS_ENSURE_SUCCESS(rv, rv); - - mApplication = aApplication; - - return NS_OK; -} - -nsresult -nsDOMMozApplicationEvent::InitFromCtor(const nsAString& aType, JSContext* aCx, jsval* aVal) -{ - mozilla::dom::MozApplicationEventInit d; - nsresult rv = d.Init(aCx, aVal); - NS_ENSURE_SUCCESS(rv, rv); - return InitMozApplicationEvent(aType, d.bubbles, d.cancelable, d.application); -} - -nsresult -NS_NewDOMMozApplicationEvent(nsIDOMEvent** aInstancePtrResult, - nsPresContext* aPresContext, - nsEvent* aEvent) -{ - nsDOMMozApplicationEvent* e = new nsDOMMozApplicationEvent(aPresContext, aEvent); - return CallQueryInterface(e, aInstancePtrResult); -}
deleted file mode 100644 --- a/content/events/src/nsDOMApplicationEvent.h +++ /dev/null @@ -1,32 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* 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/. */ - -#ifndef nsDOMApplicationEvent_h__ -#define nsDOMApplicationEvent_h__ - -#include "nsIDOMApplicationRegistry.h" -#include "nsDOMEvent.h" - -class nsDOMMozApplicationEvent : public nsDOMEvent, - public nsIDOMMozApplicationEvent -{ -public: - nsDOMMozApplicationEvent(nsPresContext* aPresContext, nsEvent* aEvent) - : nsDOMEvent(aPresContext, aEvent) {} - - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsDOMMozApplicationEvent, nsDOMEvent) - // Forward to base class - NS_FORWARD_TO_NSDOMEVENT - - NS_DECL_NSIDOMMOZAPPLICATIONEVENT - - virtual nsresult InitFromCtor(const nsAString& aType, JSContext* aCx, jsval* aVal); - -private: - nsCOMPtr<mozIDOMApplication> mApplication; -}; - -#endif // nsDOMContactChangeEvent_h__ \ No newline at end of file
deleted file mode 100644 --- a/content/events/src/nsDOMDeviceLightEvent.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* 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 "nsDOMClassInfoID.h" -#include "nsDOMDeviceLightEvent.h" -#include "DictionaryHelpers.h" - -NS_IMPL_ADDREF_INHERITED(nsDOMDeviceLightEvent, nsDOMEvent) -NS_IMPL_RELEASE_INHERITED(nsDOMDeviceLightEvent, nsDOMEvent) - -DOMCI_DATA(DeviceLightEvent, nsDOMDeviceLightEvent) - -NS_INTERFACE_MAP_BEGIN(nsDOMDeviceLightEvent) - NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceLightEvent) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceLightEvent) -NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent) - -NS_IMETHODIMP -nsDOMDeviceLightEvent::InitDeviceLightEvent(const nsAString & aEventTypeArg, - bool aCanBubbleArg, - bool aCancelableArg, - double aValue) -{ - nsresult rv = nsDOMEvent::InitEvent(aEventTypeArg, aCanBubbleArg, aCancelableArg); - NS_ENSURE_SUCCESS(rv, rv); - - mValue = aValue; - return NS_OK; -} - -NS_IMETHODIMP -nsDOMDeviceLightEvent::GetValue(double *aValue) -{ - NS_ENSURE_ARG_POINTER(aValue); - *aValue = mValue; - return NS_OK; -} - -nsresult -nsDOMDeviceLightEvent::InitFromCtor(const nsAString& aType, - JSContext* aCx, jsval* aVal) -{ - mozilla::dom::DeviceLightEventInit d; - nsresult rv = d.Init(aCx, aVal); - NS_ENSURE_SUCCESS(rv, rv); - return InitDeviceLightEvent(aType, d.bubbles, d.cancelable, d.value); -} - -nsresult -NS_NewDOMDeviceLightEvent(nsIDOMEvent** aInstancePtrResult, - nsPresContext* aPresContext, - nsEvent *aEvent) -{ - NS_ENSURE_ARG_POINTER(aInstancePtrResult); - nsDOMDeviceLightEvent* it = new nsDOMDeviceLightEvent(aPresContext, aEvent); - return CallQueryInterface(it, aInstancePtrResult); -}
deleted file mode 100644 --- a/content/events/src/nsDOMDeviceLightEvent.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 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/. */ - -#ifndef nsDOMDeviceLightEvent_h__ -#define nsDOMDeviceLightEvent_h__ - -#include "nsIDOMDeviceLightEvent.h" -#include "nsDOMEvent.h" - -class nsDOMDeviceLightEvent - : public nsDOMEvent - , public nsIDOMDeviceLightEvent -{ -public: - - nsDOMDeviceLightEvent(nsPresContext* aPresContext, nsEvent* aEvent) - : nsDOMEvent(aPresContext, aEvent), - mValue(0) {} - - NS_DECL_ISUPPORTS_INHERITED - - // Forward to nsDOMEvent - NS_FORWARD_TO_NSDOMEVENT - - // nsIDOMDeviceLightEvent Interface - NS_DECL_NSIDOMDEVICELIGHTEVENT - - virtual nsresult InitFromCtor(const nsAString& aType, - JSContext* aCx, - jsval* aVal); -protected: - double mValue; -}; - -#endif
deleted file mode 100644 --- a/content/events/src/nsDOMDeviceOrientationEvent.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* 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 "nsDOMClassInfoID.h" -#include "nsDOMDeviceOrientationEvent.h" - -NS_IMPL_ADDREF_INHERITED(nsDOMDeviceOrientationEvent, nsDOMEvent) -NS_IMPL_RELEASE_INHERITED(nsDOMDeviceOrientationEvent, nsDOMEvent) - -DOMCI_DATA(DeviceOrientationEvent, nsDOMDeviceOrientationEvent) - -NS_INTERFACE_MAP_BEGIN(nsDOMDeviceOrientationEvent) - NS_INTERFACE_MAP_ENTRY(nsIDOMDeviceOrientationEvent) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DeviceOrientationEvent) -NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent) - -NS_IMETHODIMP nsDOMDeviceOrientationEvent::InitDeviceOrientationEvent(const nsAString & aEventTypeArg, - bool aCanBubbleArg, - bool aCancelableArg, - double aAlpha, - double aBeta, - double aGamma, - bool aAbsolute) -{ - nsresult rv = nsDOMEvent::InitEvent(aEventTypeArg, aCanBubbleArg, aCancelableArg); - NS_ENSURE_SUCCESS(rv, rv); - - mAlpha = aAlpha; - mBeta = aBeta; - mGamma = aGamma; - mAbsolute = aAbsolute; - - return NS_OK; -} - -NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetAlpha(double *aAlpha) -{ - NS_ENSURE_ARG_POINTER(aAlpha); - - *aAlpha = mAlpha; - return NS_OK; -} - -NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetBeta(double *aBeta) -{ - NS_ENSURE_ARG_POINTER(aBeta); - - *aBeta = mBeta; - return NS_OK; -} - -NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetGamma(double *aGamma) -{ - NS_ENSURE_ARG_POINTER(aGamma); - - *aGamma = mGamma; - return NS_OK; -} - -NS_IMETHODIMP nsDOMDeviceOrientationEvent::GetAbsolute(bool *aAbsolute) -{ - NS_ENSURE_ARG_POINTER(aAbsolute); - - *aAbsolute = mAbsolute; - return NS_OK; -} - -nsresult NS_NewDOMDeviceOrientationEvent(nsIDOMEvent** aInstancePtrResult, - nsPresContext* aPresContext, - nsEvent *aEvent) -{ - NS_ENSURE_ARG_POINTER(aInstancePtrResult); - - nsDOMDeviceOrientationEvent* it = new nsDOMDeviceOrientationEvent(aPresContext, aEvent); - if (nsnull == it) { - return NS_ERROR_OUT_OF_MEMORY; - } - - return CallQueryInterface(it, aInstancePtrResult); -}
deleted file mode 100644 --- a/content/events/src/nsDOMDeviceOrientationEvent.h +++ /dev/null @@ -1,36 +0,0 @@ -/* 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/. */ - -#ifndef nsDOMDeviceOrientationEvent_h__ -#define nsDOMDeviceOrientationEvent_h__ - -#include "nsIDOMDeviceOrientationEvent.h" -#include "nsDOMEvent.h" - -class nsDOMDeviceOrientationEvent : public nsDOMEvent, - public nsIDOMDeviceOrientationEvent -{ -public: - - nsDOMDeviceOrientationEvent(nsPresContext* aPresContext, nsEvent* aEvent) - : nsDOMEvent(aPresContext, aEvent), - mAlpha(0), - mBeta(0), - mGamma(0), - mAbsolute(true) {} - - NS_DECL_ISUPPORTS_INHERITED - - // Forward to nsDOMEvent - NS_FORWARD_TO_NSDOMEVENT - - // nsIDOMDeviceOrientationEvent Interface - NS_DECL_NSIDOMDEVICEORIENTATIONEVENT - -protected: - double mAlpha, mBeta, mGamma; - bool mAbsolute; -}; - -#endif
--- a/content/events/test/test_eventctors.html +++ b/content/events/test/test_eventctors.html @@ -420,16 +420,34 @@ is(receivedEvent, e, "Wrong event!"); // DeviceLightEvent e = new DeviceLightEvent("hello", {value: 1} ); ok(e.type, "hello", "Wrong event type!"); ok(!e.isTrusted, "Event should not be trusted"); is(e.value, 1, "value should be 1"); document.dispatchEvent(e); is(receivedEvent, e, "Wrong event!"); +// DeviceOrientationEvent +e = new DeviceOrientationEvent("hello"); +ok(e.type, "hello", "Wrong event type!"); +ok(!e.isTrusted, "Event should not be trusted"); +is(e.alpha, 0); +is(e.beta, 0); +is(e.gamma, 0); +is(e.absolute, false); + +e = new DeviceOrientationEvent("hello", { alpha: 1, beta: 2, gamma: 3, absolute: true } ); +ok(e.type, "hello", "Wrong event type!"); +ok(!e.isTrusted, "Event should not be trusted"); +is(e.alpha, 1); +is(e.beta, 2); +is(e.gamma, 3); +is(e.absolute, true); +document.dispatchEvent(e); +is(receivedEvent, e, "Wrong event!"); // MouseEvent try { e = new MouseEvent(); } catch(exp) { ex = true; }
--- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -293,18 +293,16 @@ #include "nsIDOMMozCSSKeyframesRule.h" #include "nsIDOMCSSPrimitiveValue.h" #include "nsIDOMCSSStyleRule.h" #include "nsIDOMCSSStyleSheet.h" #include "nsDOMCSSValueList.h" #define MOZ_GENERATED_EVENTS_INCLUDES #include "GeneratedEvents.h" #undef MOZ_GENERATED_EVENTS_INCLUDES -#include "nsIDOMDeviceLightEvent.h" -#include "nsIDOMDeviceOrientationEvent.h" #include "nsIDOMDeviceMotionEvent.h" #include "nsIDOMRange.h" #include "nsIDOMNodeIterator.h" #include "nsIDOMTreeWalker.h" #include "nsIDOMXULDocument.h" #include "nsIDOMXULElement.h" #include "nsIDOMXULCommandDispatcher.h" #include "nsIDOMCrypto.h" @@ -810,30 +808,24 @@ static nsDOMClassInfoData sClassInfoData NS_DEFINE_CLASSINFO_DATA(DragEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(KeyboardEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(CompositionEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(PopupBlockedEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - // Device Light - NS_DEFINE_CLASSINFO_DATA(DeviceLightEvent, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) #define MOZ_GENERATED_EVENT_LIST #define MOZ_GENERATED_EVENT(_event_interface) \ NS_DEFINE_CLASSINFO_DATA(_event_interface, nsDOMGenericSH, \ DOM_DEFAULT_SCRIPTABLE_FLAGS) #include "GeneratedEvents.h" #undef MOZ_GENERATED_EVENT_LIST - // Device Orientation - NS_DEFINE_CLASSINFO_DATA(DeviceOrientationEvent, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(DeviceMotionEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(DeviceAcceleration, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(DeviceRotationRate, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) // Misc HTML classes @@ -1636,18 +1628,16 @@ static nsDOMClassInfoData sClassInfoData NS_DEFINE_CLASSINFO_DATA(MediaQueryList, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MutationObserver, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MutationRecord, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MozSettingsEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(MozApplicationEvent, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) #ifdef MOZ_B2G_RIL NS_DEFINE_CLASSINFO_DATA(MozWifiStatusChangeEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(MozWifiConnectionInfoEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(Telephony, nsEventTargetSH, EVENTTARGET_SCRIPTABLE_FLAGS) @@ -1722,20 +1712,18 @@ static const nsContractIDMapData kConstr nsIDOMEvent* e = nsnull; \ nsresult rv = NS_NewDOM##_class(&e, nsnull, nsnull); \ *aInstancePtrResult = e; \ return rv; \ } NS_DEFINE_EVENT_CTOR(Event) NS_DEFINE_EVENT_CTOR(MozSettingsEvent) -NS_DEFINE_EVENT_CTOR(MozApplicationEvent) NS_DEFINE_EVENT_CTOR(UIEvent) NS_DEFINE_EVENT_CTOR(MouseEvent) -NS_DEFINE_EVENT_CTOR(DeviceLightEvent) #ifdef MOZ_B2G_RIL NS_DEFINE_EVENT_CTOR(MozWifiStatusChangeEvent) NS_DEFINE_EVENT_CTOR(MozWifiConnectionInfoEvent) #endif #define MOZ_GENERATED_EVENT_LIST #define MOZ_GENERATED_EVENT(_event_interface) \ NS_DEFINE_EVENT_CTOR(_event_interface) @@ -1770,20 +1758,18 @@ struct nsConstructorFuncMapData static const nsConstructorFuncMapData kConstructorFuncMap[] = { NS_DEFINE_CONSTRUCTOR_FUNC_DATA(Blob, nsDOMMultipartFile::NewBlob) NS_DEFINE_CONSTRUCTOR_FUNC_DATA(File, nsDOMFileFile::NewFile) NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozBlobBuilder, NS_NewBlobBuilder) NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(Event) NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozSettingsEvent) - NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozApplicationEvent) NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(UIEvent) NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MouseEvent) - NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(DeviceLightEvent) NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(StorageEvent) #ifdef MOZ_B2G_RIL NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozWifiStatusChangeEvent) NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MozWifiConnectionInfoEvent) #endif #define MOZ_GENERATED_EVENT_LIST #define MOZ_GENERATED_EVENT(_event_interface) \ NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(_event_interface) @@ -2595,35 +2581,25 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(PopupBlockedEvent, nsIDOMPopupBlockedEvent) DOM_CLASSINFO_MAP_ENTRY(nsIDOMPopupBlockedEvent) DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(DeviceLightEvent, nsIDOMDeviceLightEvent) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceLightEvent) - DOM_CLASSINFO_EVENT_MAP_ENTRIES - DOM_CLASSINFO_MAP_END - #define MOZ_GENERATED_EVENT_LIST #define MOZ_GENERATED_EVENT(_event_interface) \ DOM_CLASSINFO_MAP_BEGIN(_event_interface, nsIDOM##_event_interface) \ DOM_CLASSINFO_MAP_ENTRY(nsIDOM##_event_interface) \ DOM_CLASSINFO_EVENT_MAP_ENTRIES \ DOM_CLASSINFO_MAP_END #include "GeneratedEvents.h" #undef MOZ_GENERATED_EVENT_LIST - DOM_CLASSINFO_MAP_BEGIN(DeviceOrientationEvent, nsIDOMDeviceOrientationEvent) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceOrientationEvent) - DOM_CLASSINFO_EVENT_MAP_ENTRIES - DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(DeviceMotionEvent, nsIDOMDeviceMotionEvent) DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceMotionEvent) DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(DeviceAcceleration, nsIDOMDeviceAcceleration) DOM_CLASSINFO_MAP_ENTRY(nsIDOMDeviceAcceleration) DOM_CLASSINFO_EVENT_MAP_ENTRIES @@ -4404,21 +4380,16 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_ENTRY(nsIDOMMutationRecord) DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(MozSettingsEvent, nsIDOMMozSettingsEvent) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSettingsEvent) DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(MozApplicationEvent, nsIDOMMozApplicationEvent) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozApplicationEvent) - DOM_CLASSINFO_EVENT_MAP_ENTRIES - DOM_CLASSINFO_MAP_END - #ifdef MOZ_B2G_RIL DOM_CLASSINFO_MAP_BEGIN(MozWifiStatusChangeEvent, nsIDOMMozWifiStatusChangeEvent) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozWifiStatusChangeEvent) DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_BEGIN(MozWifiConnectionInfoEvent, nsIDOMMozWifiConnectionInfoEvent) DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozWifiConnectionInfoEvent)
--- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -38,22 +38,20 @@ DOMCI_CLASS(Event) DOMCI_CLASS(MutationEvent) DOMCI_CLASS(UIEvent) DOMCI_CLASS(MouseEvent) DOMCI_CLASS(MouseScrollEvent) DOMCI_CLASS(DragEvent) DOMCI_CLASS(KeyboardEvent) DOMCI_CLASS(CompositionEvent) DOMCI_CLASS(PopupBlockedEvent) -DOMCI_CLASS(DeviceLightEvent) #define MOZ_GENERATED_EVENT_LIST #define MOZ_GENERATED_EVENT(_event_interface) DOMCI_CLASS(_event_interface) #include "GeneratedEvents.h" #undef MOZ_GENERATED_EVENT_LIST -DOMCI_CLASS(DeviceOrientationEvent) DOMCI_CLASS(DeviceMotionEvent) DOMCI_CLASS(DeviceAcceleration) DOMCI_CLASS(DeviceRotationRate) // HTML classes DOMCI_CLASS(HTMLDocument) DOMCI_CLASS(HTMLOptionsCollection) DOMCI_CLASS(HTMLCollection) @@ -504,18 +502,16 @@ DOMCI_CLASS(MozCSSKeyframesRule) DOMCI_CLASS(MediaQueryList) DOMCI_CLASS(MutationObserver) DOMCI_CLASS(MutationRecord) DOMCI_CLASS(MozSettingsEvent) -DOMCI_CLASS(MozApplicationEvent) - #ifdef MOZ_B2G_RIL DOMCI_CLASS(MozWifiStatusChangeEvent) DOMCI_CLASS(MozWifiConnectionInfoEvent) DOMCI_CLASS(Telephony) DOMCI_CLASS(TelephonyCall) DOMCI_CLASS(CallEvent) DOMCI_CLASS(MozVoicemail) DOMCI_CLASS(MozVoicemailEvent)
--- a/dom/interfaces/apps/Makefile.in +++ b/dom/interfaces/apps/Makefile.in @@ -12,11 +12,12 @@ include $(DEPTH)/config/autoconf.mk MODULE = dom XPIDL_MODULE = dom_apps GRE_MODULE = 1 XPIDLSRCS = \ nsIDOMApplicationRegistry.idl \ nsIAppsService.idl \ + nsIDOMMozApplicationEvent.idl \ $(NULL) include $(topsrcdir)/config/rules.mk
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl +++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl @@ -1,14 +1,13 @@ /* 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 "domstubs.idl" -#include "nsIDOMEvent.idl" #include "nsIDOMEventTarget.idl" interface nsIDOMDOMRequest; interface nsIArray; [scriptable, uuid(9583b825-46b1-4e8f-bb48-9fed660a95e6)] interface mozIDOMApplication : nsISupports { @@ -38,32 +37,16 @@ interface mozIDOMApplication : nsISuppo */ attribute nsIDOMEventListener onprogress; /* startPoint will be used when several launch_path exists for an app */ nsIDOMDOMRequest launch([optional] in DOMString startPoint); nsIDOMDOMRequest uninstall(); }; -[scriptable, builtinclass, uuid(8f2bfba8-f10e-4f63-a5e0-7a7056e1dbe6)] -interface nsIDOMMozApplicationEvent : nsIDOMEvent -{ - readonly attribute mozIDOMApplication application; - - [noscript] void initMozApplicationEvent(in DOMString aType, - in boolean aCanBubble, - in boolean aCancelable, - in mozIDOMApplication aApplication); -}; - -dictionary MozApplicationEventInit : EventInit -{ - mozIDOMApplication application; -}; - [scriptable, uuid(bd304874-d532-4e13-8034-544211445583)] interface mozIDOMApplicationMgmt : nsISupports { /** * the request will return the all the applications installed. Only accessible * to privileged callers. */ nsIDOMDOMRequest getAll();
new file mode 100644 --- /dev/null +++ b/dom/interfaces/apps/nsIDOMMozApplicationEvent.idl @@ -0,0 +1,23 @@ +/* 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 "nsIDOMEvent.idl" + +interface mozIDOMApplication; + +[scriptable, builtinclass, uuid(8f2bfba8-f10e-4f63-a5e0-7a7056e1dbe6)] +interface nsIDOMMozApplicationEvent : nsIDOMEvent +{ + readonly attribute mozIDOMApplication application; + + [noscript] void initMozApplicationEvent(in DOMString aType, + in boolean aCanBubble, + in boolean aCancelable, + in mozIDOMApplication aApplication); +}; + +dictionary MozApplicationEventInit : EventInit +{ + mozIDOMApplication application; +};
--- a/dom/interfaces/events/nsIDOMDeviceOrientationEvent.idl +++ b/dom/interfaces/events/nsIDOMDeviceOrientationEvent.idl @@ -36,8 +36,15 @@ interface nsIDOMDeviceOrientationEvent : */ readonly attribute double alpha; readonly attribute double beta; readonly attribute double gamma; readonly attribute boolean absolute; }; +dictionary DeviceOrientationEventInit : EventInit +{ + double alpha; + double beta; + double gamma; + boolean absolute; +};
--- a/dom/interfaces/events/nsIDOMEvent.idl +++ b/dom/interfaces/events/nsIDOMEvent.idl @@ -199,20 +199,16 @@ nsresult NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsKeyEvent *aEvent); nsresult NS_NewDOMCompositionEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsCompositionEvent *aEvent); nsresult NS_NewDOMMutationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsMutationEvent* aEvent); nsresult NS_NewDOMPopupBlockedEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent); nsresult -NS_NewDOMDeviceOrientationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent); -nsresult -NS_NewDOMDeviceLightEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent); -nsresult NS_NewDOMDeviceMotionEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent); nsresult NS_NewDOMTextEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsTextEvent* aEvent); nsresult NS_NewDOMBeforeUnloadEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, nsEvent* aEvent); nsresult NS_NewDOMSVGEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsEvent* aEvent); nsresult
--- a/js/xpconnect/src/dictionary_helper_gen.conf +++ b/js/xpconnect/src/dictionary_helper_gen.conf @@ -11,26 +11,25 @@ dictionaries = [ [ 'IDBIndexParameters', 'nsIIDBObjectStore.idl' ], [ 'StorageEventInit', 'nsIDOMStorageEvent.idl' ], [ 'BlobPropertyBag', 'nsIDOMFile.idl' ], [ 'MutationObserverInit', 'nsIDOMMutationObserver.idl' ], [ 'SettingsEventInit', 'nsIDOMSettingsManager.idl' ], [ 'WifiConnectionInfoEventInit', 'nsIWifiEventInits.idl' ], [ 'WifiStatusChangeEventInit', 'nsIWifiEventInits.idl' ], [ 'GeoPositionOptions', 'nsIDOMGeoGeolocation.idl' ], - [ 'DeviceLightEventInit', 'nsIDOMDeviceLightEvent.idl' ], - [ 'MozApplicationEventInit', 'nsIDOMApplicationRegistry.idl' ], [ 'DOMFileMetadataParameters', 'nsIDOMLockedFile.idl' ], [ 'XMLHttpRequestParameters', 'nsIXMLHttpRequest.idl' ], [ 'DeviceStorageEnumerationParameters', 'nsIDOMDeviceStorage.idl' ] ] # include file names special_includes = [ 'nsContentUtils.h', - 'XPCQuickStubs.h' + 'XPCQuickStubs.h', + 'nsIDOMApplicationRegistry.h' ] # name of the type to not include using #include "typename.h" exclude_automatic_type_include = [ 'nsISupports', 'mozIDOMApplication' ]
--- a/js/xpconnect/src/dictionary_helper_gen.py +++ b/js/xpconnect/src/dictionary_helper_gen.py @@ -299,16 +299,18 @@ def write_getter(a, iface, fd): % (get_jsid(a.name), a.name)) else: fd.write(" NS_ENSURE_STATE(JS_GetPropertyById(aCx, aObj, %s, &v));\n" % get_jsid(a.name)) if realtype.count("bool"): fd.write(" JSBool b;\n") fd.write(" MOZ_ALWAYS_TRUE(JS_ValueToBoolean(aCx, v, &b));\n") fd.write(" aDict.%s = b;\n" % a.name) + elif realtype.count("PRUint32"): + fd.write(" NS_ENSURE_STATE(JS_ValueToECMAUint32(aCx, v, &aDict.%s));\n" % a.name) elif realtype.count("PRInt32"): fd.write(" NS_ENSURE_STATE(JS_ValueToECMAInt32(aCx, v, &aDict.%s));\n" % a.name) elif realtype.count("double"): fd.write(" NS_ENSURE_STATE(JS_ValueToNumber(aCx, v, &aDict.%s));\n" % a.name) elif realtype.count("float"): fd.write(" double d;\n") fd.write(" NS_ENSURE_STATE(JS_ValueToNumber(aCx, v, &d));") fd.write(" aDict.%s = (float) d;\n" % a.name)
--- a/js/xpconnect/src/event_impl_gen.conf.in +++ b/js/xpconnect/src/event_impl_gen.conf.in @@ -10,21 +10,26 @@ simple_events = [ 'DeviceProximityEvent', 'UserProximityEvent', 'CustomEvent', 'PageTransitionEvent', 'PopStateEvent', 'HashChangeEvent', 'CloseEvent', - 'MozContactChangeEvent' + 'MozContactChangeEvent', + 'DeviceOrientationEvent', + 'DeviceLightEvent', + 'MozApplicationEvent' ] """ include file names """ special_includes = [ 'DictionaryHelpers.h', - 'nsContentUtils.h' + 'nsContentUtils.h', + 'nsIDOMApplicationRegistry.h' ] """ name of the type to not include using #include "typename.h" """ exclude_automatic_type_include = [ - 'nsISupports' + 'nsISupports', + 'mozIDOMApplication' ]
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -6093,16 +6093,17 @@ var RemoteDebugger = { this._stop(); this._start(); }, _start: function rd_start() { try { if (!DebuggerServer.initialized) { DebuggerServer.init(this._allowConnection); + DebuggerServer.addBrowserActors(); DebuggerServer.addActors("chrome://browser/content/dbg-browser-actors.js"); } let port = this._getPort(); DebuggerServer.openListener(port); dump("Remote debugger listening on port " + port); } catch(e) { dump("Remote debugger didn't start: " + e);
--- a/mobile/android/chrome/content/dbg-browser-actors.js +++ b/mobile/android/chrome/content/dbg-browser-actors.js @@ -1,435 +1,119 @@ /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* 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"; /** - * Fennec-specific actors. + * Fennec-specific root actor that extends BrowserRootActor and overrides some + * of its methods. */ -var windowMediator = Cc["@mozilla.org/appshell/window-mediator;1"] - .getService(Ci.nsIWindowMediator); - +/** + * The function that creates the root actor. DebuggerServer expects to find this + * function in the loaded actors in order to initialize properly. + */ function createRootActor(aConnection) { - return new BrowserRootActor(aConnection); + return new FennecRootActor(aConnection); } /** * Creates the root actor that client-server communications always start with. * The root actor is responsible for the initial 'hello' packet and for * responding to a 'listTabs' request that produces the list of currently open * tabs. * * @param aConnection DebuggerServerConnection * The conection to the client. */ -function BrowserRootActor(aConnection) { - this.conn = aConnection; - this._tabActors = new WeakMap(); - this._tabActorPool = null; - this._actorFactories = null; - - this.onTabClosed = this.onTabClosed.bind(this); - windowMediator.addListener(this); +function FennecRootActor(aConnection) { + BrowserRootActor.call(this, aConnection); } -BrowserRootActor.prototype = { - /** - * Return a 'hello' packet as specified by the Remote Debugging Protocol. - */ - sayHello: function BRA_sayHello() { - return { from: "root", - applicationType: "browser", - traits: [] }; - }, +FennecRootActor.prototype = new BrowserRootActor(); - /** - * Disconnects the actor from the browser window. - */ - disconnect: function BRA_disconnect() { - windowMediator.removeListener(this); +/** + * Handles the listTabs request. Builds a list of actors + * for the tabs running in the process. The actors will survive + * until at least the next listTabs request. + */ +FennecRootActor.prototype.onListTabs = function FRA_onListTabs() { + // Get actors for all the currently-running tabs (reusing + // existing actors where applicable), and store them in + // an ActorPool. - // We may have registered event listeners on browser windows to - // watch for tab closes, remove those. - let win = windowMediator.getMostRecentWindow("navigator:browser"); - this.unwatchWindow(win); - - // Signal our imminent shutdown. - let evt = win.document.createEvent("Event"); - evt.initEvent("Debugger:Shutdown", true, false); - win.document.documentElement.dispatchEvent(evt); - }, + let actorPool = new ActorPool(this.conn); + let actorList = []; - /** - * Handles the listTabs request. Builds a list of actors - * for the tabs running in the process. The actors will survive - * until at least the next listTabs request. - */ - onListTabs: function BRA_onListTabs() { - // Get actors for all the currently-running tabs (reusing - // existing actors where applicable), and store them in - // an ActorPool. + let win = windowMediator.getMostRecentWindow("navigator:browser"); + this.browser = win.BrowserApp.selectedBrowser; - let actorPool = new ActorPool(this.conn); - let actorList = []; - - let win = windowMediator.getMostRecentWindow("navigator:browser"); - this.browser = win.BrowserApp.selectedBrowser; - - // Watch the window for tab closes so we can invalidate - // actors as needed. - this.watchWindow(win); + // Watch the window for tab closes so we can invalidate + // actors as needed. + this.watchWindow(win); - let tabs = win.BrowserApp.tabs; - let selected; - - for each (let tab in tabs) { - let browser = tab.browser; - - if (browser == this.browser) { - selected = actorList.length; - } + let tabs = win.BrowserApp.tabs; + let selected; - let actor = this._tabActors.get(browser); - if (!actor) { - actor = new BrowserTabActor(this.conn, browser); - actor.parentID = this.actorID; - this._tabActors.set(browser, actor); - } + for each (let tab in tabs) { + let browser = tab.browser; - actorPool.addActor(actor); - actorList.push(actor); + if (browser == this.browser) { + selected = actorList.length; } - // Now drop the old actorID -> actor map. Actors that still - // mattered were added to the new map, others will go - // away. - if (this._tabActorPool) { - this.conn.removeActorPool(this._tabActorPool); + let actor = this._tabActors.get(browser); + if (!actor) { + actor = new BrowserTabActor(this.conn, browser); + actor.parentID = this.actorID; + this._tabActors.set(browser, actor); } - this._tabActorPool = actorPool; - this.conn.addActorPool(this._tabActorPool); - - return { "from": "root", - "selected": selected, - "tabs": [actor.grip() - for each (actor in actorList)] }; - }, - - /** - * Watch a window that was visited during onListTabs for - * tab closures. - */ - watchWindow: function BRA_watchWindow(aWindow) { - let tabContainer = aWindow.document.getElementById("browsers"); - tabContainer.addEventListener("TabClose", - this.onTabClosed, - false); - }, - - /** - * Stop watching a window for tab closes. - */ - unwatchWindow: function BRA_unwatchWindow(aWindow) { - let tabContainer = aWindow.document.getElementById("browsers"); - tabContainer.removeEventListener("TabClose", this.onTabClosed); - this.exitTabActor(aWindow); - }, + actorPool.addActor(actor); + actorList.push(actor); + } - /** - * When a tab is closed, exit its tab actor. The actor - * will be dropped at the next listTabs request. - */ - onTabClosed: function BRA_onTabClosed(aEvent) { - this.exitTabActor(aEvent.target.browser); - }, + // Now drop the old actorID -> actor map. Actors that still + // mattered were added to the new map, others will go + // away. + if (this._tabActorPool) { + this.conn.removeActorPool(this._tabActorPool); + } - /** - * Exit the tab actor of the specified tab. - */ - exitTabActor: function BRA_exitTabActor(aWindow) { - let actor = this._tabActors.get(aWindow); - if (actor) { - actor.exit(); - } - }, + this._tabActorPool = actorPool; + this.conn.addActorPool(this._tabActorPool); - // nsIWindowMediatorListener - onWindowTitleChange: function BRA_onWindowTitleChange(aWindow, aTitle) { }, - onOpenWindow: function BRA_onOpenWindow(aWindow) { }, - onCloseWindow: function BRA_onCloseWindow(aWindow) { - if (aWindow.BrowserApp) { - this.unwatchWindow(aWindow); - } - } -} - -/** - * The request types this actor can handle. - */ -BrowserRootActor.prototype.requestTypes = { - "listTabs": BrowserRootActor.prototype.onListTabs + return { "from": "root", + "selected": selected, + "tabs": [actor.grip() + for each (actor in actorList)] }; }; /** - * Creates a tab actor for handling requests to a browser tab, like attaching - * and detaching. - * - * @param aConnection DebuggerServerConnection - * The conection to the client. - * @param aBrowser browser - * The browser instance that contains this tab. + * Return the tab container for the specified window. */ -function BrowserTabActor(aConnection, aBrowser) -{ - this.conn = aConnection; - this._browser = aBrowser; - - this._onWindowCreated = this.onWindowCreated.bind(this); -} - -// XXX (bug 710213): BrowserTabActor attach/detach/exit/disconnect is a -// *complete* mess, needs to be rethought asap. - -BrowserTabActor.prototype = { - get browser() { return this._browser; }, - - get exited() { return !this._browser; }, - get attached() { return !!this._attached }, - - _tabPool: null, - get tabActorPool() { return this._tabPool; }, - - _contextPool: null, - get contextActorPool() { return this._contextPool; }, - - /** - * Add the specified breakpoint to the default actor pool connection, in order - * to be alive as long as the server is. - * - * @param BreakpointActor aActor - * The actor object. - */ - addToBreakpointPool: function BTA_addToBreakpointPool(aActor) { - this.conn.addActor(aActor); - }, - - /** - * Remove the specified breakpint from the default actor pool. - * - * @param string aActor - * The actor ID. - */ - removeFromBreakpointPool: function BTA_removeFromBreakpointPool(aActor) { - this.conn.removeActor(aActor); - }, - - actorPrefix: "tab", - - grip: function BTA_grip() { - dbg_assert(!this.exited, - "grip() shouldn't be called on exited browser actor."); - dbg_assert(this.actorID, - "tab should have an actorID."); - return { actor: this.actorID, - title: this._browser.contentTitle, - url: this._browser.currentURI.spec } - }, - - /** - * Called when the actor is removed from the connection. - */ - disconnect: function BTA_disconnect() { - this._detach(); - }, - - /** - * Called by the root actor when the underlying tab is closed. - */ - exit: function BTA_exit() { - if (this.exited) { - return; - } - - if (this.attached) { - this._detach(); - this.conn.send({ from: this.actorID, - type: "tabDetached" }); - } - - this._browser = null; - }, - - /** - * Does the actual work of attching to a tab. - */ - _attach: function BTA_attach() { - if (this._attached) { - return; - } - - // Create a pool for tab-lifetime actors. - dbg_assert(!this._tabPool, "Shouldn't have a tab pool if we weren't attached."); - this._tabPool = new ActorPool(this.conn); - this.conn.addActorPool(this._tabPool); - - // ... and a pool for context-lifetime actors. - this._pushContext(); - - // Watch for globals being created in this tab. - this._browser.addEventListener("DOMWindowCreated", this._onWindowCreated, true); - - this._attached = true; - }, +FennecRootActor.prototype.getTabContainer = function FRA_getTabContainer(aWindow) { + return aWindow.document.getElementById("browsers"); +}; - /** - * Creates a thread actor and a pool for context-lifetime actors. It then sets - * up the content window for debugging. - */ - _pushContext: function BTA_pushContext() { - dbg_assert(!this._contextPool, "Can't push multiple contexts"); - - this._contextPool = new ActorPool(this.conn); - this.conn.addActorPool(this._contextPool); - - this.threadActor = new ThreadActor(this); - this._addDebuggees(this._browser.contentWindow.wrappedJSObject); - this._contextPool.addActor(this.threadActor); - }, - - /** - * Add the provided window and all windows in its frame tree as debuggees. - */ - _addDebuggees: function BTA__addDebuggees(aWindow) { - this.threadActor.addDebuggee(aWindow); - let frames = aWindow.frames; - for (let i = 0; i < frames.length; i++) { - this._addDebuggees(frames[i]); - } - }, - - /** - * Exits the current thread actor and removes the context-lifetime actor pool. - * The content window is no longer being debugged after this call. - */ - _popContext: function BTA_popContext() { - dbg_assert(!!this._contextPool, "No context to pop."); - - this.conn.removeActorPool(this._contextPool); - this._contextPool = null; - this.threadActor.exit(); - this.threadActor = null; - }, - - /** - * Does the actual work of detaching from a tab. - */ - _detach: function BTA_detach() { - if (!this.attached) { - return; - } - - this._browser.removeEventListener("DOMWindowCreated", this._onWindowCreated, true); - - this._popContext(); - - // Shut down actors that belong to this tab's pool. - this.conn.removeActorPool(this._tabPool); - this._tabPool = null; - - this._attached = false; - }, - - // Protocol Request Handlers +/** + * When a tab is closed, exit its tab actor. The actor + * will be dropped at the next listTabs request. + */ +FennecRootActor.prototype.onTabClosed = function FRA_onTabClosed(aEvent) { + this.exitTabActor(aEvent.target.browser); +}; - onAttach: function BTA_onAttach(aRequest) { - if (this.exited) { - return { type: "exited" }; - } - - this._attach(); - - return { type: "tabAttached", threadActor: this.threadActor.actorID }; - }, - - onDetach: function BTA_onDetach(aRequest) { - if (!this.attached) { - return { error: "wrongState" }; - } - - this._detach(); - - return { type: "detached" }; - }, - - /** - * Prepare to enter a nested event loop by disabling debuggee events. - */ - preNest: function BTA_preNest() { - if (!this._browser) { - // The tab is already closed. - return; - } - let windowUtils = this._browser.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - windowUtils.suppressEventHandling(true); - windowUtils.suspendTimeouts(); - }, - - /** - * Prepare to exit a nested event loop by enabling debuggee events. - */ - postNest: function BTA_postNest(aNestData) { - if (!this._browser) { - // The tab is already closed. - return; - } - let windowUtils = this._browser.contentWindow - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - windowUtils.resumeTimeouts(); - windowUtils.suppressEventHandling(false); - }, - - /** - * Handle location changes, by sending a tabNavigated notification to the - * client. - */ - onWindowCreated: function BTA_onWindowCreated(evt) { - if (evt.target === this._browser.contentDocument) { - if (this._attached) { - this.conn.send({ from: this.actorID, type: "tabNavigated", - url: this._browser.contentDocument.URL }); - } - } +// nsIWindowMediatorListener +FennecRootActor.prototype.onCloseWindow = function FRA_onCloseWindow(aWindow) { + if (aWindow.BrowserApp) { + this.unwatchWindow(aWindow); } }; /** * The request types this actor can handle. */ -BrowserTabActor.prototype.requestTypes = { - "attach": BrowserTabActor.prototype.onAttach, - "detach": BrowserTabActor.prototype.onDetach +FennecRootActor.prototype.requestTypes = { + "listTabs": FennecRootActor.prototype.onListTabs }; - -/** - * Registers handlers for new request types defined dynamically. This is used - * for example by add-ons to augment the functionality of the tab actor. - * - * @param aName string - * The name of the new request type. - * @param aFunction function - * The handler for this request type. - */ -DebuggerServer.addTabRequest = function DS_addTabRequest(aName, aFunction) { - BrowserTabActor.prototype.requestTypes[aName] = function(aRequest) { - if (!this.attached) { - return { error: "wrongState" }; - } - return aFunction(this, aRequest); - } -};
--- a/services/common/storageserver.js +++ b/services/common/storageserver.js @@ -127,26 +127,38 @@ ServerBSO.prototype = { let body; function sendResponse() { response.setStatusLine(request.httpVersion, code, status); writeHttpBody(response, body); } if (request.hasHeader("x-if-modified-since")) { - let headerModified = parseInt(request.getHeader("x-if-modified-since")); + let headerModified = parseInt(request.getHeader("x-if-modified-since"), + 10); CommonUtils.ensureMillisecondsTimestamp(headerModified); if (headerModified >= this.modified) { code = 304; status = "Not Modified"; sendResponse(); return; } + } else if (request.hasHeader("x-if-unmodified-since")) { + let requestModified = parseInt(request.getHeader("x-if-unmodified-since"), + 10); + let serverModified = this.modified; + + if (serverModified > requestModified) { + code = 412; + status = "Precondition Failed"; + sendResponse(); + return; + } } if (!this.deleted) { body = JSON.stringify(this.toJSON()); response.setHeader("Content-Type", "application/json", false); response.setHeader("X-Last-Modified", "" + this.modified, false); } else { code = 404; @@ -406,36 +418,16 @@ StorageServerCollection.prototype = { } if (options.older) { if (bso.modified >= options.older) { return false; } } - if (options.index_above) { - if (bso.sortindex === undefined) { - return false; - } - - if (bso.sortindex <= options.index_above) { - return false; - } - } - - if (options.index_below) { - if (bso.sortindex === undefined) { - return false; - } - - if (bso.sortindex >= options.index_below) { - return false; - } - } - return true; }, count: function count(options) { options = options || {}; let c = 0; for (let [id, bso] in Iterator(this._bsos)) { if (bso.modified && this._inResultSet(bso, options)) { @@ -607,20 +599,21 @@ StorageServerCollection.prototype = { parseOptions: function parseOptions(request) { let options = {}; for each (let chunk in request.queryString.split("&")) { if (!chunk) { continue; } chunk = chunk.split("="); + let key = decodeURIComponent(chunk[0]); if (chunk.length == 1) { - options[chunk[0]] = ""; + options[key] = ""; } else { - options[chunk[0]] = chunk[1]; + options[key] = decodeURIComponent(chunk[1]); } } if (options.ids) { options.ids = options.ids.split(","); } if (options.newer) { @@ -636,28 +629,16 @@ StorageServerCollection.prototype = { if (!isInteger(options.older)) { throw HTTP_400; } CommonUtils.ensureMillisecondsTimestamp(options.older); options.older = parseInt(options.older, 10); } - if (options.index_above) { - if (!isInteger(options.index_above)) { - throw HTTP_400; - } - } - - if (options.index_below) { - if (!isInteger(options.index_below)) { - throw HTTP_400; - } - } - if (options.limit) { if (!isInteger(options.limit)) { throw HTTP_400; } options.limit = parseInt(options.limit, 10); } @@ -678,16 +659,26 @@ StorageServerCollection.prototype = { } } if (requestModified >= newestBSO) { response.setHeader("X-Last-Modified", "" + newestBSO); response.setStatusLine(request.httpVersion, 304, "Not Modified"); return; } + } else if (request.hasHeader("x-if-unmodified-since")) { + let requestModified = parseInt(request.getHeader("x-if-unmodified-since"), + 10); + let serverModified = this.timestamp; + + if (serverModified > requestModified) { + response.setHeader("X-Last-Modified", "" + serverModified); + response.setStatusLine(request.httpVersion, 412, "Precondition Failed"); + return; + } } if (options.full) { data = data.map(function map(bso) { return bso.toJSON(); }); } else { data = data.map(function map(bso) { @@ -705,19 +696,17 @@ StorageServerCollection.prototype = { throw HTTP_406; } } let body; if (newlines) { response.setHeader("Content-Type", "application/newlines", false); let normalized = data.map(function map(d) { - let result = JSON.stringify(d); - - return result.replace("\n", "\\u000a"); + return JSON.stringify(d); }); body = normalized.join("\n") + "\n"; } else { response.setHeader("Content-Type", "application/json", false); body = JSON.stringify({items: data}); } @@ -750,20 +739,19 @@ StorageServerCollection.prototype = { } if (!Array.isArray(input)) { this._log.info("Input JSON type not an array!"); return sendMozSvcError(request, response, "8"); } } else if (inputMediaType == "application/newlines") { for each (let line in inputBody.split("\n")) { - let json = line.replace("\\u000a", "\n"); let record; try { - record = JSON.parse(json); + record = JSON.parse(line); } catch (ex) { this._log.info("JSON parse error on line!"); return sendMozSvcError(request, response, "8"); } input.push(record); } } else {
--- a/services/common/storageservice.js +++ b/services/common/storageservice.js @@ -865,19 +865,22 @@ StorageServiceRequest.prototype = { this._log.error("415 HTTP response seen from server! This should " + "never happen!"); this._error = new StorageServiceRequestError(); this._error.client = new Error("415 Unsupported Media Type received!"); callOnComplete(); return; } - if (response.status == 503) { + if (response.status >= 500 && response.status <= 599) { + this._log.error(response.status + " seen from server!"); this._error = new StorageServiceRequestError(); - this._error.server = new Error("503 Received."); + this._error.server = new Error(response.status + " status code."); + callOnComplete(); + return; } callOnComplete(); } catch (ex) { this._clientError = ex; this._log.info("Exception when processing _onComplete: " + ex); @@ -989,30 +992,16 @@ StorageCollectionGetRequest.prototype = if (value) { this._namedArgs.full = "1"; } else { delete this._namedArgs["full"]; } }, /** - * Only retrieve BSOs whose sortindex is higher than this integer value. - */ - set index_above(value) { - this._namedArgs.index_above = value; - }, - - /** - * Only retrieve BSOs whose sortindex is lower than this integer value. - */ - set index_below(value) { - this._namedArgs.index_below = value; - }, - - /** * Limit the max number of returned BSOs to this integer number. */ set limit(value) { this._namedArgs.limit = value; }, /** * If set with any value, sort the results based on modification time, oldest @@ -1079,25 +1068,31 @@ StorageCollectionGetRequest.prototype = /** * Represents a request that sets data in a collection * * Instances of this type are returned by StorageServiceClient.setBSOs(). */ function StorageCollectionSetRequest() { StorageServiceRequest.call(this); - this._lines = []; - this._size = 0; + this.size = 0; - this.successfulIDs = new Set(); - this.failures = new Map(); + // TODO Bug 775781 convert to Set and Map once iterable. + this.successfulIDs = []; + this.failures = {}; + + this._lines = []; } StorageCollectionSetRequest.prototype = { __proto__: StorageServiceRequest.prototype, + get count() { + return this._lines.length; + }, + /** * Add a BasicStorageObject to this request. * * Please note that the BSO content is retrieved when the BSO is added to * the request. If the BSO changes after it is added to a request, those * changes will not be reflected in the request. * * @param bso @@ -1107,44 +1102,395 @@ StorageCollectionSetRequest.prototype = if (!bso instanceof BasicStorageObject) { throw new Error("argument must be a BasicStorageObject instance."); } if (!bso.id) { throw new Error("Passed BSO must have id defined."); } - let line = JSON.stringify(bso).replace("\n", "\u000a"); + this.addLine(JSON.stringify(bso)); + }, + /** + * Add a BSO (represented by its serialized newline-delimited form). + * + * You probably shouldn't use this. It is used for batching. + */ + addLine: function addLine(line) { // This is off by 1 in the larger direction. We don't care. - this._size += line.length + "\n".length; + this.size += line.length + 1; this._lines.push(line); }, _onDispatch: function _onDispatch() { this._data = this._lines.join("\n"); + this.size = this._data.length; }, _completeParser: function _completeParser(response) { let result = JSON.parse(response.body); for (let id of result.success) { - this.successfulIDs.add(id); + this.successfulIDs.push(id); } this.allSucceeded = true; - for (let [id, reasons] in result.failed) { + for (let [id, reasons] in Iterator(result.failed)) { this.failures[id] = reasons; this.allSucceeded = false; } }, }; /** + * Represents a batch upload of BSOs to an individual collection. + * + * This is a more intelligent way to upload may BSOs to the server. It will + * split the uploaded data into multiple requests so size limits, etc aren't + * exceeded. + * + * Once a client obtains an instance of this type, it calls `addBSO` for each + * BSO to be uploaded. When the client is done providing BSOs to be uploaded, + * it calls `finish`. When `finish` is called, no more BSOs can be added to the + * batch. When all requests created from this batch have finished, the callback + * provided to `finish` will be invoked. + * + * Clients can also explicitly flush pending outgoing BSOs via `flush`. This + * allows callers to control their own batching/chunking. + * + * Interally, this maintains a queue of StorageCollectionSetRequest to be + * issued. At most one request is allowed to be in-flight at once. This is to + * avoid potential conflicts on the server. And, in the case of conditional + * requests, it prevents requests from being declined due to the server being + * updated by another request issued by us. + * + * If a request errors for any reason, all queued uploads are abandoned and the + * `finish` callback is invoked as soon as possible. The `successfulIDs` and + * `failures` properties will contain data from all requests that had this + * response data. In other words, the IDs have BSOs that were never sent to the + * server are not lumped in to either property. + * + * Requests can be made conditional by setting `locallyModifiedVersion` to the + * most recent version of server data. As responses from the server are seen, + * the last server version is carried forward to subsequent requests. + * + * The server version from the last request is available in the + * `serverModifiedVersion` property. It should only be accessed during or + * after the callback passed to `finish`. + * + * @param client + * (StorageServiceClient) Client instance to use for uploading. + * + * @param collection + * (string) Collection the batch operation will upload to. + */ +function StorageCollectionBatchedSet(client, collection) { + this.client = client; + this.collection = collection; + + this._log = client._log; + + this.locallyModifiedVersion = null; + this.serverModifiedVersion = null; + + // TODO Bug 775781 convert to Set and Map once iterable. + this.successfulIDs = []; + this.failures = {}; + + // Request currently being populated. + this._stagingRequest = client.setBSOs(this.collection); + + // Requests ready to be sent over the wire. + this._outgoingRequests = []; + + // Whether we are waiting for a response. + this._requestInFlight = false; + + this._onFinishCallback = null; + this._finished = false; + this._errorEncountered = false; +} +StorageCollectionBatchedSet.prototype = { + /** + * Add a BSO to be uploaded as part of this batch. + */ + addBSO: function addBSO(bso) { + if (this._errorEncountered) { + return; + } + + let line = JSON.stringify(bso); + + if (line.length > this.client.REQUEST_SIZE_LIMIT) { + throw new Error("BSO is larger than allowed limit: " + line.length + + " > " + this.client.REQUEST_SIZE_LIMIT); + } + + if (this._stagingRequest.size + line.length > this.client.REQUEST_SIZE_LIMIT) { + this._log.debug("Sending request because payload size would be exceeded"); + this._finishStagedRequest(); + + this._stagingRequest.addLine(line); + return; + } + + // We are guaranteed to fit within size limits. + this._stagingRequest.addLine(line); + + if (this._stagingRequest.count >= this.client.REQUEST_BSO_COUNT_LIMIT) { + this._log.debug("Sending request because BSO count threshold reached."); + this._finishStagedRequest(); + return; + } + }, + + finish: function finish(cb) { + if (this._finished) { + throw new Error("Batch request has already been finished."); + } + + this.flush(); + + this._onFinishCallback = cb; + this._finished = true; + this._stagingRequest = null; + }, + + flush: function flush() { + if (this._finished) { + throw new Error("Batch request has been finished."); + } + + if (!this._stagingRequest.count) { + return; + } + + this._finishStagedRequest(); + }, + + _finishStagedRequest: function _finishStagedRequest() { + this._outgoingRequests.push(this._stagingRequest); + this._sendOutgoingRequest(); + this._stagingRequest = this.client.setBSOs(this.collection); + }, + + _sendOutgoingRequest: function _sendOutgoingRequest() { + if (this._requestInFlight || this._errorEncountered) { + return; + } + + if (!this._outgoingRequests.length) { + return; + } + + let request = this._outgoingRequests.shift(); + + if (this.locallyModifiedVersion) { + request.locallyModifiedVersion = this.locallyModifiedVersion; + } + + request.dispatch(this._onBatchComplete.bind(this)); + this._requestInFlight = true; + }, + + _onBatchComplete: function _onBatchComplete(error, request) { + this._requestInFlight = false; + + this.serverModifiedVersion = request.serverTime; + + // Only update if we had a value before. Otherwise, this breaks + // unconditional requests! + if (this.locallyModifiedVersion) { + this.locallyModifiedVersion = request.serverTime; + } + + for (let id of request.successfulIDs) { + this.successfulIDs.push(id); + } + + for (let [id, reason] in Iterator(request.failures)) { + this.failures[id] = reason; + } + + if (request.error) { + this._errorEncountered = true; + } + + this._checkFinish(); + }, + + _checkFinish: function _checkFinish() { + if (this._outgoingRequests.length && !this._errorEncountered) { + this._sendOutgoingRequest(); + return; + } + + if (!this._onFinishCallback) { + return; + } + + try { + this._onFinishCallback(this); + } catch (ex) { + this._log.warn("Exception when calling finished callback: " + + CommonUtils.exceptionStr(ex)); + } + }, +}; +Object.freeze(StorageCollectionBatchedSet.prototype); + +/** + * Manages a batch of BSO deletion requests. + * + * A single instance of this virtual request allows deletion of many individual + * BSOs without having to worry about server limits. + * + * Instances are obtained by calling `deleteBSOsBatching` on + * StorageServiceClient. + * + * Usage is roughly the same as StorageCollectionBatchedSet. Callers obtain + * an instance and select individual BSOs for deletion by calling `addID`. + * When the caller is finished marking BSOs for deletion, they call `finish` + * with a callback which will be invoked when all deletion requests finish. + * + * When the finished callback is invoked, any encountered errors will be stored + * in the `errors` property of this instance (which is passed to the callback). + * This will be an empty array if no errors were encountered. Else, it will + * contain the errors from the `onComplete` handler of request instances. The + * set of succeeded and failed IDs is not currently available. + * + * Deletes can be made conditional by setting `locallyModifiedVersion`. The + * behavior is the same as request types. The only difference is that the + * updated version from the server as a result of requests is carried forward + * to subsequent requests. + * + * The server version from the last request is stored in the + * `serverModifiedVersion` property. It is not safe to access this until the + * callback from `finish`. + * + * Like StorageCollectionBatchedSet, requests are issued serially to avoid + * race conditions on the server. + * + * @param client + * (StorageServiceClient) Client request is associated with. + * @param collection + * (string) Collection being operated on. + */ +function StorageCollectionBatchedDelete(client, collection) { + this.client = client; + this.collection = collection; + + this._log = client._log; + + this.locallyModifiedVersion = null; + this.serverModifiedVersion = null; + this.errors = []; + + this._pendingIDs = []; + this._requestInFlight = false; + this._finished = false; + this._finishedCallback = null; +} +StorageCollectionBatchedDelete.prototype = { + addID: function addID(id) { + if (this._finished) { + throw new Error("Cannot add IDs to a finished instance."); + } + + // If we saw errors already, don't do any work. This is an optimization + // and isn't strictly required, as _sendRequest() should no-op. + if (this.errors.length) { + return; + } + + this._pendingIDs.push(id); + + if (this._pendingIDs.length >= this.client.REQUEST_BSO_DELETE_LIMIT) { + this._sendRequest(); + } + }, + + /** + * Finish this batch operation. + * + * No more IDs can be added to this operation. Existing IDs are flushed as + * a request. The passed callback will be called when all requests have + * finished. + */ + finish: function finish(cb) { + if (this._finished) { + throw new Error("Batch delete instance has already been finished."); + } + + this._finished = true; + this._finishedCallback = cb; + + if (this._pendingIDs.length) { + this._sendRequest(); + } + }, + + _sendRequest: function _sendRequest() { + // Only allow 1 active request at a time and don't send additional + // requests if one has failed. + if (this._requestInFlight || this.errors.length) { + return; + } + + let ids = this._pendingIDs.splice(0, this.client.REQUEST_BSO_DELETE_LIMIT); + let request = this.client.deleteBSOs(this.collection, ids); + + if (this.locallyModifiedVersion) { + request.locallyModifiedVersion = this.locallyModifiedVersion; + } + + request.dispatch(this._onRequestComplete.bind(this)); + this._requestInFlight = true; + }, + + _onRequestComplete: function _onRequestComplete(error, request) { + this._requestInFlight = false; + + if (error) { + // We don't currently track metadata of what failed. This is an obvious + // feature that could be added. + this._log.warn("Error received from server: " + error); + this.errors.push(error); + } + + this.serverModifiedVersion = request.serverTime; + + // If performing conditional requests, carry forward the new server version + // so subsequent conditional requests work. + if (this.locallyModifiedVersion) { + this.locallyModifiedVersion = request.serverTime; + } + + if (this._pendingIDs.length && !this.errors.length) { + this._sendRequest(); + return; + } + + if (!this._finishedCallback) { + return; + } + + try { + this._finishedCallback(this); + } catch (ex) { + this._log.warn("Exception when invoking finished callback: " + + CommonUtils.exceptionStr(ex)); + } + }, +}; +Object.freeze(StorageCollectionBatchedDelete.prototype); + +/** * Construct a new client for the SyncStorage API, version 2.0. * * Clients are constructed against a base URI. This URI is typically obtained * from the token server via the endpoint component of a successful token * response. * * The purpose of this type is to serve as a middleware between a client's core * logic and the HTTP API. It hides the details of how the storage API is @@ -1190,16 +1536,37 @@ function StorageServiceClient(baseURI) { StorageServiceClient.prototype = { /** * The user agent sent with every request. * * You probably want to change this. */ userAgent: "StorageServiceClient", + /** + * Maximum size of entity bodies. + * + * TODO this should come from the server somehow. See bug 769759. + */ + REQUEST_SIZE_LIMIT: 512000, + + /** + * Maximum number of BSOs in requests. + * + * TODO this should come from the server somehow. See bug 769759. + */ + REQUEST_BSO_COUNT_LIMIT: 100, + + /** + * Maximum number of BSOs that can be deleted in a single DELETE. + * + * TODO this should come from the server. See bug 769759. + */ + REQUEST_BSO_DELETE_LIMIT: 100, + _baseURI: null, _log: null, _listeners: null, //---------------------------- // Event Listener Management | //---------------------------- @@ -1579,21 +1946,19 @@ StorageServiceClient.prototype = { * * The request can be made conditional by setting `locallyModifiedVersion` * on the returned request instance. * * This function returns a StorageCollectionSetRequest instance. This type * has additional functions and properties specific to this operation. See * its documentation for more. * - * Future improvement: support streaming of uploaded records. Currently, data - * is buffered in the client before going over the wire. Ideally, we'd support - * sending over the wire as soon as data is available. This will require - * support in RESTRequest, which doesn't support streaming on requests, only - * responses. + * Most consumers interested in submitting multiple BSOs to the server will + * want to use `setBSOsBatching` instead. That API intelligently splits up + * requests as necessary, etc. * * Example usage: * * let request = client.setBSOs("collection0"); * let bso0 = new BasicStorageObject("id0"); * bso0.payload = "payload0"; * * let bso1 = new BasicStorageObject("id1"); @@ -1631,16 +1996,40 @@ StorageServiceClient.prototype = { accept: "application/json", allowIfUnmodified: true, }); return request; }, /** + * This is a batching variant of setBSOs. + * + * Whereas `setBSOs` is a 1:1 mapping between function calls and HTTP + * requests issued, this one is a 1:N mapping. It will intelligently break + * up outgoing BSOs into multiple requests so size limits, etc aren't + * exceeded. + * + * Please see the documentation for `StorageCollectionBatchedSet` for + * usage info. + * + * @param collection + * (string) Collection to operate on. + * @return + * (StorageCollectionBatchedSet) Batched set instance. + */ + setBSOsBatching: function setBSOsBatching(collection) { + if (!collection) { + throw new Error("collection argument must be defined."); + } + + return new StorageCollectionBatchedSet(this, collection); + }, + + /** * Deletes a single BSO from a collection. * * The request can be made conditional by setting `locallyModifiedVersion` * on the returned request instance. * * @param collection * (string) Collection to operate on. * @param id @@ -1665,16 +2054,20 @@ StorageServiceClient.prototype = { * Delete multiple BSOs from a specific collection. * * This is functional equivalent to calling deleteBSO() for every ID but * much more efficient because it only results in 1 round trip to the server. * * The request can be made conditional by setting `locallyModifiedVersion` * on the returned request instance. * + * If the number of BSOs to delete is potentially large, it is preferred to + * use `deleteBSOsBatching`. That API automatically splits the operation into + * multiple requests so server limits aren't exceeded. + * * @param collection * (string) Name of collection to delete BSOs from. * @param ids * (iterable of strings) Set of BSO IDs to delete. */ deleteBSOs: function deleteBSOs(collection, ids) { // In theory we should URL encode. However, IDs are supposed to be URL // safe. If we get garbage in, we'll get garbage out and the server will @@ -1684,16 +2077,34 @@ StorageServiceClient.prototype = { let uri = this._baseURI + "storage/" + collection + "?ids=" + s; return this._getRequest(uri, "DELETE", { allowIfUnmodified: true, }); }, /** + * Bulk deletion of BSOs with no size limit. + * + * This allows a large amount of BSOs to be deleted easily. It will formulate + * multiple `deleteBSOs` queries so the client does not exceed server limits. + * + * @param collection + * (string) Name of collection to delete BSOs from. + * @return StorageCollectionBatchedDelete + */ + deleteBSOsBatching: function deleteBSOsBatching(collection) { + if (!collection) { + throw new Error("collection argument must be defined."); + } + + return new StorageCollectionBatchedDelete(this, collection); + }, + + /** * Deletes a single collection from the server. * * The request can be made conditional by setting `locallyModifiedVersion` * on the returned request instance. * * @param collection * (string) Name of collection to delete. */
--- a/services/common/tests/unit/test_storage_server.js +++ b/services/common/tests/unit/test_storage_server.js @@ -248,16 +248,38 @@ add_test(function test_bso_get_existing( do_check_eq(bso.modified, coll.bso("bso").modified); let payload = JSON.parse(bso.payload); do_check_attribute_count(payload, 1); do_check_eq(payload.foo, "bar"); server.stop(run_next_test); }); +add_test(function test_percent_decoding() { + _("Ensure query string arguments with percent encoded are handled."); + + let server = new StorageServer(); + server.registerUser("123", "password"); + server.startSynchronous(PORT); + + let coll = server.user("123").createCollection("test"); + coll.insert("001", {foo: "bar"}); + coll.insert("002", {bar: "foo"}); + + let request = localRequest("/2.0/123/storage/test?ids=001%2C002", "123", + "password"); + let error = doGetRequest(request); + do_check_null(error); + do_check_eq(request.response.status, 200); + let items = JSON.parse(request.response.body).items; + do_check_attribute_count(items, 2); + + server.stop(run_next_test); +}); + add_test(function test_bso_404() { _("Ensure the server responds with a 404 if a BSO does not exist."); let server = new StorageServer(); server.registerUser("123", "password"); server.createContents("123", { test: {} }); @@ -437,16 +459,70 @@ add_test(function test_bso_delete_unmodi let error = doDeleteRequest(request); do_check_eq(error, null); do_check_eq(request.response.status, 204); do_check_true(coll.bso("myid").deleted); server.stop(run_next_test); }); +add_test(function test_collection_get_unmodified_since() { + _("Ensure conditional unmodified get on collection works when it should."); + + let server = new StorageServer(); + server.registerUser("123", "password"); + server.startSynchronous(PORT); + let collection = server.user("123").createCollection("testcoll"); + collection.insert("bso0", {foo: "bar"}); + + let serverModified = collection.timestamp; + + let request1 = localRequest("/2.0/123/storage/testcoll", "123", "password"); + request1.setHeader("X-If-Unmodified-Since", serverModified); + let error = doGetRequest(request1); + do_check_null(error); + do_check_eq(request1.response.status, 200); + + let request2 = localRequest("/2.0/123/storage/testcoll", "123", "password"); + request2.setHeader("X-If-Unmodified-Since", serverModified - 1); + let error = doGetRequest(request2); + do_check_null(error); + do_check_eq(request2.response.status, 412); + + server.stop(run_next_test); +}); + +add_test(function test_bso_get_unmodified_since() { + _("Ensure conditional unmodified get on BSO works appropriately."); + + let server = new StorageServer(); + server.registerUser("123", "password"); + server.startSynchronous(PORT); + let collection = server.user("123").createCollection("testcoll"); + let bso = collection.insert("bso0", {foo: "bar"}); + + let serverModified = bso.modified; + + let request1 = localRequest("/2.0/123/storage/testcoll/bso0", "123", + "password"); + request1.setHeader("X-If-Unmodified-Since", serverModified); + let error = doGetRequest(request1); + do_check_null(error); + do_check_eq(request1.response.status, 200); + + let request2 = localRequest("/2.0/123/storage/testcoll/bso0", "123", + "password"); + request2.setHeader("X-If-Unmodified-Since", serverModified - 1); + let error = doGetRequest(request2); + do_check_null(error); + do_check_eq(request2.response.status, 412); + + server.stop(run_next_test); +}); + add_test(function test_missing_collection_404() { _("Ensure a missing collection returns a 404."); let server = new StorageServer(); server.registerUser("123", "password"); server.startSynchronous(PORT); let request = localRequest("/2.0/123/storage/none", "123", "password");
--- a/services/common/tests/unit/test_storageservice_client.js +++ b/services/common/tests/unit/test_storageservice_client.js @@ -7,40 +7,44 @@ Cu.import("resource://testing-common/ser const BASE_URI = "http://localhost:8080/2.0"; function run_test() { initTestLogging("Trace"); run_next_test(); } -function getEmptyServer(user="765", password="password") { +function getRandomUser() { + return "" + (Math.floor(Math.random() * 100000) + 1); +} + +function getEmptyServer(user=getRandomUser(), password="password") { let users = {}; users[user] = password; return storageServerForUsers(users, { meta: {}, clients: {}, crypto: {}, }); } -function getClient(user="765", password="password") { +function getClient(user=getRandomUser(), password="password") { let client = new StorageServiceClient(BASE_URI + "/" + user); client.addListener({ onDispatch: function onDispatch(request) { let up = user + ":" + password; request.request.setHeader("authorization", "Basic " + btoa(up)); } }); return client; } -function getServerAndClient(user="765", password="password") { +function getServerAndClient(user=getRandomUser(), password="password") { let server = getEmptyServer(user, password); let client = getClient(user, password); return [server, client, user, password]; } add_test(function test_auth_failure_listener() { _("Ensure the onAuthFailure listener is invoked."); @@ -639,19 +643,19 @@ add_test(function test_set_bsos_simple() let request = client.setBSOs("testcollection"); request.addBSO(bso0); request.addBSO(bso1); request.dispatch(function onComplete(error, req) { do_check_null(error); let successful = req.successfulIDs; - do_check_eq(successful.size(), 2); - do_check_true(successful.has(bso0.id)); - do_check_true(successful.has(bso1.id)); + do_check_eq(successful.length, 2); + do_check_eq(successful.indexOf(bso0.id), 0); + do_check_true(successful.indexOf(bso1.id), 1); server.stop(run_next_test); }); }); add_test(function test_set_bsos_invalid_bso() { _("Ensure that adding an invalid BSO throws."); @@ -696,17 +700,17 @@ add_test(function test_set_bsos_newline( request.addBSO(bso0); let bso1 = new BasicStorageObject("bso1"); bso1.payload = "foobar"; request.addBSO(bso1); request.dispatch(function onComplete(error, request) { do_check_null(error); - do_check_eq(request.successfulIDs.size(), 2); + do_check_eq(request.successfulIDs.length, 2); let coll = user.collection("testcoll"); do_check_eq(coll.bso("bso0").payload, bso0.payload); do_check_eq(coll.bso("bso1").payload, bso1.payload); server.stop(run_next_test); }); }); @@ -960,8 +964,415 @@ add_test(function test_network_error_lis } }); let request = client.getCollectionInfo(); request.dispatch(function() { do_check_true(listenerCalled); run_next_test(); }); }); + +add_test(function test_batching_set_too_large() { + _("Ensure we throw when attempting to add a BSO that is too large to fit."); + + let [server, client, username] = getServerAndClient(); + + let request = client.setBSOsBatching("testcoll"); + let payload = ""; + + // The actual length of the payload is a little less. But, this ensures we + // exceed it. + for (let i = 0; i < client.REQUEST_SIZE_LIMIT; i++) { + payload += i; + } + + let bso = new BasicStorageObject("bso"); + bso.payload = payload; + do_check_throws(function add() { request.addBSO(bso); }); + + server.stop(run_next_test); +}); + +add_test(function test_batching_set_basic() { + _("Ensure batching set works with single requests."); + + let [server, client, username] = getServerAndClient(); + + let request = client.setBSOsBatching("testcoll"); + for (let i = 0; i < 10; i++) { + let bso = new BasicStorageObject("bso" + i); + bso.payload = "payload" + i; + request.addBSO(bso); + } + + request.finish(function onFinish(request) { + do_check_eq(request.successfulIDs.length, 10); + + let collection = server.user(username).collection("testcoll"); + do_check_eq(collection.timestamp, request.serverModifiedVersion); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batching_set_batch_count() { + _("Ensure multiple outgoing request batching works when count is exceeded."); + + let [server, client, username] = getServerAndClient(); + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + } + + let request = client.setBSOsBatching("testcoll"); + for (let i = 1; i <= 300; i++) { + let bso = new BasicStorageObject("bso" + i); + bso.payload = "XXXXXXX"; + request.addBSO(bso); + } + + request.finish(function onFinish(request) { + do_check_eq(request.successfulIDs.length, 300); + do_check_eq(requestCount, 3); + + let collection = server.user(username).collection("testcoll"); + do_check_eq(collection.timestamp, request.serverModifiedVersion); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batching_set_batch_size() { + _("Ensure outgoing requests batch when size is exceeded."); + + let [server, client, username] = getServerAndClient(); + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + }; + + let limit = client.REQUEST_SIZE_LIMIT; + + let request = client.setBSOsBatching("testcoll"); + + // JavaScript: Y U NO EASY REPETITION FUNCTIONALITY? + let data = []; + for (let i = (limit / 2) - 100; i; i -= 1) { + data.push("X"); + } + + let payload = data.join(""); + + for (let i = 0; i < 4; i++) { + let bso = new BasicStorageObject("bso" + i); + bso.payload = payload; + request.addBSO(bso); + } + + request.finish(function onFinish(request) { + do_check_eq(request.successfulIDs.length, 4); + do_check_eq(requestCount, 2); + + let collection = server.user(username).collection("testcoll"); + do_check_eq(collection.timestamp, request.serverModifiedVersion); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batching_set_flush() { + _("Ensure flushing batch sets works."); + + let [server, client, username] = getServerAndClient(); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + } + + let request = client.setBSOsBatching("testcoll"); + for (let i = 1; i < 101; i++) { + let bso = new BasicStorageObject("bso" + i); + bso.payload = "foo"; + request.addBSO(bso); + + if (i % 10 == 0) { + request.flush(); + } + } + + request.finish(function onFinish(request) { + do_check_eq(request.successfulIDs.length, 100); + do_check_eq(requestCount, 10); + + let collection = server.user(username).collection("testcoll"); + do_check_eq(collection.timestamp, request.serverModifiedVersion); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batching_set_conditional_success() { + _("Ensure conditional requests for batched sets work properly."); + + let [server, client, username] = getServerAndClient(); + + let collection = server.user(username).createCollection("testcoll"); + + let lastServerVersion = Date.now(); + collection.insertBSO(new ServerBSO("foo", "bar", lastServerVersion)); + collection.timestamp = lastServerVersion; + do_check_eq(collection.timestamp, lastServerVersion); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + } + + let request = client.setBSOsBatching("testcoll"); + request.locallyModifiedVersion = collection.timestamp; + + for (let i = 1; i < 251; i++) { + let bso = new BasicStorageObject("bso" + i); + bso.payload = "foo" + i; + request.addBSO(bso); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 3); + + do_check_eq(collection.timestamp, request.serverModifiedVersion); + do_check_eq(collection.timestamp, request.locallyModifiedVersion); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batching_set_initial_failure() { + _("Ensure that an initial request failure setting BSOs is handled properly."); + + let [server, client, username] = getServerAndClient(); + + let collection = server.user(username).createCollection("testcoll"); + collection.timestamp = Date.now(); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + } + + let request = client.setBSOsBatching("testcoll"); + request.locallyModifiedVersion = collection.timestamp - 1; + + for (let i = 1; i < 250; i++) { + let bso = new BasicStorageObject("bso" + i); + bso.payload = "foo" + i; + request.addBSO(bso); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 1); + + do_check_eq(request.successfulIDs.length, 0); + do_check_eq(Object.keys(request.failures).length, 0); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batching_set_subsequent_failure() { + _("Ensure a non-initial failure during batching set is handled properly."); + + let [server, client, username] = getServerAndClient(); + let collection = server.user(username).createCollection("testcoll"); + collection.timestamp = Date.now(); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + + if (requestCount == 1) { + return; + } + + collection.timestamp++; + } + + let request = client.setBSOsBatching("testcoll"); + request.locallyModifiedVersion = collection.timestamp; + + for (let i = 0; i < 250; i++) { + let bso = new BasicStorageObject("bso" + i); + bso.payload = "foo" + i; + request.addBSO(bso); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 2); + do_check_eq(request.successfulIDs.length, 100); + do_check_eq(Object.keys(request.failures).length, 0); + + server.stop(run_next_test); + }); +}); + +function getBatchedDeleteData(collection="testcoll") { + let [server, client, username] = getServerAndClient(); + + let serverBSOs = {}; + for (let i = 1000; i; i -= 1) { + serverBSOs["bso" + i] = new ServerBSO("bso" + i, "payload" + i); + } + + let user = server.user(username); + user.createCollection(collection, serverBSOs); + + return [server, client, username, collection]; +} + +add_test(function test_batched_delete_single() { + _("Ensure batched delete with single request works."); + + let [server, client, username, collection] = getBatchedDeleteData(); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount += 1; + } + + let request = client.deleteBSOsBatching(collection); + for (let i = 1; i < 51; i += 1) { + request.addID("bso" + i); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 1); + do_check_eq(request.errors.length, 0); + + let coll = server.user(username).collection(collection); + do_check_eq(coll.count(), 950); + + do_check_eq(request.serverModifiedVersion, coll.timestamp); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batched_delete_multiple() { + _("Ensure batched delete splits requests properly."); + + let [server, client, username, collection] = getBatchedDeleteData(); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount += 1; + } + + let request = client.deleteBSOsBatching(collection); + for (let i = 1; i < 251; i += 1) { + request.addID("bso" + i); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 3); + do_check_eq(request.errors.length, 0); + + let coll = server.user(username).collection(collection); + do_check_eq(coll.count(), 750); + + do_check_eq(request.serverModifiedVersion, coll.timestamp); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batched_delete_conditional_success() { + _("Ensure conditional batched delete all work."); + + let [server, client, username, collection] = getBatchedDeleteData(); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + } + + let serverCollection = server.user(username).collection(collection); + let initialTimestamp = serverCollection.timestamp; + + let request = client.deleteBSOsBatching(collection); + request.locallyModifiedVersion = initialTimestamp; + + for (let i = 1; i < 251; i += 1) { + request.addID("bso" + 1); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 3); + do_check_eq(request.errors.length, 0); + + do_check_true(request.locallyModifiedVersion > initialTimestamp); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batched_delete_conditional_initial_failure() { + _("Ensure conditional batched delete failure on initial request works."); + + // The client needs to issue multiple requests but the first one was + // rejected. The client should only issue that initial request. + let [server, client, username, collection] = getBatchedDeleteData(); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + } + + let serverCollection = server.user(username).collection(collection); + let request = client.deleteBSOsBatching(collection); + request.locallyModifiedVersion = serverCollection.timestamp - 1; + + for (let i = 1; i < 251; i += 1) { + request.addID("bso" + i); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 1); + do_check_eq(request.errors.length, 1); + + server.stop(run_next_test); + }); +}); + +add_test(function test_batched_delete_conditional_subsequent_failure() { + _("Ensure conditional batched delete failure on non-initial request."); + + let [server, client, username, collection] = getBatchedDeleteData(); + + let serverCollection = server.user(username).collection(collection); + + let requestCount = 0; + server.callback.onRequest = function onRequest() { + requestCount++; + + if (requestCount <= 1) { + return; + } + + // Advance collection's timestamp on subsequent requests so request is + // rejected. + serverCollection.timestamp++; + } + + let request = client.deleteBSOsBatching(collection); + request.locallyModifiedVersion = serverCollection.timestamp; + + for (let i = 1; i < 251; i += 1) { + request.addID("bso" + i); + } + + request.finish(function onFinish(request) { + do_check_eq(requestCount, 2); + do_check_eq(request.errors.length, 1); + + server.stop(run_next_test); + }); +});
--- a/toolkit/devtools/debugger/server/dbg-browser-actors.js +++ b/toolkit/devtools/debugger/server/dbg-browser-actors.js @@ -123,31 +123,38 @@ BrowserRootActor.prototype = { for each (actor in actorList)] }; }, /** * Watch a window that was visited during onListTabs for * tab closures. */ watchWindow: function BRA_watchWindow(aWindow) { - aWindow.getBrowser().tabContainer.addEventListener("TabClose", - this.onTabClosed, - false); + this.getTabContainer(aWindow).addEventListener("TabClose", + this.onTabClosed, + false); }, /** * Stop watching a window for tab closes. */ unwatchWindow: function BRA_unwatchWindow(aWindow) { - aWindow.getBrowser().tabContainer.removeEventListener("TabClose", - this.onTabClosed); + this.getTabContainer(aWindow).removeEventListener("TabClose", + this.onTabClosed); this.exitTabActor(aWindow); }, /** + * Return the tab container for the specified window. + */ + getTabContainer: function BRA_getTabContainer(aWindow) { + return aWindow.getBrowser().tabContainer; + }, + + /** * When a tab is closed, exit its tab actor. The actor * will be dropped at the next listTabs request. */ onTabClosed: function BRA_onTabClosed(aEvent) { this.exitTabActor(aEvent.target.linkedBrowser); }, /**
--- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -6802,20 +6802,20 @@ nsWindow::SetupKeyModifiersSequence(nsTA for (PRUint32 i = 0; i < ArrayLength(sModifierKeyMap); ++i) { const PRUint32* map = sModifierKeyMap[i]; if (aModifiers & map[0]) { aArray->AppendElement(KeyPair(map[1], map[2])); } } } -static BOOL WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam) -{ - *((HWND*)lParam) = hwnd; - return FALSE; +static BOOL WINAPI EnumFirstChild(HWND hwnd, LPARAM lParam) +{ + *((HWND*)lParam) = hwnd; + return FALSE; } static void InvalidatePluginAsWorkaround(nsWindow *aWindow, const nsIntRect &aRect) { aWindow->Invalidate(aRect); // XXX - Even more evil workaround!! See bug 762948, flash's bottom // level sandboxed window doesn't seem to get our invalidate. We send