diff --git a/accessible/src/jsat/AccessFu.jsm b/accessible/src/jsat/AccessFu.jsm index 7ef42210848a..3df599cc1bc8 100644 --- a/accessible/src/jsat/AccessFu.jsm +++ b/accessible/src/jsat/AccessFu.jsm @@ -39,12 +39,9 @@ this.AccessFu = { Services.obs.addObserver(this, 'Accessibility:Settings', false); } catch (x) { // Not on Android - aWindow.addEventListener( - 'ContentStart', - (function(event) { - let content = aWindow.shell.contentBrowser.contentWindow; - content.addEventListener('mozContentEvent', this, false, true); - }).bind(this), false); + if (Utils.MozBuildApp === 'b2g') { + aWindow.addEventListener('ContentStart', this, false); + } } try { @@ -58,6 +55,25 @@ this.AccessFu = { this._enableOrDisable(); }, + /** + * Shut down chrome-layer accessibility functionality from the outside. + */ + detach: function detach() { + // Avoid disabling twice. + if (this._enabled) { + this._disable(); + } + if (Utils.MozBuildApp === 'mobile/android') { + Services.obs.removeObserver(this, 'Accessibility:Settings'); + } else if (Utils.MozBuildApp === 'b2g') { + Utils.win.shell.contentBrowser.contentWindow.removeEventListener( + 'mozContentEvent', this); + Utils.win.removeEventListener('ContentStart', this); + } + this.prefsBranch.removeObserver('activate', this); + Utils.uninit(); + }, + /** * Start AccessFu mode, this primarily means controlling the virtual cursor * with arrow keys. @@ -73,8 +89,10 @@ this.AccessFu = { Logger.info('enable'); - for each (let mm in Utils.AllMessageManagers) + for each (let mm in Utils.AllMessageManagers) { + this._addMessageListeners(mm); this._loadFrameScript(mm); + } // Add stylesheet let stylesheetURL = 'chrome://global/content/accessibility/AccessFu.css'; @@ -93,6 +111,11 @@ this.AccessFu = { Services.obs.addObserver(this, 'Accessibility:Focus', false); Utils.win.addEventListener('TabOpen', this); Utils.win.addEventListener('TabSelect', this); + + if (this.readyCallback) { + this.readyCallback(); + delete this.readyCallback; + } }, /** @@ -108,8 +131,10 @@ this.AccessFu = { Utils.win.document.removeChild(this.stylesheet.get()); - for each (let mm in Utils.AllMessageManagers) + for each (let mm in Utils.AllMessageManagers) { mm.sendAsyncMessage('AccessFu:Stop'); + this._removeMessageListeners(mm); + } Input.stop(); Output.stop(); @@ -122,6 +147,11 @@ this.AccessFu = { Services.obs.removeObserver(this, 'Accessibility:NextObject'); Services.obs.removeObserver(this, 'Accessibility:PreviousObject'); Services.obs.removeObserver(this, 'Accessibility:Focus'); + + if (this.doneCallback) { + this.doneCallback(); + delete this.doneCallback; + } }, _enableOrDisable: function _enableOrDisable() { @@ -143,8 +173,10 @@ this.AccessFu = { switch (aMessage.name) { case 'AccessFu:Ready': let mm = Utils.getMessageManager(aMessage.target); - mm.sendAsyncMessage('AccessFu:Start', - {method: 'start', buildApp: Utils.MozBuildApp}); + if (this._enabled) { + mm.sendAsyncMessage('AccessFu:Start', + {method: 'start', buildApp: Utils.MozBuildApp}); + } break; case 'AccessFu:Present': this._output(aMessage.json, aMessage.target); @@ -169,12 +201,35 @@ this.AccessFu = { }, _loadFrameScript: function _loadFrameScript(aMessageManager) { + if (this._processedMessageManagers.indexOf(aMessageManager) < 0) { + aMessageManager.loadFrameScript( + 'chrome://global/content/accessibility/content-script.js', true); + this._processedMessageManagers.push(aMessageManager); + } else if (this._enabled) { + // If the content-script is already loaded and AccessFu is enabled, + // send an AccessFu:Start message. + aMessageManager.sendAsyncMessage('AccessFu:Start', + {method: 'start', buildApp: Utils.MozBuildApp}); + } + }, + + _addMessageListeners: function _addMessageListeners(aMessageManager) { aMessageManager.addMessageListener('AccessFu:Present', this); aMessageManager.addMessageListener('AccessFu:Input', this); aMessageManager.addMessageListener('AccessFu:Ready', this); - aMessageManager. - loadFrameScript( - 'chrome://global/content/accessibility/content-script.js', true); + }, + + _removeMessageListeners: function _removeMessageListeners(aMessageManager) { + aMessageManager.removeMessageListener('AccessFu:Present', this); + aMessageManager.removeMessageListener('AccessFu:Input', this); + aMessageManager.removeMessageListener('AccessFu:Ready', this); + }, + + _handleMessageManager: function _handleMessageManager(aMessageManager) { + if (this._enabled) { + this._addMessageListeners(aMessageManager); + } + this._loadFrameScript(aMessageManager); }, observe: function observe(aSubject, aTopic, aData) { @@ -207,8 +262,8 @@ this.AccessFu = { break; case 'remote-browser-frame-shown': { - this._loadFrameScript( - aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager); + let mm = aSubject.QueryInterface(Ci.nsIFrameLoader).messageManager; + this._handleMessageManager(mm); break; } } @@ -216,6 +271,12 @@ this.AccessFu = { handleEvent: function handleEvent(aEvent) { switch (aEvent.type) { + case 'ContentStart': + { + Utils.win.shell.contentBrowser.contentWindow.addEventListener( + 'mozContentEvent', this, false, true); + break; + } case 'mozContentEvent': { if (aEvent.detail.type == 'accessibility-screenreader') { @@ -226,7 +287,8 @@ this.AccessFu = { } case 'TabOpen': { - this._loadFrameScript(Utils.getMessageManager(aEvent.target)); + let mm = Utils.getMessageManager(aEvent.target); + this._handleMessageManager(mm); break; } case 'TabSelect': @@ -255,7 +317,11 @@ this.AccessFu = { _enabled: false, // Layerview is focused - _focused: false + _focused: false, + + // Keep track of message managers tha already have a 'content-script.js' + // injected. + _processedMessageManagers: [] }; var Output = { diff --git a/accessible/src/jsat/EventManager.jsm b/accessible/src/jsat/EventManager.jsm index 3cced589be14..eea83bf6fb91 100644 --- a/accessible/src/jsat/EventManager.jsm +++ b/accessible/src/jsat/EventManager.jsm @@ -38,6 +38,10 @@ this.EventManager = { }, stop: function stop() { + if (!this._started) { + return; + } + Logger.info('EventManager.stop', Utils.MozBuildApp); Services.obs.removeObserver(this, 'accessible-event'); this._started = false; }, diff --git a/accessible/src/jsat/Utils.jsm b/accessible/src/jsat/Utils.jsm index 0f85c36241a0..c7a38a41cb7e 100644 --- a/accessible/src/jsat/Utils.jsm +++ b/accessible/src/jsat/Utils.jsm @@ -29,6 +29,13 @@ this.Utils = { this._win = Cu.getWeakReference(aWindow); }, + uninit: function Utils_uninit() { + if (!this._win) { + return; + } + delete this._win; + }, + get win() { return this._win.get(); }, @@ -178,13 +185,25 @@ this.Logger = { logLevel: 1, // INFO; + test: false, + log: function log(aLogLevel) { if (aLogLevel < this.logLevel) return; let message = Array.prototype.slice.call(arguments, 1).join(' '); - dump('[' + Utils.ScriptName + '] ' + - this._LEVEL_NAMES[aLogLevel] +' ' + message + '\n'); + message = '[' + Utils.ScriptName + '] ' + this._LEVEL_NAMES[aLogLevel] + + ' ' + message + '\n'; + dump(message); + // Note: used for testing purposes. If |this.test| is true, also log to + // the console service. + if (this.test) { + try { + Services.console.logStringMessage(message); + } catch (ex) { + // There was an exception logging to the console service. + } + } }, info: function info() { diff --git a/accessible/src/jsat/content-script.js b/accessible/src/jsat/content-script.js index 452f5edc5ae0..90a10eb6174e 100644 --- a/accessible/src/jsat/content-script.js +++ b/accessible/src/jsat/content-script.js @@ -216,16 +216,17 @@ function scroll(aMessage) { } } -addMessageListener('AccessFu:VirtualCursor', virtualCursorControl); -addMessageListener('AccessFu:Activate', activateCurrent); -addMessageListener('AccessFu:Scroll', scroll); - addMessageListener( 'AccessFu:Start', function(m) { + Logger.debug('AccessFu:Start'); if (m.json.buildApp) Utils.MozBuildApp = m.json.buildApp; + addMessageListener('AccessFu:VirtualCursor', virtualCursorControl); + addMessageListener('AccessFu:Activate', activateCurrent); + addMessageListener('AccessFu:Scroll', scroll); + EventManager.start( function sendMessage(aName, aDetails) { sendAsyncMessage(aName, aDetails); @@ -247,6 +248,10 @@ addMessageListener( function(m) { Logger.debug('AccessFu:Stop'); + removeMessageListener('AccessFu:VirtualCursor', virtualCursorControl); + removeMessageListener('AccessFu:Activate', activateCurrent); + removeMessageListener('AccessFu:Scroll', scroll); + EventManager.stop(); docShell.QueryInterface(Ci.nsIInterfaceRequestor). diff --git a/accessible/src/mac/RootAccessibleWrap.mm b/accessible/src/mac/RootAccessibleWrap.mm index 729087d03468..338d154bb179 100644 --- a/accessible/src/mac/RootAccessibleWrap.mm +++ b/accessible/src/mac/RootAccessibleWrap.mm @@ -9,8 +9,9 @@ #include "nsCOMPtr.h" #include "nsObjCExceptions.h" +#include "nsIFrame.h" +#include "nsView.h" #include "nsIWidget.h" -#include "nsViewManager.h" using namespace mozilla::a11y; diff --git a/accessible/tests/mochitest/common.js b/accessible/tests/mochitest/common.js index 01ea3b0a0367..78f87815944f 100644 --- a/accessible/tests/mochitest/common.js +++ b/accessible/tests/mochitest/common.js @@ -661,6 +661,23 @@ function shortenString(aString, aMaxLength) aString.substring(aString.length - trimOffset, aString.length); } +//////////////////////////////////////////////////////////////////////////////// +// General Utils +//////////////////////////////////////////////////////////////////////////////// +/** + * Return main chrome window (crosses chrome boundary) + */ +function getMainChromeWindow(aWindow) +{ + return aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIWebNavigation) + .QueryInterface(Components.interfaces.nsIDocShellTreeItem) + .rootTreeItem + .QueryInterface(Components.interfaces.nsIInterfaceRequestor) + .getInterface(Components.interfaces.nsIDOMWindow); +} + + //////////////////////////////////////////////////////////////////////////////// // Private //////////////////////////////////////////////////////////////////////////////// diff --git a/accessible/tests/mochitest/jsat/Makefile.in b/accessible/tests/mochitest/jsat/Makefile.in index 8747591f3f2f..6e5a9b84a434 100644 --- a/accessible/tests/mochitest/jsat/Makefile.in +++ b/accessible/tests/mochitest/jsat/Makefile.in @@ -12,6 +12,8 @@ relativesrcdir = @relativesrcdir@ include $(DEPTH)/config/autoconf.mk MOCHITEST_A11Y_FILES =\ +jsatcommon.js \ +test_alive.html \ test_utterance_order.html \ $(NULL) diff --git a/accessible/tests/mochitest/jsat/jsatcommon.js b/accessible/tests/mochitest/jsat/jsatcommon.js new file mode 100644 index 000000000000..1842c1b5314a --- /dev/null +++ b/accessible/tests/mochitest/jsat/jsatcommon.js @@ -0,0 +1,91 @@ +// A common module to run tests on the AccessFu module + +/** + * A global variable holding an array of test functions. + */ +var gTestFuncs = []; +/** + * A global Iterator for the array of test functions. + */ +var gIterator; + +Components.utils.import('resource://gre/modules/Services.jsm'); +Components.utils.import("resource://gre/modules/accessibility/Utils.jsm"); +Components.utils.import("resource://gre/modules/accessibility/EventManager.jsm"); + +var AccessFuTest = { + + addFunc: function AccessFuTest_addFunc(aFunc) { + if (aFunc) { + gTestFuncs.push(aFunc); + } + }, + + _waitForExplicitFinish: false, + + waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() { + this._waitForExplicitFinish = true; + }, + + finish: function AccessFuTest_finish() { + // Disable the console service logging. + Logger.test = false; + AccessFu.doneCallback = function doneCallback() { + // This is being called once AccessFu has been shut down. + // Detach AccessFu from everything it attached itself to. + AccessFu.detach(); + // and finish the test run. + SimpleTest.finish(); + }; + // Tear down accessibility and make AccessFu stop. + SpecialPowers.setIntPref("accessibility.accessfu.activate", 0); + }, + + nextTest: function AccessFuTest_nextTest() { + var testFunc; + try { + // Get the next test function from the iterator. If none left, + // StopIteration exception is thrown. + testFunc = gIterator.next()[1]; + } catch (ex) { + // StopIteration exception. + this.finish(); + return; + } + testFunc(); + }, + + runTests: function AccessFuTest_runTests() { + if (gTestFuncs.length === 0) { + ok(false, "No tests specified!"); + simpleTest.finish(); + return; + } + + // Create an Iterator for gTestFuncs array. + gIterator = Iterator(gTestFuncs); + + // Start AccessFu and put it in stand-by. + Components.utils.import("resource://gre/modules/accessibility/AccessFu.jsm"); + + AccessFu.attach(getMainChromeWindow(window)); + + AccessFu.readyCallback = function readyCallback() { + // Enable logging to the console service. + Logger.test = true; + // This is being called once accessibility has been turned on. + + if (AccessFuTest._waitForExplicitFinish) { + // Run all test functions asynchronously. + AccessFuTest.nextTest(); + } else { + // Run all test functions synchronously. + [testFunc() for (testFunc of gTestFuncs)]; + AccessFuTest.finish(); + } + }; + + // Invoke the whole thing. + SpecialPowers.setIntPref("accessibility.accessfu.activate", 1); + } +}; diff --git a/accessible/tests/mochitest/jsat/test_alive.html b/accessible/tests/mochitest/jsat/test_alive.html new file mode 100644 index 000000000000..2b4b751971dc --- /dev/null +++ b/accessible/tests/mochitest/jsat/test_alive.html @@ -0,0 +1,81 @@ + + + + AccessFu test for enabling + + + + + + + + + + + Mozilla Bug 811307 + + + \ No newline at end of file diff --git a/b2g/components/DirectoryProvider.js b/b2g/components/DirectoryProvider.js index c17a6652e1dc..866dae3fdcec 100644 --- a/b2g/components/DirectoryProvider.js +++ b/b2g/components/DirectoryProvider.js @@ -91,7 +91,7 @@ DirectoryProvider.prototype = { if (!Services.volumeService) { return false; } - let volume = Services.volumeService.getVolumeByPath(volumePath); + let volume = Services.volumeService.createOrGetVolumeByPath(volumePath); if (!volume || volume.state !== Ci.nsIVolume.STATE_MOUNTED) { return false; } @@ -201,7 +201,7 @@ DirectoryProvider.prototype = { } if (Services.volumeService) { - let extVolume = Services.volumeService.getVolumeByPath(path); + let extVolume = Services.volumeService.createOrGetVolumeByPath(path); if (!extVolume) { path = LOCAL_DIR; } diff --git a/b2g/config/panda/config.json b/b2g/config/panda/config.json index 36a818bb52d5..d0e4e9e79a94 100644 --- a/b2g/config/panda/config.json +++ b/b2g/config/panda/config.json @@ -1,8 +1,8 @@ { - "config_version": 1, + "config_version": 2, "tooltool_manifest": "releng-pandaboard.tt", "mock_target": "mozilla-centos6-i386", - "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel"], + "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "git"], "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]], "build_targets": ["boottarball", "systemtarball", "userdatatarball", "package-tests"], "upload_files": [ @@ -12,6 +12,7 @@ "{srcdir}/b2g/config/panda/README", "{workdir}/sources.xml" ], + "b2g_manifest": "pandaboard.xml", "gecko_l10n_root": "http://hg.mozilla.org/l10n-central", "gaia": { "vcs": "hgtool", diff --git a/b2g/config/panda/releng-pandaboard.tt b/b2g/config/panda/releng-pandaboard.tt index 690096cafe96..a4783278cadd 100644 --- a/b2g/config/panda/releng-pandaboard.tt +++ b/b2g/config/panda/releng-pandaboard.tt @@ -1,11 +1,5 @@ [ { -"size": 678265436, -"digest": "36d05d77831be476e639095c04f25557171bb61c6764b2f6a49e253471aac8855adff17989089f1dce790d7e860c91d0b1d0f268fbc8fc661fca0c83ca7d65f5", -"algorithm": "sha512", -"filename": "gonk.tar.xz" -}, -{ "size": 2116507, "digest": "be67a012963a5c162834f9fcb989bcebd2d047dcb4e17ee23031b694dcf7cdfd6d7a6545d7a1f5e7293b6d24415403972f4ea1ab8c6c78fefcabfaf3f6875214", "algorithm": "sha512", diff --git a/b2g/config/unagi/config.json b/b2g/config/unagi/config.json index 4d14a1b2bf68..fdbc60dc8a47 100644 --- a/b2g/config/unagi/config.json +++ b/b2g/config/unagi/config.json @@ -1,8 +1,8 @@ { - "config_version": 1, + "config_version": 2, "tooltool_manifest": "releng-unagi.tt", "mock_target": "mozilla-centos6-i386", - "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "java-1.6.0-openjdk-devel"], + "mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "java-1.6.0-openjdk-devel", "git"], "mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]], "build_targets": [], "upload_files": [ @@ -10,6 +10,8 @@ "{objdir}/dist/b2g-*.crashreporter-symbols.zip", "{workdir}/sources.xml" ], + "b2g_manifest": "unagi.xml", + "additional_source_tarballs": ["backup-unagi.tar.bz2"], "zip_files": [ ["{workdir}/out/target/product/unagi/*.img", "out/target/product/unagi/"], ["{workdir}/boot.img", "out/target/product/unagi/"], diff --git a/b2g/config/unagi/releng-unagi.tt b/b2g/config/unagi/releng-unagi.tt index 58763d146fef..716076e947a4 100644 --- a/b2g/config/unagi/releng-unagi.tt +++ b/b2g/config/unagi/releng-unagi.tt @@ -1,9 +1,15 @@ [ { -"size": 833424196, -"digest": "f47e040bac9a0e872dc7289993093c3d1be1befbab2d7caad17645a222147398573aa563f5485ca00ccfbf8c3cefc12d09fe91bf10499baa6d373e80de6bdd70", +"size": 84128995, +"digest": "b833dae269b02fec9a0549f467a78717a7b2bf96512caafa3736efe72610b50c5d2073b68afcdb2fea0779e2007e5ec9efc25b14d94f06e194e4ac66d49c676e", "algorithm": "sha512", -"filename": "gonk.tar.xz" +"filename": "backup-unagi.tar.bz2" +}, +{ +"size": 1570553, +"digest": "ea03de74df73b05e939c314cd15c54aac7b5488a407b7cc4f5f263f3049a1f69642c567dd35c43d0bc3f0d599d0385a26ab2dd947a6b18f9044e4918b382eea7", +"algorithm": "sha512", +"filename": "Adreno200-AU_LINUX_ANDROID_ICS_CHOCO_CS.04.00.03.06.001.zip" }, { "size": 8622080, diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 5754792ade0c..a8f914e0e0c8 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1082,6 +1082,7 @@ pref("devtools.netmonitor.enabled", true); // The default Network Monitor UI settings pref("devtools.netmonitor.panes-network-details-width", 450); +pref("devtools.netmonitor.panes-network-details-height", 450); // Enable the Tilt inspector pref("devtools.tilt.enabled", true); diff --git a/browser/base/content/browser-places.js b/browser/base/content/browser-places.js index 9371534ce269..6420a5c0263b 100644 --- a/browser/base/content/browser-places.js +++ b/browser/base/content/browser-places.js @@ -2,6 +2,8 @@ # 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/. +//////////////////////////////////////////////////////////////////////////////// +//// StarUI var StarUI = { _itemId: -1, @@ -218,10 +220,6 @@ var StarUI = { gEditItemOverlay.uninitPanel(true); }, - editButtonCommand: function SU_editButtonCommand() { - this.showEditBookmarkPopup(); - }, - cancelButtonOnCommand: function SU_cancelButtonOnCommand() { this._actionOnHide = "cancel"; this.panel.hidePopup(); @@ -241,6 +239,9 @@ var StarUI = { } } +//////////////////////////////////////////////////////////////////////////////// +//// PlacesCommandHook + var PlacesCommandHook = { /** * Adds a bookmark to the page loaded in the given browser. @@ -293,28 +294,37 @@ var PlacesCommandHook = { PlacesUtils.bookmarks.DEFAULT_INDEX, title, null, [descAnno]); PlacesUtils.transactionManager.doTransaction(txn); + itemId = txn.item.id; // Set the character-set if (charset && !PrivateBrowsingUtils.isWindowPrivate(aBrowser.contentWindow)) PlacesUtils.setCharsetForURI(uri, charset); - itemId = PlacesUtils.getMostRecentBookmarkForURI(uri); } // Revert the contents of the location bar if (gURLBar) gURLBar.handleRevert(); - // dock the panel to the star icon when possible, otherwise dock - // it to the content area - if (aBrowser.contentWindow == window.content) { - var starIcon = aBrowser.ownerDocument.getElementById("star-button"); - if (starIcon && isElementVisible(starIcon)) { - if (aShowEditUI) - StarUI.showEditBookmarkPopup(itemId, starIcon, "bottomcenter topright"); - return; - } + // If it was not requested to open directly in "edit" mode, we are done. + if (!aShowEditUI) + return; + + // Try to dock the panel to: + // 1. the bookmarks menu button + // 2. the page-proxy-favicon + // 3. the content area + if (BookmarksMenuButton.anchor) { + StarUI.showEditBookmarkPopup(itemId, BookmarksMenuButton.anchor, + "bottomcenter topright"); + return; } - StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap"); + let pageProxyFavicon = document.getElementById("page-proxy-favicon"); + if (isElementVisible(pageProxyFavicon)) { + StarUI.showEditBookmarkPopup(itemId, pageProxyFavicon, + "bottomcenter topright"); + } else { + StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap"); + } }, /** @@ -460,6 +470,9 @@ var PlacesCommandHook = { } }; +//////////////////////////////////////////////////////////////////////////////// +//// HistoryMenu + // View for the history menu. function HistoryMenu(aPopupShowingEvent) { // Workaround for Bug 610187. The sidebar does not include all the Places @@ -686,6 +699,9 @@ HistoryMenu.prototype = { } }; +//////////////////////////////////////////////////////////////////////////////// +//// BookmarksEventHandler + /** * Functions for handling events in the Bookmarks Toolbar and menu. */ @@ -811,6 +827,8 @@ var BookmarksEventHandler = { } }; +//////////////////////////////////////////////////////////////////////////////// +//// PlacesMenuDNDHandler // Handles special drag and drop functionality for Places menus that are not // part of a Places view (e.g. the bookmarks menu in the menubar). @@ -829,26 +847,37 @@ var PlacesMenuDNDHandler = { if (!this._isStaticContainer(event.target)) return; + let popup = event.target.lastChild; + if (this._loadTimer || popup.state === "showing" || popup.state === "open") + return; + this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); - this._loadTimer.initWithCallback(function() { - PlacesMenuDNDHandler._loadTimer = null; - event.target.lastChild.setAttribute("autoopened", "true"); - event.target.lastChild.showPopup(event.target.lastChild); + this._loadTimer.initWithCallback(() => { + this._loadTimer = null; + popup.setAttribute("autoopened", "true"); + popup.showPopup(popup); }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT); event.preventDefault(); event.stopPropagation(); }, /** - * Handles dragexit on the element. + * Handles dragleave on the element. * @returns true if the element is a container element (menu or * menu-toolbarbutton), false otherwise. */ - onDragExit: function PMDH_onDragExit(event) { + onDragLeave: function PMDH_onDragLeave(event) { + // Handle menu-button separate targets. + if (event.relatedTarget === event.currentTarget || + event.relatedTarget.parentNode === event.currentTarget) + return; + // Closing menus in a Places popup is handled by the view itself. if (!this._isStaticContainer(event.target)) return; + let popup = event.target.lastChild; + if (this._loadTimer) { this._loadTimer.cancel(); this._loadTimer = null; @@ -862,10 +891,9 @@ var PlacesMenuDNDHandler = { inHierarchy = node == event.target; node = node.parentNode; } - if (!inHierarchy && event.target.lastChild && - event.target.lastChild.hasAttribute("autoopened")) { - event.target.lastChild.removeAttribute("autoopened"); - event.target.lastChild.hidePopup(); + if (!inHierarchy && popup && popup.hasAttribute("autoopened")) { + popup.removeAttribute("autoopened"); + popup.hidePopup(); } }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT); }, @@ -878,7 +906,8 @@ var PlacesMenuDNDHandler = { _isStaticContainer: function PMDH__isContainer(node) { let isMenu = node.localName == "menu" || (node.localName == "toolbarbutton" && - node.getAttribute("type") == "menu"); + (node.getAttribute("type") == "menu" || + node.getAttribute("type") == "menu-button")); let isStatic = !("_placesNode" in node) && node.lastChild && node.lastChild.hasAttribute("placespopup") && !node.parentNode.hasAttribute("placespopup"); @@ -915,179 +944,13 @@ var PlacesMenuDNDHandler = { } }; +//////////////////////////////////////////////////////////////////////////////// +//// PlacesToolbarHelper -var PlacesStarButton = { - _hasBookmarksObserver: false, - uninit: function PSB_uninit() - { - if (this._hasBookmarksObserver) { - PlacesUtils.removeLazyBookmarkObserver(this); - } - if (this._pendingStmt) { - this._pendingStmt.cancel(); - delete this._pendingStmt; - } - }, - - QueryInterface: XPCOMUtils.generateQI([ - Ci.nsINavBookmarkObserver - ]), - - get _starredTooltip() - { - delete this._starredTooltip; - return this._starredTooltip = - gNavigatorBundle.getString("starButtonOn.tooltip"); - }, - get _unstarredTooltip() - { - delete this._unstarredTooltip; - return this._unstarredTooltip = - gNavigatorBundle.getString("starButtonOff.tooltip"); - }, - - updateState: function PSB_updateState() - { - this._starIcon = document.getElementById("star-button"); - if (!this._starIcon || (this._uri && gBrowser.currentURI.equals(this._uri))) { - return; - } - - // Reset tracked values. - this._uri = gBrowser.currentURI; - this._itemIds = []; - - if (this._pendingStmt) { - this._pendingStmt.cancel(); - delete this._pendingStmt; - } - - // We can load about:blank before the actual page, but there is no point in handling that page. - if (isBlankPageURL(this._uri.spec)) { - return; - } - - this._pendingStmt = PlacesUtils.asyncGetBookmarkIds(this._uri, function (aItemIds, aURI) { - // Safety check that the bookmarked URI equals the tracked one. - if (!aURI.equals(this._uri)) { - Components.utils.reportError("PlacesStarButton did not receive current URI"); - return; - } - - // It's possible that onItemAdded gets called before the async statement - // calls back. For such an edge case, retain all unique entries from both - // arrays. - this._itemIds = this._itemIds.filter( - function (id) aItemIds.indexOf(id) == -1 - ).concat(aItemIds); - this._updateStateInternal(); - - // Start observing bookmarks if needed. - if (!this._hasBookmarksObserver) { - try { - PlacesUtils.addLazyBookmarkObserver(this); - this._hasBookmarksObserver = true; - } catch(ex) { - Components.utils.reportError("PlacesStarButton failed adding a bookmarks observer: " + ex); - } - } - - delete this._pendingStmt; - }, this); - }, - - _updateStateInternal: function PSB__updateStateInternal() - { - if (!this._starIcon) { - return; - } - - if (this._itemIds.length > 0) { - this._starIcon.setAttribute("starred", "true"); - this._starIcon.setAttribute("tooltiptext", this._starredTooltip); - } - else { - this._starIcon.removeAttribute("starred"); - this._starIcon.setAttribute("tooltiptext", this._unstarredTooltip); - } - }, - - onClick: function PSB_onClick(aEvent) - { - // Ignore clicks on the star while we update its state. - if (aEvent.button == 0 && !this._pendingStmt) { - PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0); - } - // Don't bubble to the textbox, to avoid unwanted selection of the address. - aEvent.stopPropagation(); - }, - - // nsINavBookmarkObserver - onItemAdded: - function PSB_onItemAdded(aItemId, aFolder, aIndex, aItemType, aURI) - { - if (!this._starIcon) { - return; - } - - if (aURI && aURI.equals(this._uri)) { - // If a new bookmark has been added to the tracked uri, register it. - if (this._itemIds.indexOf(aItemId) == -1) { - this._itemIds.push(aItemId); - this._updateStateInternal(); - } - } - }, - - onItemRemoved: - function PSB_onItemRemoved(aItemId, aFolder, aIndex, aItemType) - { - if (!this._starIcon) { - return; - } - - let index = this._itemIds.indexOf(aItemId); - // If one of the tracked bookmarks has been removed, unregister it. - if (index != -1) { - this._itemIds.splice(index, 1); - this._updateStateInternal(); - } - }, - - onItemChanged: - function PSB_onItemChanged(aItemId, aProperty, aIsAnnotationProperty, - aNewValue, aLastModified, aItemType) - { - if (!this._starIcon) { - return; - } - - if (aProperty == "uri") { - let index = this._itemIds.indexOf(aItemId); - // If the changed bookmark was tracked, check if it is now pointing to - // a different uri and unregister it. - if (index != -1 && aNewValue != this._uri.spec) { - this._itemIds.splice(index, 1); - this._updateStateInternal(); - } - // If another bookmark is now pointing to the tracked uri, register it. - else if (index == -1 && aNewValue == this._uri.spec) { - this._itemIds.push(aItemId); - this._updateStateInternal(); - } - } - }, - - onBeginUpdateBatch: function () {}, - onEndUpdateBatch: function () {}, - onItemVisited: function () {}, - onItemMoved: function () {} -}; - - -// This object handles the initialization and uninitialization of the bookmarks -// toolbar. updateState is called when the browser window is opened and -// after closing the toolbar customization dialog. +/** + * This object handles the initialization and uninitialization of the bookmarks + * toolbar. + */ let PlacesToolbarHelper = { _place: "place:folder=TOOLBAR", @@ -1127,58 +990,95 @@ let PlacesToolbarHelper = { } }; +//////////////////////////////////////////////////////////////////////////////// +//// BookmarksMenuButton -// Handles the bookmarks menu button shown when the main menubar is hidden. +/** + * Handles the bookmarks menu-button in the toolbar. + */ let BookmarksMenuButton = { get button() { - return document.getElementById("bookmarks-menu-button"); + if (!this._button) { + this._button = document.getElementById("bookmarks-menu-button"); + } + return this._button; }, - get buttonContainer() { - return document.getElementById("bookmarks-menu-button-container"); + get star() { + if (!this._star && this.button) { + this._star = document.getAnonymousElementByAttribute(this.button, + "anonid", + "button"); + } + return this._star; }, - get personalToolbar() { - delete this.personalToolbar; - return this.personalToolbar = document.getElementById("PersonalToolbar"); + get anchor() { + if (!this._anchor && this.star && isElementVisible(this.star)) { + // Anchor to the icon, so the panel looks more natural. + this._anchor = document.getAnonymousElementByAttribute(this.star, + "class", + "toolbarbutton-icon"); + } + return this._anchor; }, - get bookmarksToolbarItem() { - return document.getElementById("personal-bookmarks"); + STATUS_UPDATING: -1, + STATUS_UNSTARRED: 0, + STATUS_STARRED: 1, + get status() { + if (this._pendingStmt) + return this.STATUS_UPDATING; + return this.button && + this.button.hasAttribute("starred") ? this.STATUS_STARRED + : this.STATUS_UNSTARRED; }, - init: function BMB_init() { - this.updatePosition(); - - // Any other stuff that does not regard the button itself should be - // handled in the onPopupShowing handler, so it does not hit Ts. + get _starredTooltip() + { + delete this._starredTooltip; + return this._starredTooltip = + gNavigatorBundle.getString("starButtonOn.tooltip"); + }, + + get _unstarredTooltip() + { + delete this._unstarredTooltip; + return this._unstarredTooltip = + gNavigatorBundle.getString("starButtonOff.tooltip"); + }, + + /** + * The popup contents must be updated when the user customizes the UI, or + * changes the personal toolbar collapsed status. In such a case, any needed + * change should be handled in the popupshowing helper, for performance + * reasons. + */ + _popupNeedsUpdate: true, + onToolbarVisibilityChange: function BMB_onToolbarVisibilityChange() { + this._popupNeedsUpdate = true; }, - _popupNeedsUpdate: {}, onPopupShowing: function BMB_onPopupShowing(event) { // Don't handle events for submenus. if (event.target != event.currentTarget) return; - let popup = event.target; - let needsUpdate = this._popupNeedsUpdate[popup.id]; - - // Check if popup contents need to be updated. Note that if needsUpdate is - // undefined we have never seen the popup, thus it should be updated. - if (needsUpdate === false) + if (!this._popupNeedsUpdate) return; - this._popupNeedsUpdate[popup.id] = false; + this._popupNeedsUpdate = false; - function getPlacesAnonymousElement(aAnonId) - document.getAnonymousElementByAttribute(popup.parentNode, - "placesanonid", - aAnonId); + let popup = event.target; + let getPlacesAnonymousElement = + aAnonId => document.getAnonymousElementByAttribute(popup.parentNode, + "placesanonid", + aAnonId); let viewToolbarMenuitem = getPlacesAnonymousElement("view-toolbar"); if (viewToolbarMenuitem) { // Update View bookmarks toolbar checkbox menuitem. - viewToolbarMenuitem.setAttribute("checked", - !this.personalToolbar.collapsed); + let personalToolbar = document.getElementById("PersonalToolbar"); + viewToolbarMenuitem.setAttribute("checked", !personalToolbar.collapsed); } let toolbarMenuitem = getPlacesAnonymousElement("toolbar-autohide"); @@ -1186,68 +1086,44 @@ let BookmarksMenuButton = { // If bookmarks items are visible, hide Bookmarks Toolbar menu and the // separator after it. toolbarMenuitem.collapsed = toolbarMenuitem.nextSibling.collapsed = - isElementVisible(this.bookmarksToolbarItem); + isElementVisible(document.getElementById("personal-bookmarks")); } }, - updatePosition: function BMB_updatePosition() { - // Popups will have to be updated when the user customizes the UI, or - // changes personal toolbar collapsed status. Both of those location call - // updatePosition(), so this is the only point asking for popup updates. - for (let popupId in this._popupNeedsUpdate) { - this._popupNeedsUpdate[popupId] = true; + /** + * Handles star styling based on page proxy state changes. + */ + onPageProxyStateChanged: function BMB_onPageProxyStateChanged(aState) { + if (!this.star) { + return; } - let button = this.button; - if (!button) - return; - - // If the toolbar containing bookmarks is visible, we want to move the - // button to bookmarksToolbarItem. - let bookmarksToolbarItem = this.bookmarksToolbarItem; - let bookmarksOnVisibleToolbar = bookmarksToolbarItem && - !bookmarksToolbarItem.parentNode.collapsed && - bookmarksToolbarItem.parentNode.getAttribute("autohide") != "true"; - - // If the container has been moved by the user to the toolbar containing - // bookmarks, we want to preserve the desired position. - let container = this.buttonContainer; - let containerNearBookmarks = container && bookmarksToolbarItem && - container.parentNode == bookmarksToolbarItem.parentNode; - - if (bookmarksOnVisibleToolbar && !containerNearBookmarks) { - if (button.parentNode != bookmarksToolbarItem) { - this._uninitView(); - bookmarksToolbarItem.appendChild(button); - } + if (aState == "invalid") { + this.star.setAttribute("disabled", "true"); + this.button.removeAttribute("starred"); } else { - if (container && button.parentNode != container) { - this._uninitView(); - container.appendChild(button); - } + this.star.removeAttribute("disabled"); } this._updateStyle(); }, _updateStyle: function BMB__updateStyle() { - let button = this.button; - if (!button) + if (!this.star) { return; + } - let container = this.buttonContainer; - let containerOnPersonalToolbar = container && - (container.parentNode == this.personalToolbar || - container.parentNode.parentNode == this.personalToolbar); + let personalToolbar = document.getElementById("PersonalToolbar"); + let onPersonalToolbar = this.button.parentNode == personalToolbar || + this.button.parentNode.parentNode == personalToolbar; - if (button.parentNode == this.bookmarksToolbarItem || - containerOnPersonalToolbar) { - button.classList.add("bookmark-item"); - button.classList.remove("toolbarbutton-1"); + if (onPersonalToolbar) { + this.button.classList.add("bookmark-item"); + this.button.classList.remove("toolbarbutton-1"); } else { - button.classList.remove("bookmark-item"); - button.classList.add("toolbarbutton-1"); + this.button.classList.remove("bookmark-item"); + this.button.classList.add("toolbarbutton-1"); } }, @@ -1255,20 +1131,13 @@ let BookmarksMenuButton = { // When an element with a placesView attached is removed and re-inserted, // XBL reapplies the binding causing any kind of issues and possible leaks, // so kill current view and let popupshowing generate a new one. - let button = this.button; - if (button && button._placesView) - button._placesView.uninit(); + if (this.button && this.button._placesView) { + this.button._placesView.uninit(); + } }, customizeStart: function BMB_customizeStart() { this._uninitView(); - let button = this.button; - let container = this.buttonContainer; - if (button && container && button.parentNode != container) { - // Move button back to the container, so user can move or remove it. - container.appendChild(button); - this._updateStyle(); - } }, customizeChange: function BMB_customizeChange() { @@ -1276,6 +1145,159 @@ let BookmarksMenuButton = { }, customizeDone: function BMB_customizeDone() { - this.updatePosition(); - } + delete this._button; + delete this._star; + delete this._anchor; + this.onToolbarVisibilityChange(); + this._updateStyle(); + }, + + _hasBookmarksObserver: false, + uninit: function BMB_uninit() { + this._uninitView(); + + if (this._hasBookmarksObserver) { + PlacesUtils.removeLazyBookmarkObserver(this); + } + + if (this._pendingStmt) { + this._pendingStmt.cancel(); + delete this._pendingStmt; + } + }, + + updateStarState: function BMB_updateStarState() { + if (!this.button || (this._uri && gBrowser.currentURI.equals(this._uri))) { + return; + } + + // Reset tracked values. + this._uri = gBrowser.currentURI; + this._itemIds = []; + + if (this._pendingStmt) { + this._pendingStmt.cancel(); + delete this._pendingStmt; + } + + // We can load about:blank before the actual page, but there is no point in handling that page. + if (isBlankPageURL(this._uri.spec)) { + return; + } + + this._pendingStmt = PlacesUtils.asyncGetBookmarkIds(this._uri, function (aItemIds, aURI) { + // Safety check that the bookmarked URI equals the tracked one. + if (!aURI.equals(this._uri)) { + Components.utils.reportError("BookmarksMenuButton did not receive current URI"); + return; + } + + // It's possible that onItemAdded gets called before the async statement + // calls back. For such an edge case, retain all unique entries from both + // arrays. + this._itemIds = this._itemIds.filter( + function (id) aItemIds.indexOf(id) == -1 + ).concat(aItemIds); + + this._updateStar(); + + // Start observing bookmarks if needed. + if (!this._hasBookmarksObserver) { + try { + PlacesUtils.addLazyBookmarkObserver(this); + this._hasBookmarksObserver = true; + } catch(ex) { + Components.utils.reportError("BookmarksMenuButton failed adding a bookmarks observer: " + ex); + } + } + + delete this._pendingStmt; + }, this); + }, + + _updateStar: function BMB__updateStar() { + if (!this.button) { + return; + } + + if (this._itemIds.length > 0) { + this.button.setAttribute("starred", "true"); + this.button.setAttribute("tooltiptext", this._starredTooltip); + } + else { + this.button.removeAttribute("starred"); + this.button.setAttribute("tooltiptext", this._unstarredTooltip); + } + }, + + onCommand: function BMB_onCommand(aEvent) { + if (aEvent.target != aEvent.currentTarget) { + return; + } + // Ignore clicks on the star if we are updating its state. + if (!this._pendingStmt) { + PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0); + } + }, + + // nsINavBookmarkObserver + onItemAdded: function BMB_onItemAdded(aItemId, aParentId, aIndex, aItemType, + aURI) { + if (!this.button) { + return; + } + + if (aURI && aURI.equals(this._uri)) { + // If a new bookmark has been added to the tracked uri, register it. + if (this._itemIds.indexOf(aItemId) == -1) { + this._itemIds.push(aItemId); + this._updateStar(); + } + } + }, + + onItemRemoved: function BMB_onItemRemoved(aItemId) { + if (!this.button) { + return; + } + + let index = this._itemIds.indexOf(aItemId); + // If one of the tracked bookmarks has been removed, unregister it. + if (index != -1) { + this._itemIds.splice(index, 1); + this._updateStar(); + } + }, + + onItemChanged: function BMB_onItemChanged(aItemId, aProperty, + aIsAnnotationProperty, aNewValue) { + if (!this.button) { + return; + } + + if (aProperty == "uri") { + let index = this._itemIds.indexOf(aItemId); + // If the changed bookmark was tracked, check if it is now pointing to + // a different uri and unregister it. + if (index != -1 && aNewValue != this._uri.spec) { + this._itemIds.splice(index, 1); + this._updateStar(); + } + // If another bookmark is now pointing to the tracked uri, register it. + else if (index == -1 && aNewValue == this._uri.spec) { + this._itemIds.push(aItemId); + this._updateStar(); + } + } + }, + + onBeginUpdateBatch: function () {}, + onEndUpdateBatch: function () {}, + onBeforeItemRemoved: function () {}, + onItemVisited: function () {}, + onItemMoved: function () {}, + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsINavBookmarkObserver + ]), }; diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index 14615818af4a..3a9b92da04e8 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -308,19 +308,20 @@ toolbarbutton.bookmark-item { max-width: 13em; } -%ifdef MENUBAR_CAN_AUTOHIDE -#toolbar-menubar:not([autohide="true"]) ~ #nav-bar > #bookmarks-menu-button-container, -#toolbar-menubar:not([autohide="true"]) ~ toolbar > #personal-bookmarks > #bookmarks-menu-button, -#toolbar-menubar:not([autohide="true"]) > #personal-bookmarks > #bookmarks-menu-button { - display: none; -} -%endif - #editBMPanel_tagsSelector { /* override default listbox width from xul.css */ width: auto; } +/* The star doesn't make sense as text */ +toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-icon { + display: -moz-box !important; +} +toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-text, +toolbar[mode="full"] #bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button > .toolbarbutton-text { + display: none; +} + menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result { display: none; } diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 6b946974a9bd..c4ec8ab28370 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -953,7 +953,6 @@ var gBrowserInit = { // Misc. inits. CombinedStopReload.init(); TabsOnTop.init(); - BookmarksMenuButton.init(); gPrivateBrowsingUI.init(); TabsInTitlebar.init(); retrieveToolbarIconsizesFromTheme(); @@ -1331,7 +1330,7 @@ var gBrowserInit = { } catch (ex) { } - PlacesStarButton.uninit(); + BookmarksMenuButton.uninit(); TabsOnTop.uninit(); @@ -2252,6 +2251,8 @@ function UpdatePageProxyState() function SetPageProxyState(aState) { + BookmarksMenuButton.onPageProxyStateChanged(aState); + if (!gURLBar) return; @@ -3794,7 +3795,7 @@ var XULBrowserWindow = { URLBarSetURI(aLocationURI); // Update starring UI - PlacesStarButton.updateState(); + BookmarksMenuButton.updateStarState(); SocialShareButton.updateShareState(); } @@ -4412,7 +4413,7 @@ function setToolbarVisibility(toolbar, isVisible) { document.persist(toolbar.id, hidingAttribute); PlacesToolbarHelper.init(); - BookmarksMenuButton.updatePosition(); + BookmarksMenuButton.onToolbarVisibilityChange(); gBrowser.updateWindowResizers(); #ifdef MENUBAR_CAN_AUTOHIDE @@ -6778,8 +6779,10 @@ let gPrivateBrowsingUI = { } } - if (gURLBar) { - // Disable switch to tab autocompletion for private windows + if (gURLBar && + !PrivateBrowsingUtils.permanentPrivateBrowsing) { + // Disable switch to tab autocompletion for private windows + // (not for "Always use private browsing" mode) gURLBar.setAttribute("autocompletesearchparam", ""); } } @@ -6802,9 +6805,10 @@ function switchToTabHavingURI(aURI, aOpenNew) { // This will switch to the tab in aWindow having aURI, if present. function switchIfURIInWindow(aWindow) { // Only switch to the tab if neither the source and desination window are - // private. - if (PrivateBrowsingUtils.isWindowPrivate(window) || - PrivateBrowsingUtils.isWindowPrivate(aWindow)) { + // private and they are not in permanent private borwsing mode + if ((PrivateBrowsingUtils.isWindowPrivate(window) || + PrivateBrowsingUtils.isWindowPrivate(aWindow)) && + !PrivateBrowsingUtils.permanentPrivateBrowsing) { return false; } diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 693fe1c8deb6..3a1fd8484791 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -528,7 +528,7 @@ toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;" fullscreentoolbar="true" mode="icons" customizable="true" iconsize="large" - defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,webrtc-status-button,downloads-button,home-button,bookmarks-menu-button-container,window-controls" + defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,webrtc-status-button,bookmarks-menu-button,downloads-button,home-button,window-controls" customizationtarget="nav-bar-customizationtarget" context="toolbar-context-menu"> @@ -626,9 +626,6 @@ hidden="true" onclick="SocialShareButton.onClick(event);"/> - + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/browser/base/content/sync/utils.js b/browser/base/content/sync/utils.js index 630581eabb6a..af4c8a811542 100644 --- a/browser/base/content/sync/utils.js +++ b/browser/base/content/sync/utils.js @@ -2,6 +2,11 @@ * 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/. */ +// Equivalent to 0600 permissions; used for saved Sync Recovery Key. +// This constant can be replaced when the equivalent values are available to +// chrome JS; see Bug 433295 and Bug 757351. +const PERMISSIONS_RWUSR = 0x180; + // Weave should always exist before before this file gets included. let gSyncUtils = { get bundle() { @@ -157,7 +162,7 @@ let gSyncUtils = { aResult == Ci.nsIFilePicker.returnReplace) { let stream = Cc["@mozilla.org/network/file-output-stream;1"]. createInstance(Ci.nsIFileOutputStream); - stream.init(fp.file, -1, 0600, 0); + stream.init(fp.file, -1, PERMISSIONS_RWUSR, 0); let serializer = new XMLSerializer(); let output = serializer.serializeToString(iframe.contentDocument); diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 819e190f97f0..a8e93736a45b 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -650,7 +650,8 @@ // Tabs in private windows aren't registered as "Open" so // that they don't appear as switch-to-tab candidates. if (!isBlankPageURL(aLocation.spec) && - !PrivateBrowsingUtils.isWindowPrivate(window)) { + (!PrivateBrowsingUtils.isWindowPrivate(window) || + PrivateBrowsingUtils.permanentPrivateBrowsing)) { autocomplete.registerOpenPage(aLocation); this.mBrowser.registeredOpenURI = aLocation; } diff --git a/browser/base/content/test/browser_bug432599.js b/browser/base/content/test/browser_bug432599.js index 2fd667d9c553..be28e6d88b6d 100644 --- a/browser/base/content/test/browser_bug432599.js +++ b/browser/base/content/test/browser_bug432599.js @@ -17,15 +17,15 @@ function invokeUsingCtrlD(phase) { function invokeUsingStarButton(phase) { switch (phase) { case 1: - EventUtils.sendMouseEvent({ type: "click" }, "star-button"); + EventUtils.synthesizeMouseAtCenter(BookmarksMenuButton.star, {}); break; case 2: case 4: EventUtils.synthesizeKey("VK_ESCAPE", {}); break; case 3: - EventUtils.synthesizeMouse(document.getElementById("star-button"), - 1, 1, { clickCount: 2 }); + EventUtils.synthesizeMouseAtCenter(BookmarksMenuButton.star, + { clickCount: 2 }); break; } } @@ -44,8 +44,8 @@ function test() { waitForExplicitFinish(); gBrowser.selectedTab = gBrowser.addTab(); - gBrowser.selectedBrowser.addEventListener("load", function () { - gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); + gBrowser.selectedBrowser.addEventListener("load", function onLoad() { + gBrowser.selectedBrowser.removeEventListener("load", onLoad, true); waitForStarChange(false, initTest); }, true); @@ -60,10 +60,12 @@ function initTest() { } function waitForStarChange(aValue, aCallback) { - let starButton = document.getElementById("star-button"); - if (PlacesStarButton._pendingStmt || starButton.hasAttribute("starred") != aValue) { + let expectedStatus = aValue ? BookmarksMenuButton.STATUS_STARRED + : BookmarksMenuButton.STATUS_UNSTARRED; + if (BookmarksMenuButton.status == BookmarksMenuButton.STATUS_UPDATING || + BookmarksMenuButton.status != expectedStatus) { info("Waiting for star button change."); - setTimeout(arguments.callee, 50, aValue, aCallback); + setTimeout(waitForStarChange, 50, aValue, aCallback); return; } aCallback(); diff --git a/browser/base/content/test/browser_bug581253.js b/browser/base/content/test/browser_bug581253.js index d8f4e7ef4f36..575bfaf94c08 100644 --- a/browser/base/content/test/browser_bug581253.js +++ b/browser/base/content/test/browser_bug581253.js @@ -4,7 +4,6 @@ let testURL = "data:text/plain,nothing but plain text"; let testTag = "581253_tag"; -let starButton = document.getElementById("star-button"); let timerID = -1; function test() { @@ -35,18 +34,19 @@ function test() { } function waitForStarChange(aValue, aCallback) { - if (PlacesStarButton._pendingStmt || starButton.hasAttribute("starred") != aValue) { + let expectedStatus = aValue ? BookmarksMenuButton.STATUS_STARRED + : BookmarksMenuButton.STATUS_UNSTARRED; + if (BookmarksMenuButton.status == BookmarksMenuButton.STATUS_UPDATING || + BookmarksMenuButton.status != expectedStatus) { info("Waiting for star button change."); - info("pendingStmt: " + (!!PlacesStarButton._pendingStmt) + ", hasAttribute: " + starButton.hasAttribute("starred") + ", tracked uri: " + PlacesStarButton._uri.spec); - timerID = setTimeout(arguments.callee, 50, aValue, aCallback); + setTimeout(waitForStarChange, 50, aValue, aCallback); return; } - timerID = -1; aCallback(); } function onStarred() { - ok(starButton.getAttribute("starred") == "true", + is(BookmarksMenuButton.status, BookmarksMenuButton.STATUS_STARRED, "star button indicates that the page is bookmarked"); let uri = makeURI(testURL); @@ -54,7 +54,7 @@ function onStarred() { PlacesUtils.transactionManager.doTransaction(tagTxn); StarUI.panel.addEventListener("popupshown", onPanelShown, false); - starButton.click(); + BookmarksMenuButton.star.click(); } function onPanelShown(aEvent) { @@ -93,7 +93,7 @@ function onPanelHidden(aEvent) { executeSoon(function() { ok(!PlacesUtils.bookmarks.isBookmarked(makeURI(testURL)), "the bookmark for the test url has been removed"); - ok(!starButton.hasAttribute("starred"), + is(BookmarksMenuButton.status, BookmarksMenuButton.STATUS_UNSTARRED, "star button indicates that the bookmark has been removed"); gBrowser.removeCurrentTab(); waitForClearHistory(finish); diff --git a/browser/base/content/test/browser_bug624734.js b/browser/base/content/test/browser_bug624734.js index 826ddf2cb70b..e64a4373945a 100644 --- a/browser/base/content/test/browser_bug624734.js +++ b/browser/base/content/test/browser_bug624734.js @@ -11,7 +11,8 @@ function test() { tab.linkedBrowser.addEventListener("load", (function(event) { tab.linkedBrowser.removeEventListener("load", arguments.callee, true); - is(PlacesStarButton._starIcon.getAttribute("tooltiptext"), PlacesStarButton._unstarredTooltip, + is(BookmarksMenuButton.button.getAttribute("tooltiptext"), + BookmarksMenuButton._unstarredTooltip, "Star icon should have the unstarred tooltip text"); gBrowser.removeCurrentTab(); diff --git a/browser/components/downloads/content/downloads.css b/browser/components/downloads/content/downloads.css index 874f0e65d903..7cb845601648 100644 --- a/browser/components/downloads/content/downloads.css +++ b/browser/components/downloads/content/downloads.css @@ -91,6 +91,11 @@ richlistitem[type="download"]:not([selected]) button { visibility: hidden; } +.download-state[state="1"]:not([exists]) .downloadShow +{ + display: none; +} + #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryProgress, #downloadsSummary:not([inprogress]) > vbox > #downloadsSummaryDetails, #downloadsFooter[showingsummary] > #downloadsHistory, diff --git a/browser/components/downloads/content/downloads.js b/browser/components/downloads/content/downloads.js index f81b43c8c5fc..be0b9129ea04 100644 --- a/browser/components/downloads/content/downloads.js +++ b/browser/components/downloads/content/downloads.js @@ -71,6 +71,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils", "resource://gre/modules/DownloadUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", "resource:///modules/DownloadsCommon.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "OS", + "resource://gre/modules/osfile.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", @@ -553,6 +555,14 @@ const DownloadsPanel = { return; } + // When the panel is opened, we check if the target files of visible items + // still exist, and update the allowed items interactions accordingly. We + // do these checks on a background thread, and don't prevent the panel to + // be displayed while these checks are being performed. + for each (let viewItem in DownloadsView._viewItems) { + viewItem.verifyTargetExists(); + } + if (aAnchor) { DownloadsCommon.log("Opening downloads panel popup."); this.panel.openPopup(aAnchor, "bottomcenter topright", 0, 0, false, @@ -1074,6 +1084,7 @@ function DownloadsViewItem(aDataItem, aElement) // Initialize more complex attributes. this._updateProgress(); this._updateStatusLine(); + this.verifyTargetExists(); } DownloadsViewItem.prototype = { @@ -1111,6 +1122,12 @@ DownloadsViewItem.prototype = { if (aOldState != Ci.nsIDownloadManager.DOWNLOAD_FINISHED && aOldState != this.dataItem.state) { this._element.setAttribute("image", this.image + "&state=normal"); + + // We assume the existence of the target of a download that just completed + // successfully, without checking the condition in the background. If the + // panel is already open, this will take effect immediately. If the panel + // is opened later, a new background existence check will be performed. + this._element.setAttribute("exists", "true"); } // Update the user interface after switching states. @@ -1252,7 +1269,33 @@ DownloadsViewItem.prototype = { } let [size, unit] = DownloadUtils.convertByteUnits(fileSize); return DownloadsCommon.strings.sizeWithUnits(size, unit); - } + }, + + ////////////////////////////////////////////////////////////////////////////// + //// Functions called by the panel + + /** + * Starts checking whether the target file of a finished download is still + * available on disk, and sets an attribute that controls how the item is + * presented visually. + * + * The existence check is executed on a background thread. + */ + verifyTargetExists: function DVI_verifyTargetExists() { + // We don't need to check if the download is not finished successfully. + if (!this.dataItem.openable) { + return; + } + + OS.File.exists(this.dataItem.localFile.path).then( + function DVI_RTE_onSuccess(aExists) { + if (aExists) { + this._element.setAttribute("exists", "true"); + } else { + this._element.removeAttribute("exists"); + } + }.bind(this), Cu.reportError); + }, }; //////////////////////////////////////////////////////////////////////////////// diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index bf1ad021b18f..0dcda6248ebf 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -1170,7 +1170,7 @@ BrowserGlue.prototype = { }, _migrateUI: function BG__migrateUI() { - const UI_VERSION = 11; + const UI_VERSION = 12; const BROWSER_DOCURL = "chrome://browser/content/browser.xul#"; let currentUIVersion = 0; try { @@ -1336,6 +1336,33 @@ BrowserGlue.prototype = { Services.prefs.clearUserPref("permissions.default.image"); } + if (currentUIVersion < 12) { + // Remove bookmarks-menu-button-container, then place + // bookmarks-menu-button into its position. + let currentsetResource = this._rdf.GetResource("currentset"); + let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar"); + let currentset = this._getPersist(toolbarResource, currentsetResource); + // Need to migrate only if toolbar is customized. + if (currentset) { + if (currentset.contains("bookmarks-menu-button-container")) + currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,"$2"); + + // Now insert the new button. + if (currentset.contains("downloads-button")) { + currentset = currentset.replace(/(^|,)downloads-button($|,)/, + "$1bookmarks-menu-button,downloads-button$2"); + } else if (currentset.contains("home-button")) { + currentset = currentset.replace(/(^|,)home-button($|,)/, + "$1bookmarks-menu-button,home-button$2"); + } else { + // Just append. + currentset = currentset.replace(/(^|,)window-controls($|,)/, + "$1bookmarks-menu-button,window-controls$2") + } + this._setPersist(toolbarResource, currentsetResource, currentset); + } + } + if (this._dirty) this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush(); diff --git a/browser/devtools/commandline/gcli.jsm b/browser/devtools/commandline/gcli.jsm index 09e4c14b9cbe..182c380f895e 100644 --- a/browser/devtools/commandline/gcli.jsm +++ b/browser/devtools/commandline/gcli.jsm @@ -7308,12 +7308,10 @@ exports.Output = Output; * Functions and data related to the execution of a command */ exports.createExecutionContext = function(requisition) { - return { + var context = { exec: requisition.exec.bind(requisition), update: requisition.update.bind(requisition), updateExec: requisition.updateExec.bind(requisition), - document: requisition.document, - environment: requisition.environment, createView: view.createView, typedData: function(data, type) { return { @@ -7334,6 +7332,18 @@ exports.createExecutionContext = function(requisition) { return Promise.defer(); } }; + + Object.defineProperty(context, 'environment', { + get: function() { return requisition.environment; }, + enumerable : true + }); + + Object.defineProperty(context, 'document', { + get: function() { return requisition.document; }, + enumerable : true + }); + + return context; }; diff --git a/browser/devtools/debugger/test/browser_dbg_propertyview-data.js b/browser/devtools/debugger/test/browser_dbg_propertyview-data.js index b6a58ccb5a23..3a70ae9a7fd5 100644 --- a/browser/devtools/debugger/test/browser_dbg_propertyview-data.js +++ b/browser/devtools/debugger/test/browser_dbg_propertyview-data.js @@ -653,12 +653,12 @@ function testKeyboardAccessibility(callback) { "The 0 item should be focused now."); EventUtils.sendKey("END", gDebugger); - is(gVariablesView.getFocusedItem().name, "foo", - "The foo item should be focused now."); + is(gVariablesView.getFocusedItem().name, "bar", + "The bar item should be focused now."); EventUtils.sendKey("DOWN", gDebugger); is(gVariablesView.getFocusedItem().name, "bar", - "The bar item should be focused now."); + "The bar item should still be focused now."); EventUtils.sendKey("UP", gDebugger); is(gVariablesView.getFocusedItem().name, "foo", @@ -669,10 +669,14 @@ function testKeyboardAccessibility(callback) { "The foo item should still be focused now."); EventUtils.sendKey("PAGE_DOWN", gDebugger); - is(gVariablesView.getFocusedItem().name, "foo", - "The foo item should still be focused now."); + is(gVariablesView.getFocusedItem().name, "bar", + "The bar item should be focused now."); EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "someProp7", + "The someProp7 item should be focused now."); + + EventUtils.sendKey("UP", gDebugger); is(gVariablesView.getFocusedItem().name, "__proto__", "The __proto__ item should be focused now."); @@ -684,10 +688,6 @@ function testKeyboardAccessibility(callback) { is(gVariablesView.getFocusedItem().name, "get", "The get item should be focused now."); - EventUtils.sendKey("UP", gDebugger); - is(gVariablesView.getFocusedItem().name, "p8", - "The p8 item should be focused now."); - EventUtils.sendKey("HOME", gDebugger); is(gVariablesView.getFocusedItem().name, "someProp0", "The someProp0 item should be focused now."); @@ -828,6 +828,18 @@ function testKeyboardAccessibility(callback) { is(gVariablesView.getFocusedItem().expanded, false, "The top-level __proto__ item should not be expanded."); + EventUtils.sendKey("END", gDebugger); + is(gVariablesView.getFocusedItem().name, "foo", + "The foo scope should be focused."); + + EventUtils.sendKey("PAGE_UP", gDebugger); + is(gVariablesView.getFocusedItem().name, "__proto__", + "The __proto__ property should be focused."); + + EventUtils.sendKey("PAGE_DOWN", gDebugger); + is(gVariablesView.getFocusedItem().name, "foo", + "The foo scope should be focused."); + executeSoon(callback); }); }); diff --git a/browser/devtools/framework/Target.jsm b/browser/devtools/framework/Target.jsm index e3103efb6e98..e5a17a8314fd 100644 --- a/browser/devtools/framework/Target.jsm +++ b/browser/devtools/framework/Target.jsm @@ -275,25 +275,31 @@ TabTarget.prototype = { this._setupRemoteListeners(); - if (this.isRemote) { - // In the remote debugging case, the protocol connection will have been - // already initialized in the connection screen code. - this._remote.resolve(null); - } else { + let attachTab = () => { + this._client.attachTab(this._form.actor, (aResponse, aTabClient) => { + if (!aTabClient) { + this._remote.reject("Unable to attach to the tab"); + return; + } + this.threadActor = aResponse.threadActor; + this._remote.resolve(null); + }); + }; + + if (this.isLocalTab) { this._client.connect((aType, aTraits) => { this._client.listTabs(aResponse => { this._form = aResponse.tabs[aResponse.selected]; - - this._client.attachTab(this._form.actor, (aResponse, aTabClient) => { - if (!aTabClient) { - this._remote.reject("Unable to attach to the tab"); - return; - } - this.threadActor = aResponse.threadActor; - this._remote.resolve(null); - }); + attachTab(); }); }); + } else if (!this.chrome) { + // In the remote debugging case, the protocol connection will have been + // already initialized in the connection screen code. + attachTab(); + } else { + // Remote chrome debugging doesn't need anything at this point. + this._remote.resolve(null); } return this._remote.promise; diff --git a/browser/devtools/netmonitor/netmonitor-controller.js b/browser/devtools/netmonitor/netmonitor-controller.js index 1912fbf62d30..5c9efe8feec4 100644 --- a/browser/devtools/netmonitor/netmonitor-controller.js +++ b/browser/devtools/netmonitor/netmonitor-controller.js @@ -515,7 +515,8 @@ let L10N = new ViewHelpers.L10N(NET_STRINGS_URI); * Shortcuts for accessing various network monitor preferences. */ let Prefs = new ViewHelpers.Prefs("devtools.netmonitor", { - networkDetailsWidth: ["Int", "panes-network-details-width"] + networkDetailsWidth: ["Int", "panes-network-details-width"], + networkDetailsHeight: ["Int", "panes-network-details-height"] }); /** diff --git a/browser/devtools/netmonitor/netmonitor-view.js b/browser/devtools/netmonitor/netmonitor-view.js index 1b489ecf4159..e50b4a596f9f 100644 --- a/browser/devtools/netmonitor/netmonitor-view.js +++ b/browser/devtools/netmonitor/netmonitor-view.js @@ -7,6 +7,7 @@ const EPSILON = 0.001; const REQUESTS_REFRESH_RATE = 50; // ms +const REQUESTS_HEADERS_SAFE_BOUNDS = 30; // px const REQUESTS_WATERFALL_SAFE_BOUNDS = 100; // px const REQUESTS_WATERFALL_BACKGROUND_PATTERN = [5, 250, 1000, 2000]; // ms const DEFAULT_HTTP_VERSION = "HTTP/1.1"; @@ -102,6 +103,7 @@ let NetMonitorView = { this._expandPaneString = L10N.getStr("expandDetailsPane"); this._detailsPane.setAttribute("width", Prefs.networkDetailsWidth); + this._detailsPane.setAttribute("height", Prefs.networkDetailsHeight); this.toggleDetailsPane({ visible: false }); }, @@ -112,6 +114,7 @@ let NetMonitorView = { dumpn("Destroying the NetMonitorView panes"); Prefs.networkDetailsWidth = this._detailsPane.getAttribute("width"); + Prefs.networkDetailsHeight = this._detailsPane.getAttribute("height"); this._detailsPane = null; this._detailsPaneToggleButton = null; @@ -592,6 +595,27 @@ create({ constructor: RequestsMenuView, proto: MenuContainer.prototype }, { // the window is resized, this needs to be invalidated. if (aReset) { this._cachedWaterfallWidth = 0; + + let table = $("#network-table"); + let toolbar = $("#requests-menu-toolbar"); + let columns = [ + [".requests-menu-waterfall", "waterfall-overflows"], + [".requests-menu-size", "size-overflows"], + [".requests-menu-type", "type-overflows"], + [".requests-menu-domain", "domain-overflows"] + ]; + + // Flush headers. + columns.forEach(([, attribute]) => table.removeAttribute(attribute)); + let availableWidth = toolbar.getBoundingClientRect().width; + + // Hide overflowing columns. + columns.forEach(([className, attribute]) => { + let bounds = $(".requests-menu-header" + className).getBoundingClientRect(); + if (bounds.right > availableWidth - REQUESTS_HEADERS_SAFE_BOUNDS) { + table.setAttribute(attribute, ""); + } + }); } // Determine the scaling to be applied to all the waterfalls so that diff --git a/browser/devtools/netmonitor/netmonitor.css b/browser/devtools/netmonitor/netmonitor.css index 4eb682a1a184..d4b8ff8215e2 100644 --- a/browser/devtools/netmonitor/netmonitor.css +++ b/browser/devtools/netmonitor/netmonitor.css @@ -13,8 +13,36 @@ /* Responsive sidebar */ @media (max-width: 700px) { + #toolbar-spacer, #details-pane-toggle, + #details-pane[pane-collapsed], .requests-menu-waterfall { display: none; } } + +@media (min-width: 701px) { + #network-table[waterfall-overflows] .requests-menu-waterfall { + display: none; + } + + #network-table[size-overflows] .requests-menu-size { + display: none; + } + + #network-table[type-overflows] .requests-menu-type { + display: none; + } + + #network-table[domain-overflows] .requests-menu-domain { + display: none; + } + + #network-table[type-overflows] .requests-menu-domain { + -moz-box-flex: 1; + } + + #network-table[domain-overflows] .requests-menu-file { + -moz-box-flex: 1; + } +} diff --git a/browser/devtools/netmonitor/netmonitor.xul b/browser/devtools/netmonitor/netmonitor.xul index 88db6a46e348..5d0fa50949ba 100644 --- a/browser/devtools/netmonitor/netmonitor.xul +++ b/browser/devtools/netmonitor/netmonitor.xul @@ -46,7 +46,7 @@ class="plain requests-menu-header requests-menu-waterfall" value="&netmonitorUI.toolbar.waterfall;" crop="end"/> - + { info("Starting test... "); + // This test reopens the network monitor a bunch of times, for different + // hosts (bottom, side, window). This seems to be slow on debug builds. + requestLongerTimeout(2); + let prefsToCheck = { networkDetailsWidth: { newValue: ~~(Math.random() * 200 + 100), - validate: () => - ~~aMonitor._view._detailsPane.getAttribute("width"), - modifyFrontend: (aValue) => - aMonitor._view._detailsPane.setAttribute("width", aValue) + validate: ($) => ~~$("#details-pane").getAttribute("width"), + modifyFrontend: ($, aValue) => $("#details-pane").setAttribute("width", aValue) + }, + networkDetailsHeight: { + newValue: ~~(Math.random() * 300 + 100), + validate: ($) => ~~$("#details-pane").getAttribute("height"), + modifyFrontend: ($, aValue) => $("#details-pane").setAttribute("height", aValue) }, /* add more prefs here... */ }; @@ -39,7 +46,7 @@ function test() { is(currentValue, firstValue, "Pref " + name + " should be equal to first value: " + firstValue); - is(currentValue, validate(), + is(currentValue, validate(aMonitor.panelWin.$), "Pref " + name + " should validate: " + currentValue); } } @@ -54,14 +61,14 @@ function test() { let validate = prefsToCheck[name].validate; let modifyFrontend = prefsToCheck[name].modifyFrontend; - modifyFrontend(newValue); + modifyFrontend(aMonitor.panelWin.$, newValue); info("Modified UI element affecting " + name + " to: " + newValue); is(currentValue, firstValue, "Pref " + name + " should still be equal to first value: " + firstValue); isnot(currentValue, newValue, "Pref " + name + " should't yet be equal to second value: " + newValue); - is(newValue, validate(), + is(newValue, validate(aMonitor.panelWin.$), "The UI element affecting " + name + " should validate: " + newValue); } } @@ -74,13 +81,12 @@ function test() { let firstValue = prefsToCheck[name].firstValue; let newValue = prefsToCheck[name].newValue; let validate = prefsToCheck[name].validate; - let modifyFrontend = prefsToCheck[name].modifyFrontend; isnot(currentValue, firstValue, "Pref " + name + " should't be equal to first value: " + firstValue); is(currentValue, newValue, "Pref " + name + " should now be equal to second value: " + newValue); - is(newValue, validate(), + is(newValue, validate(aMonitor.panelWin.$), "The UI element affecting " + name + " should validate: " + newValue); } } @@ -95,36 +101,117 @@ function test() { let validate = prefsToCheck[name].validate; let modifyFrontend = prefsToCheck[name].modifyFrontend; - modifyFrontend(firstValue); + modifyFrontend(aMonitor.panelWin.$, firstValue); info("Modified UI element affecting " + name + " to: " + firstValue); isnot(currentValue, firstValue, "Pref " + name + " should't yet be equal to first value: " + firstValue); is(currentValue, newValue, "Pref " + name + " should still be equal to second value: " + newValue); - is(firstValue, validate(), + is(firstValue, validate(aMonitor.panelWin.$), "The UI element affecting " + name + " should validate: " + firstValue); } } - storeFirstPrefValues(); + function testBottom() { + info("Testing prefs reload for a bottom host."); + storeFirstPrefValues(); - // Validate and modify. - validateFirstPrefValues(); - modifyFrontend(); - restartNetMonitor(aMonitor).then(([,, aNewMonitor]) => { - aMonitor = aNewMonitor; + // Validate and modify while toolbox is on the bottom. + validateFirstPrefValues(); + modifyFrontend(); - // Revalidate and reset. - validateNewPrefValues(); - resetFrontend(); - restartNetMonitor(aMonitor).then(([,, aNewMonitor]) => { - aMonitor = aNewMonitor; + return restartNetMonitor(aMonitor) + .then(([,, aNewMonitor]) => { + aMonitor = aNewMonitor; - // Revalidate and finish. - validateFirstPrefValues(); - teardown(aMonitor).then(finish); - }); - }); + // Revalidate and reset frontend while toolbox is on the bottom. + validateNewPrefValues(); + resetFrontend(); + + return restartNetMonitor(aMonitor); + }) + .then(([,, aNewMonitor]) => { + aMonitor = aNewMonitor; + + // Revalidate. + validateFirstPrefValues(); + }); + } + + function testSide() { + info("Moving toolbox to the side..."); + + return aMonitor._toolbox.switchHost(Toolbox.HostType.SIDE) + .then(() => { + info("Testing prefs reload for a side host."); + storeFirstPrefValues(); + + // Validate and modify frontend while toolbox is on the side. + validateFirstPrefValues(); + modifyFrontend(); + + return restartNetMonitor(aMonitor); + }) + .then(([,, aNewMonitor]) => { + aMonitor = aNewMonitor; + + // Revalidate and reset frontend while toolbox is on the side. + validateNewPrefValues(); + resetFrontend(); + + return restartNetMonitor(aMonitor); + }) + .then(([,, aNewMonitor]) => { + aMonitor = aNewMonitor; + + // Revalidate. + validateFirstPrefValues(); + }); + } + + function testWindow() { + info("Moving toolbox into a window..."); + + return aMonitor._toolbox.switchHost(Toolbox.HostType.WINDOW) + .then(() => { + info("Testing prefs reload for a window host."); + storeFirstPrefValues(); + + // Validate and modify frontend while toolbox is in a window. + validateFirstPrefValues(); + modifyFrontend(); + + return restartNetMonitor(aMonitor); + }) + .then(([,, aNewMonitor]) => { + aMonitor = aNewMonitor; + + // Revalidate and reset frontend while toolbox is in a window. + validateNewPrefValues(); + resetFrontend(); + + return restartNetMonitor(aMonitor); + }) + .then(([,, aNewMonitor]) => { + aMonitor = aNewMonitor; + + // Revalidate. + validateFirstPrefValues(); + }); + } + + function cleanupAndFinish() { + info("Moving toolbox back to the bottom..."); + + aMonitor._toolbox.switchHost(Toolbox.HostType.BOTTOM) + .then(() => teardown(aMonitor)) + .then(finish); + } + + testBottom() + .then(testSide) + .then(testWindow) + .then(cleanupAndFinish); }); } diff --git a/browser/devtools/netmonitor/test/browser_net_simple-request-details.js b/browser/devtools/netmonitor/test/browser_net_simple-request-details.js index d3b651fb1b37..b11bc6abe66e 100644 --- a/browser/devtools/netmonitor/test/browser_net_simple-request-details.js +++ b/browser/devtools/netmonitor/test/browser_net_simple-request-details.js @@ -73,7 +73,7 @@ function test() { is(responseScope.querySelector(".name").getAttribute("value"), L10N.getStr("responseHeaders") + " (" + - L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(0.168, 3)) + ")", + L10N.getFormatStr("networkMenu.sizeKB", L10N.numberWithDecimals(173/1024, 3)) + ")", "The response headers scope doesn't have the correct title."); ok(requestScope.querySelector(".name").getAttribute("value").contains( diff --git a/browser/devtools/netmonitor/test/head.js b/browser/devtools/netmonitor/test/head.js index b52477d0a14b..202f5c023a82 100644 --- a/browser/devtools/netmonitor/test/head.js +++ b/browser/devtools/netmonitor/test/head.js @@ -7,6 +7,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components; let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); let { Promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}); let { TargetFactory } = Cu.import("resource:///modules/devtools/Target.jsm", {}); +let { Toolbox } = Cu.import("resource:///modules/devtools/Toolbox.jsm", {}); let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); const EXAMPLE_URL = "http://example.com/browser/browser/devtools/netmonitor/test/"; diff --git a/browser/devtools/shared/DeveloperToolbar.jsm b/browser/devtools/shared/DeveloperToolbar.jsm index 55accdf4be37..4f88380519ac 100644 --- a/browser/devtools/shared/DeveloperToolbar.jsm +++ b/browser/devtools/shared/DeveloperToolbar.jsm @@ -506,7 +506,7 @@ DeveloperToolbar.prototype.destroy = function DT_destroy() let tabbrowser = this._chromeWindow.getBrowser(); tabbrowser.tabContainer.removeEventListener("TabSelect", this, false); tabbrowser.tabContainer.removeEventListener("TabClose", this, false); - tabbrowser.removeEventListener("load", this, true); + tabbrowser.removeEventListener("load", this, true); tabbrowser.removeEventListener("beforeunload", this, true); Array.prototype.forEach.call(tabbrowser.tabs, this._stopErrorsCount, this); diff --git a/browser/devtools/shared/widgets/VariablesView.jsm b/browser/devtools/shared/widgets/VariablesView.jsm index 43f3267234a5..2fb6689d2aee 100644 --- a/browser/devtools/shared/widgets/VariablesView.jsm +++ b/browser/devtools/shared/widgets/VariablesView.jsm @@ -52,6 +52,7 @@ const STR = Services.strings.createBundle(DBG_STRINGS_URI); */ this.VariablesView = function VariablesView(aParentNode, aFlags = {}) { this._store = new Map(); + this._items = []; this._itemsByElement = new WeakMap(); this._prevHierarchy = new Map(); this._currHierarchy = new Map(); @@ -103,6 +104,7 @@ VariablesView.prototype = { let scope = new Scope(this, aName); this._store.set(scope.id, scope); + this._items.push(scope); this._currHierarchy.set(aName, scope); this._itemsByElement.set(scope._target, scope); scope.header = !!aName; @@ -135,6 +137,7 @@ VariablesView.prototype = { } this._store.clear(); + this._items.length = 0; this._itemsByElement.clear(); this._appendEmptyNotice(); @@ -161,6 +164,7 @@ VariablesView.prototype = { let currList = this._list = this.document.createElement("scrollbox"); this._store.clear(); + this._items.length = 0; this._itemsByElement.clear(); this._emptyTimeout = this.window.setTimeout(function() { @@ -529,64 +533,73 @@ VariablesView.prototype = { }, /** - * Focuses the first visible variable or property in this container. + * Find the first item in the tree of visible items in this container that + * matches the predicate. Searches in visual order (the order seen by the + * user). Descends into each scope to check the scope and its children. + * + * @param function aPredicate + * A function that returns true when a match is found. + * @return Scope | Variable | Property + * The first visible scope, variable or property, or null if nothing + * is found. */ - focusFirstVisibleNode: function VV_focusFirstVisibleNode() { - let property, variable, scope; - - for (let [, item] of this._currHierarchy) { - if (!item.focusable) { - continue; - } - if (item instanceof Property) { - property = item; - break; - } else if (item instanceof Variable) { - variable = item; - break; - } else if (item instanceof Scope) { - scope = item; - break; + _findInVisibleItems: function VV__findInVisibleItems(aPredicate) { + for (let scope of this._items) { + let result = scope._findInVisibleItems(aPredicate); + if (result) { + return result; } } - if (scope) { - this._focusItem(scope); - } else if (variable) { - this._focusItem(variable); - } else if (property) { - this._focusItem(property); + return null; + }, + + /** + * Find the last item in the tree of visible items in this container that + * matches the predicate. Searches in reverse visual order (opposite of the + * order seen by the user). Descends into each scope to check the scope and + * its children. + * + * @param function aPredicate + * A function that returns true when a match is found. + * @return Scope | Variable | Property + * The last visible scope, variable or property, or null if nothing + * is found. + */ + _findInVisibleItemsReverse: function VV__findInVisibleItemsReverse(aPredicate) { + for (let i = this._items.length - 1; i >= 0; i--) { + let scope = this._items[i]; + let result = scope._findInVisibleItemsReverse(aPredicate); + if (result) { + return result; + } + } + return null; + }, + + /** + * Focuses the first visible scope, variable, or property in this container. + */ + focusFirstVisibleNode: function VV_focusFirstVisibleNode() { + let focusableItem = this._findInVisibleItems(item => item.focusable); + + if (focusableItem) { + this._focusItem(focusableItem); } this._parent.scrollTop = 0; this._parent.scrollLeft = 0; }, /** - * Focuses the last visible variable or property in this container. + * Focuses the last visible scope, variable, or property in this container. */ focusLastVisibleNode: function VV_focusLastVisibleNode() { - let property, variable, scope; + let focusableItem = this._findInVisibleItemsReverse(item => item.focusable); - for (let [, item] of this._currHierarchy) { - if (!item.focusable) { - continue; - } - if (item instanceof Property) { - property = item; - } else if (item instanceof Variable) { - variable = item; - } else if (item instanceof Scope) { - scope = item; - } - } - if (property && (!variable || property.isDescendantOf(variable))) { - this._focusItem(property); - } else if (variable && (!scope || variable.isDescendantOf(scope))) { - this._focusItem(variable); - } else if (scope) { - this._focusItem(scope); - this._parent.scrollTop = this._parent.scrollHeight; - this._parent.scrollLeft = 0; + if (focusableItem) { + this._focusItem(focusableItem); } + this._parent.scrollTop = this._parent.scrollHeight; + this._parent.scrollLeft = 0; }, /** @@ -888,6 +901,7 @@ VariablesView.prototype = { _window: null, _store: null, + _items: null, _prevHierarchy: null, _currHierarchy: null, _enumVisible: true, @@ -1082,6 +1096,8 @@ function Scope(aView, aName, aFlags = {}) { this.separatorStr = aView.separatorStr; this._store = new Map(); + this._enumItems = []; + this._nonEnumItems = []; this._init(aName.trim(), aFlags); } @@ -1785,6 +1801,89 @@ Scope.prototype = { return null; }, + /** + * Find the first item in the tree of visible items in this item that matches + * the predicate. Searches in visual order (the order seen by the user). + * Tests itself, then descends into first the enumerable children and then + * the non-enumerable children (since they are presented in separate groups). + * + * @param function aPredicate + * A function that returns true when a match is found. + * @return Scope | Variable | Property + * The first visible scope, variable or property, or null if nothing + * is found. + */ + _findInVisibleItems: function S__findInVisibleItems(aPredicate) { + if (aPredicate(this)) { + return this; + } + + if (this._isExpanded) { + if (this._variablesView._enumVisible) { + for (let item of this._enumItems) { + let result = item._findInVisibleItems(aPredicate); + if (result) { + return result; + } + } + } + + if (this._variablesView._nonEnumVisible) { + for (let item of this._nonEnumItems) { + let result = item._findInVisibleItems(aPredicate); + if (result) { + return result; + } + } + } + } + + return null; + }, + + /** + * Find the last item in the tree of visible items in this item that matches + * the predicate. Searches in reverse visual order (opposite of the order + * seen by the user). Descends into first the non-enumerable children, then + * the enumerable children (since they are presented in separate groups), and + * finally tests itself. + * + * @param function aPredicate + * A function that returns true when a match is found. + * @return Scope | Variable | Property + * The last visible scope, variable or property, or null if nothing + * is found. + */ + _findInVisibleItemsReverse: function S__findInVisibleItemsReverse(aPredicate) { + if (this._isExpanded) { + if (this._variablesView._nonEnumVisible) { + for (let i = this._nonEnumItems.length - 1; i >= 0; i--) { + let item = this._nonEnumItems[i]; + let result = item._findInVisibleItemsReverse(aPredicate); + if (result) { + return result; + } + } + } + + if (this._variablesView._enumVisible) { + for (let i = this._enumItems.length - 1; i >= 0; i--) { + let item = this._enumItems[i]; + let result = item._findInVisibleItemsReverse(aPredicate); + if (result) { + return result; + } + } + } + } + + if (aPredicate(this)) { + return this; + } + + return null; + }, + /** * Gets top level variables view instance. * @return VariablesView @@ -1854,7 +1953,9 @@ Scope.prototype = { _name: null, _title: null, _enum: null, + _enumItems: null, _nonenum: null, + _nonEnumItems: null, _throbber: null }; @@ -2167,8 +2268,10 @@ ViewHelpers.create({ constructor: Variable, proto: Scope.prototype }, { this._nameString == "this" || this._nameString == "") { this.ownerView._lazyAppend(aImmediateFlag, true, this._target); + this.ownerView._enumItems.push(this); } else { this.ownerView._lazyAppend(aImmediateFlag, false, this._target); + this.ownerView._nonEnumItems.push(this); } }, @@ -2674,8 +2777,10 @@ ViewHelpers.create({ constructor: Property, proto: Variable.prototype }, { _onInit: function P__onInit(aImmediateFlag) { if (this._initialDescriptor.enumerable) { this.ownerView._lazyAppend(aImmediateFlag, true, this._target); + this.ownerView._enumItems.push(this); } else { this.ownerView._lazyAppend(aImmediateFlag, false, this._target); + this.ownerView._nonEnumItems.push(this); } } }); diff --git a/browser/devtools/webconsole/HUDService.jsm b/browser/devtools/webconsole/HUDService.jsm index f85f90d0679a..cb3bd8fb6518 100644 --- a/browser/devtools/webconsole/HUDService.jsm +++ b/browser/devtools/webconsole/HUDService.jsm @@ -663,9 +663,19 @@ var HeadsUpDisplayUICommands = { let client = new DebuggerClient(DebuggerServer.connectPipe()); client.connect(() => - client.listTabs((aResponse) => - deferred.resolve({ form: aResponse, client: client }) - )); + client.listTabs((aResponse) => { + // Add Global Process debugging... + let globals = JSON.parse(JSON.stringify(aResponse)); + delete globals.tabs; + delete globals.selected; + // ...only if there are appropriate actors (a 'from' property will + // always be there). + if (Object.keys(globals).length > 1) { + deferred.resolve({ form: globals, client: client, chrome: true }); + } else { + deferred.reject("Global console not found!"); + } + })); return deferred.promise; } diff --git a/browser/devtools/webconsole/test/browser_webconsole_bug_821877_csp_errors.js b/browser/devtools/webconsole/test/browser_webconsole_bug_821877_csp_errors.js index 5a1001d78b31..dae8f4d8b42c 100644 --- a/browser/devtools/webconsole/test/browser_webconsole_bug_821877_csp_errors.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_821877_csp_errors.js @@ -12,14 +12,17 @@ function test() browser.addEventListener("load", function onLoad(aEvent) { browser.removeEventListener(aEvent.type, onLoad, true); openConsole(null, function testCSPErrorLogged (hud) { - waitForSuccess({ - name: "CSP error displayed successfully", - validatorFn: function () { - return hud.outputNode.textContent.indexOf(CSP_DEPRECATED_HEADER_MSG) > -1; - }, - successFn: finishTest, - failureFn: finishTest, - }); + waitForMessages({ + webconsole: hud, + messages: [ + { + name: "Deprecated CSP header error displayed successfully", + text: CSP_DEPRECATED_HEADER_MSG, + category: CATEGORY_SECURITY, + severity: SEVERITY_WARNING + }, + ], + }).then(finishTest); }); }, true); } diff --git a/browser/devtools/webconsole/webconsole.js b/browser/devtools/webconsole/webconsole.js index ff9dcb77fd44..dd43473682e3 100644 --- a/browser/devtools/webconsole/webconsole.js +++ b/browser/devtools/webconsole/webconsole.js @@ -4278,6 +4278,7 @@ var Utils = { return CATEGORY_CSS; case "Mixed Content Blocker": + case "CSP": return CATEGORY_SECURITY; default: diff --git a/browser/devtools/webconsole/webconsole.xul b/browser/devtools/webconsole/webconsole.xul index d430e77ab8aa..911cbe854bfb 100644 --- a/browser/devtools/webconsole/webconsole.xul +++ b/browser/devtools/webconsole/webconsole.xul @@ -62,8 +62,8 @@ - - + + diff --git a/browser/metro/base/content/Util.js b/browser/metro/base/content/Util.js index eef44e22d19c..b1491e939cea 100644 --- a/browser/metro/base/content/Util.js +++ b/browser/metro/base/content/Util.js @@ -315,6 +315,30 @@ let Util = { * Screen and layout utilities */ + /* + * translateToTopLevelWindow - Given an element potentially within + * a subframe, calculate the offsets up to the top level browser. + */ + translateToTopLevelWindow: function translateToTopLevelWindow(aElement) { + let offsetX = 0; + let offsetY = 0; + let element = aElement; + while (element && + element.ownerDocument && + element.ownerDocument.defaultView != content) { + element = element.ownerDocument.defaultView.frameElement; + let rect = element.getBoundingClientRect(); + offsetX += rect.left; + offsetY += rect.top; + } + let win = null; + if (element == aElement) + win = content; + else + win = element.contentDocument.defaultView; + return { targetWindow: win, offsetX: offsetX, offsetY: offsetY }; + }, + get displayDPI() { delete this.displayDPI; return this.displayDPI = this.getWindowUtils(window).displayDPI; diff --git a/browser/metro/base/content/bindings/grid.xml b/browser/metro/base/content/bindings/grid.xml index 252c5af09051..ee6b7e34060c 100644 --- a/browser/metro/base/content/bindings/grid.xml +++ b/browser/metro/base/content/bindings/grid.xml @@ -412,7 +412,7 @@ if (this.controller && this.controller.gridBoundCallback != undefined) this.controller.gridBoundCallback(); // set up cross-slide gesture handling for multiple-selection grids - if (CrossSlide && "multiple" == this.getAttribute("seltype")) { + if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) { this._xslideHandler = new CrossSlide.Handler(this, { REARRANGESTART: this.crossSlideBoundary }); diff --git a/browser/metro/base/content/contenthandlers/Content.js b/browser/metro/base/content/contenthandlers/Content.js index da6e0801026c..e7948ba921c2 100644 --- a/browser/metro/base/content/contenthandlers/Content.js +++ b/browser/metro/base/content/contenthandlers/Content.js @@ -46,161 +46,6 @@ const kReferenceDpi = 240; // standard "pixel" size used in some preferences const kStateActive = 0x00000001; // :active pseudoclass for elements -/* - * ElementTouchHelper - * - * Assists users by watching for mouse clicks in content and redirect - * them to the best found target. - */ -const ElementTouchHelper = { - get radius() { - let prefs = Services.prefs; - delete this.radius; - return this.radius = { "top": prefs.getIntPref("ui.touch.radius.topmm"), - "right": prefs.getIntPref("ui.touch.radius.rightmm"), - "bottom": prefs.getIntPref("ui.touch.radius.bottommm"), - "left": prefs.getIntPref("ui.touch.radius.leftmm") - }; - }, - - get weight() { - delete this.weight; - return this.weight = { "visited": Services.prefs.getIntPref("ui.touch.radius.visitedWeight") - }; - }, - - /* Retrieve the closest element to a point by looking at borders position */ - getClosest: function getClosest(aWindowUtils, aX, aY) { - if (!this.dpiRatio) - this.dpiRatio = aWindowUtils.displayDPI / kReferenceDpi; - - let dpiRatio = this.dpiRatio; - - let target = aWindowUtils.elementFromPoint(aX, aY, - true, /* ignore root scroll frame*/ - false); /* don't flush layout */ - - // return early if the click is just over a clickable element - if (this._isElementClickable(target)) - return target; - - let nodes = aWindowUtils.nodesFromRect(aX, aY, this.radius.top * dpiRatio, - this.radius.right * dpiRatio, - this.radius.bottom * dpiRatio, - this.radius.left * dpiRatio, true, false); - - let threshold = Number.POSITIVE_INFINITY; - for (let i = 0; i < nodes.length; i++) { - let current = nodes[i]; - if (!current.mozMatchesSelector || !this._isElementClickable(current)) - continue; - - let rect = current.getBoundingClientRect(); - let distance = this._computeDistanceFromRect(aX, aY, rect); - - // increase a little bit the weight for already visited items - if (current && current.mozMatchesSelector("*:visited")) - distance *= (this.weight.visited / 100); - - if (distance < threshold) { - target = current; - threshold = distance; - } - } - - return target; - }, - - _isElementClickable: function _isElementClickable(aElement) { - const selector = "a,:link,:visited,[role=button],button,input,select,textarea,label"; - for (let elem = aElement; elem; elem = elem.parentNode) { - if (this._hasMouseListener(elem)) - return true; - if (elem.mozMatchesSelector && elem.mozMatchesSelector(selector)) - return true; - } - return false; - }, - - _computeDistanceFromRect: function _computeDistanceFromRect(aX, aY, aRect) { - let x = 0, y = 0; - let xmost = aRect.left + aRect.width; - let ymost = aRect.top + aRect.height; - - // compute horizontal distance from left/right border depending if X is - // before/inside/after the element's rectangle - if (aRect.left < aX && aX < xmost) - x = Math.min(xmost - aX, aX - aRect.left); - else if (aX < aRect.left) - x = aRect.left - aX; - else if (aX > xmost) - x = aX - xmost; - - // compute vertical distance from top/bottom border depending if Y is - // above/inside/below the element's rectangle - if (aRect.top < aY && aY < ymost) - y = Math.min(ymost - aY, aY - aRect.top); - else if (aY < aRect.top) - y = aRect.top - aY; - if (aY > ymost) - y = aY - ymost; - - return Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2)); - }, - - _els: Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci.nsIEventListenerService), - _clickableEvents: ["mousedown", "mouseup", "click"], - _hasMouseListener: function _hasMouseListener(aElement) { - let els = this._els; - let listeners = els.getListenerInfoFor(aElement, {}); - for (let i = 0; i < listeners.length; i++) { - if (this._clickableEvents.indexOf(listeners[i].type) != -1) - return true; - } - return false; - } -}; - - -/* - * Global functions - */ - -/* - * elementFromPoint - find the closes element at a point. searches - * sub-frames. - * - * @param aX, aY browser coordinates - * @return - * element - element at the position, or null if no active browser or - * element was found. - * frameX - x position within the subframe element was found. aX if no - * sub-frame was found. - * frameY - y position within the subframe element was found. aY if no - * sub-frame was found. - */ -function elementFromPoint(aX, aY) { - // browser's elementFromPoint expect browser-relative client coordinates. - // subtract browser's scroll values to adjust - let cwu = Util.getWindowUtils(content); - let elem = ElementTouchHelper.getClosest(cwu, aX, aY); - - // step through layers of IFRAMEs and FRAMES to find innermost element - while (elem && (elem instanceof HTMLIFrameElement || - elem instanceof HTMLFrameElement)) { - // adjust client coordinates' origin to be top left of iframe viewport - let rect = elem.getBoundingClientRect(); - aX -= rect.left; - aY -= rect.top; - let windowUtils = elem.contentDocument - .defaultView - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - elem = ElementTouchHelper.getClosest(windowUtils, aX, aY); - } - return { element: elem, frameX: aX, frameY: aY }; -} - /* * getBoundingContentRect * @@ -349,8 +194,7 @@ let Content = { break; case "touchstart": - let touch = aEvent.changedTouches[0]; - this._onTouchStart(touch.clientX, touch.clientY); + this._onTouchStart(aEvent); break; } }, @@ -403,10 +247,8 @@ let Content = { * Event handlers */ - _onTouchStart: function _onTouchStart(x, y) { - let { element } = elementFromPoint(x, y); - if (!element) - return; + _onTouchStart: function _onTouchStart(aEvent) { + let element = aEvent.target; // There is no need to have a feedback for disabled element let isDisabled = element instanceof HTMLOptionElement ? @@ -419,11 +261,9 @@ let Content = { }, _onClickCapture: function _onClickCapture(aEvent) { - ContextMenuHandler.reset(); + let element = aEvent.target; - let { element: element } = elementFromPoint(aEvent.clientX, aEvent.clientY); - if (!element) - return; + ContextMenuHandler.reset(); // Only show autocomplete after the item is clicked if (!this.lastClickElement || this.lastClickElement != element) { @@ -442,9 +282,10 @@ let Content = { // A tap on a form input triggers touch input caret selection if (Util.isTextInput(element) && aEvent.mozInputSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) { + let { offsetX, offsetY } = Util.translateToTopLevelWindow(element); sendAsyncMessage("Content:SelectionCaret", { - xPos: aEvent.clientX, - yPos: aEvent.clientY + xPos: aEvent.clientX + offsetX, + yPos: aEvent.clientY + offsetY }); } }, diff --git a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js index 530d4fdb77ba..2b3637f03df9 100644 --- a/browser/metro/base/content/contenthandlers/ContextMenuHandler.js +++ b/browser/metro/base/content/contenthandlers/ContextMenuHandler.js @@ -159,31 +159,6 @@ var ContextMenuHandler = { * Utility routines */ - /* - * _translateToTopLevelWindow - Given a potential coordinate set within - * a subframe, translate up to the parent window which is what front - * end code expect. - */ - _translateToTopLevelWindow: function _translateToTopLevelWindow(aPopupNode) { - let offsetX = 0; - let offsetY = 0; - let element = aPopupNode; - while (element && - element.ownerDocument && - element.ownerDocument.defaultView != content) { - element = element.ownerDocument.defaultView.frameElement; - let rect = element.getBoundingClientRect(); - offsetX += rect.left; - offsetY += rect.top; - } - let win = null; - if (element == aPopupNode) - win = content; - else - win = element.contentDocument.defaultView; - return { targetWindow: win, offsetX: offsetX, offsetY: offsetY }; - }, - /* * _processPopupNode - Generate and send a Content:ContextMenu message * to browser detailing the underlying content types at this.popupNode. @@ -197,7 +172,7 @@ var ContextMenuHandler = { let { targetWindow: targetWindow, offsetX: offsetX, offsetY: offsetY } = - this._translateToTopLevelWindow(aPopupNode); + Util.translateToTopLevelWindow(aPopupNode); let popupNode = this.popupNode = aPopupNode; let imageUrl = ""; diff --git a/browser/metro/base/content/contenthandlers/SelectionHandler.js b/browser/metro/base/content/contenthandlers/SelectionHandler.js index c8d89ee56d79..0f64c19e5947 100644 --- a/browser/metro/base/content/contenthandlers/SelectionHandler.js +++ b/browser/metro/base/content/contenthandlers/SelectionHandler.js @@ -574,7 +574,8 @@ var SelectionHandler = { _restrictSelectionRectToEditBounds: function _restrictSelectionRectToEditBounds() { if (!this._targetIsEditable) return; - let bounds = this._getTargetClientRect(); + + let bounds = this._getTargetBrowserRect(); if (this._cache.start.xPos < bounds.left) this._cache.start.xPos = bounds.left; if (this._cache.end.xPos < bounds.left) @@ -794,7 +795,7 @@ var SelectionHandler = { * @return new constrained point struct */ _constrainPointWithinControl: function _cpwc(aPoint, aHalfLineHeight) { - let bounds = this._getTargetClientRect(); + let bounds = this._getTargetBrowserRect(); let point = { xPos: aPoint.xPos, yPos: aPoint.yPos }; if (point.xPos <= bounds.left) point.xPos = bounds.left + 2; @@ -815,7 +816,7 @@ var SelectionHandler = { * Works on client coordinates. */ _pointOrientationToRect: function _pointOrientationToRect(aPoint) { - let bounds = this._targetElement.getBoundingClientRect(); + let bounds = this._getTargetBrowserRect(); let result = { left: 0, right: 0, top: 0, bottom: 0 }; if (aPoint.xPos <= bounds.left) result.left = bounds.left - aPoint.xPos; @@ -1103,7 +1104,7 @@ var SelectionHandler = { // height of the target element let targetHeight = this._cache.element.bottom - this._cache.element.top; // height of the browser view. - let viewBottom = this._targetElement.ownerDocument.defaultView.innerHeight; + let viewBottom = content.innerHeight; // If the target is shorter than the new content height, we can go ahead // and center it. @@ -1294,10 +1295,27 @@ var SelectionHandler = { return seldata; }, + /* + * Returns bounds of the element relative to the inner sub frame it sits + * in. + */ _getTargetClientRect: function _getTargetClientRect() { return this._targetElement.getBoundingClientRect(); }, + /* + * Returns bounds of the element relative to the top level browser. + */ + _getTargetBrowserRect: function _getTargetBrowserRect() { + let client = this._getTargetClientRect(); + return { + left: client.left + this._contentOffset.x, + top: client.top + this._contentOffset.y, + right: client.right + this._contentOffset.x, + bottom: client.bottom + this._contentOffset.y + }; + }, + /* * Translate a top level client point to frame relative client point. */ diff --git a/browser/metro/base/tests/mochitest/Makefile.in b/browser/metro/base/tests/mochitest/Makefile.in index d4042de6ccbd..92b7811c36e6 100644 --- a/browser/metro/base/tests/mochitest/Makefile.in +++ b/browser/metro/base/tests/mochitest/Makefile.in @@ -21,19 +21,20 @@ BROWSER_TESTS = \ browser_onscreen_keyboard.html \ browser_remotetabs.js \ browser_downloads.js \ - browser_plugin_input.html \ - browser_plugin_input_mouse.js \ - browser_plugin_input_keyboard.js \ browser_context_menu_tests.js \ browser_context_menu_tests_01.html \ browser_context_menu_tests_02.html \ browser_context_menu_tests_03.html \ - text-block.html \ browser_prefs_ui.js \ browser_topsites.js \ browser_tabs.js \ $(NULL) +# disabled due to timeouts and lack of plugin support. +# browser_plugin_input.html \ +# browser_plugin_input_mouse.js \ +# browser_plugin_input_keyboard.js \ + ifndef MOZ_DEBUG BROWSER_TESTS += \ browser_selection_basic.js \ @@ -42,11 +43,19 @@ BROWSER_TESTS += \ browser_selection_textarea.html \ browser_selection_frame_content.js \ browser_selection_frame_content.html \ + browser_selection_inputs.js \ + browser_selection_inputs.html \ + browser_selection_frame_textarea.js \ + browser_selection_frame_textarea.html \ + browser_selection_frame_inputs.js \ + browser_selection_frame_inputs.html \ $(NULL) endif BROWSER_TEST_RESOURCES = \ res/image01.png \ + res/textblock01.html \ + res/textinput01.html \ $(NULL) libs:: $(BROWSER_TESTS) diff --git a/browser/metro/base/tests/mochitest/browser_context_menu_tests_03.html b/browser/metro/base/tests/mochitest/browser_context_menu_tests_03.html index b45bb5c23b6f..32e90de484bd 100644 --- a/browser/metro/base/tests/mochitest/browser_context_menu_tests_03.html +++ b/browser/metro/base/tests/mochitest/browser_context_menu_tests_03.html @@ -9,7 +9,7 @@ Hello there. hello there.
- +

diff --git a/browser/metro/base/tests/mochitest/browser_selection_basic.js b/browser/metro/base/tests/mochitest/browser_selection_basic.js index f1c510a2cbf5..bd81b820ad51 100644 --- a/browser/metro/base/tests/mochitest/browser_selection_basic.js +++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js @@ -25,7 +25,6 @@ function setUpAndTearDown() { yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - yield hideContextUI(); } gTests.push({ @@ -40,6 +39,8 @@ gTests.push({ return !StartUI.isStartPageVisible; }, 10000, 100); + yield hideContextUI(); + gWindow = Browser.selectedTab.browser.contentWindow; InputSourceHelper.isPrecise = false; }, diff --git a/browser/metro/base/tests/mochitest/browser_selection_frame_content.html b/browser/metro/base/tests/mochitest/browser_selection_frame_content.html index 636d8350f021..9484ff5e1585 100644 --- a/browser/metro/base/tests/mochitest/browser_selection_frame_content.html +++ b/browser/metro/base/tests/mochitest/browser_selection_frame_content.html @@ -4,7 +4,7 @@
- +

Hello there. Hi! diff --git a/browser/metro/base/tests/mochitest/browser_selection_frame_content.js b/browser/metro/base/tests/mochitest/browser_selection_frame_content.js index 52351ed71dbb..bcba64c11874 100644 --- a/browser/metro/base/tests/mochitest/browser_selection_frame_content.js +++ b/browser/metro/base/tests/mochitest/browser_selection_frame_content.js @@ -25,7 +25,6 @@ function setUpAndTearDown() { yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - yield hideContextUI(); } gTests.push({ @@ -40,6 +39,8 @@ gTests.push({ return !StartUI.isStartPageVisible; }, 10000, 100); + yield hideContextUI(); + gWindow = Browser.selectedTab.browser.contentWindow; gFrame = gWindow.document.getElementById("frame1"); @@ -212,11 +213,6 @@ gTests.push({ }); function test() { - if (isDebugBuild()) { - todo(false, "selection tests can't run in debug builds."); - return; - } - if (!isLandscapeMode()) { todo(false, "browser_selection_tests need landscape mode to run."); return; diff --git a/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.html b/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.html new file mode 100644 index 000000000000..1c0ad15496c9 --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.html @@ -0,0 +1,82 @@ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ +

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.js b/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.js new file mode 100644 index 000000000000..3f8ca464add2 --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_selection_frame_inputs.js @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let gWindow = null; +var gFrame = null; +var gInput = null; + +const kMarkerOffsetY = 12; +const kCommonWaitMs = 5000; +const kCommonPollMs = 100; + +/////////////////////////////////////////////////// +// form input tests +/////////////////////////////////////////////////// + +function setUpAndTearDown() { + emptyClipboard(); + if (gWindow) + clearSelection(gWindow); + if (gFrame) + clearSelection(gFrame); + if (gInput) + clearSelection(gInput); + yield waitForCondition(function () { + return !SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); +} + +gTests.push({ + desc: "normalize browser", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + info(chromeRoot + "browser_selection_frame_inputs.html"); + yield addTab(chromeRoot + "browser_selection_frame_inputs.html"); + + yield waitForCondition(function () { + return !StartUI.isStartPageVisible; + }, 10000, 100); + + yield hideContextUI(); + + gWindow = Browser.selectedTab.browser.contentWindow; + gFrame = gWindow.document.getElementById("frame1"); + gInput = gFrame.contentDocument.getElementById("textinput"); + + ok(gWindow != null, "gWindow"); + ok(gFrame != null, "gFrame"); + ok(gInput != null, "gInput"); + + InputSourceHelper.isPrecise = false; + }, +}); + +gTests.push({ + desc: "basic selection", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gInput.focus(); + gInput.selectionStart = gInput.selectionEnd = 0; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(232, 583); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + is(getTrimmedSelection(gInput).toString(), "straight", "selection test"); + + checkMonoclePositionRange("start", 210, 220, 600, 605); + checkMonoclePositionRange("end", 250, 260, 600, 605); + }, +}); + +gTests.push({ + desc: "drag selection", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gInput.focus(); + gInput.selectionStart = gInput.selectionEnd = 0; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(232, 583); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + is(getTrimmedSelection(gInput).toString(), "straight", "selection test"); + + // end marker to the right + let xpos = SelectionHelperUI.endMark.xPos; + let ypos = SelectionHelperUI.endMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, xpos + 350, ypos); + yield waitForCondition(function () { + return getTrimmedSelection(gInput).toString() == + "straight on like a tunnel for some way and then dipped suddenly down"; + }, 6000, 2000); + touchdrag.end(); + + yield waitForCondition(function () { + return !SelectionHelperUI.hasActiveDrag; + }, kCommonWaitMs, kCommonPollMs); + yield SelectionHelperUI.pingSelectionHandler(); + + // start marker to the left + let xpos = SelectionHelperUI.startMark.xPos; + let ypos = SelectionHelperUI.startMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, 10, ypos); + yield waitForCondition(function () { + return getTrimmedSelection(gInput).toString() == + "The rabbit-hole went straight on like a tunnel for some way and then dipped suddenly down"; + }, 6000, 2000); + touchdrag.end(); + + yield waitForCondition(function () { + return !SelectionHelperUI.hasActiveDrag; + }, kCommonWaitMs, kCommonPollMs); + yield SelectionHelperUI.pingSelectionHandler(); + }, +}); + +function test() { + if (!isLandscapeMode()) { + todo(false, "browser_selection_tests need landscape mode to run."); + return; + } + + requestLongerTimeout(3); + runTests(); +} diff --git a/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.html b/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.html new file mode 100644 index 000000000000..27e37b0e9ac3 --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.html @@ -0,0 +1,82 @@ + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

+ +

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + diff --git a/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.js b/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.js new file mode 100644 index 000000000000..b94dac8f5e7f --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_selection_frame_textarea.js @@ -0,0 +1,257 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let gWindow = null; +var gFrame = null; +var gTextArea = null; + +const kMarkerOffsetY = 12; +const kCommonWaitMs = 5000; +const kCommonPollMs = 100; + +/////////////////////////////////////////////////// +// form input tests +/////////////////////////////////////////////////// + +function setUpAndTearDown() { + emptyClipboard(); + if (gWindow) + clearSelection(gWindow); + if (gFrame) + clearSelection(gFrame); + if (gTextArea) + clearSelection(gTextArea); + yield waitForCondition(function () { + return !SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); +} + +gTests.push({ + desc: "normalize browser", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + info(chromeRoot + "browser_selection_frame_textarea.html"); + yield addTab(chromeRoot + "browser_selection_frame_textarea.html"); + + yield waitForCondition(function () { + return !StartUI.isStartPageVisible; + }, 10000, 100); + + yield hideContextUI(); + + gWindow = Browser.selectedTab.browser.contentWindow; + gFrame = gWindow.document.getElementById("frame1"); + gTextArea = gFrame.contentDocument.getElementById("textarea"); + ok(gWindow != null, "gWindow"); + ok(gFrame != null, "gFrame"); + ok(gTextArea != null, "gTextArea"); + + InputSourceHelper.isPrecise = false; + }, +}); + +gTests.push({ + desc: "basic selection", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gTextArea.focus(); + gTextArea.selectionStart = gTextArea.selectionEnd = 0; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(275, 663); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + is(getTrimmedSelection(gTextArea).toString(), "wondered", "selection test"); + + checkMonoclePositionRange("start", 260, 275, 675, 685); + checkMonoclePositionRange("end", 320, 335, 675, 685); + }, +}); + +gTests.push({ + desc: "drag selection", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gTextArea.focus(); + gTextArea.selectionStart = gTextArea.selectionEnd = 0; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(275, 663); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + is(getTrimmedSelection(gTextArea).toString(), "wondered", "selection test"); + + // end marker to the right + let xpos = SelectionHelperUI.endMark.xPos; + let ypos = SelectionHelperUI.endMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, xpos + 150, ypos); + yield waitForCondition(function () { + return getTrimmedSelection(gTextArea).toString() == + "wondered at this,"; + }, 6000, 2000); + touchdrag.end(); + + yield waitForCondition(function () { + return !SelectionHelperUI.hasActiveDrag; + }, kCommonWaitMs, kCommonPollMs); + yield SelectionHelperUI.pingSelectionHandler(); + + // start marker up and to the left + let xpos = SelectionHelperUI.startMark.xPos; + let ypos = SelectionHelperUI.startMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, 40, 500); + yield waitForCondition(function () { + return getTrimmedSelection(gTextArea).toString().substring(0, 17) == + "There was nothing"; + }, 6000, 2000); + touchdrag.end(); + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(250, 640); + yield promise; + + checkContextUIMenuItemVisibility(["context-cut", + "context-copy"]); + + let menuItem = document.getElementById("context-copy"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + let string = ""; + yield waitForCondition(function () { + string = SpecialPowers.getClipboardData("text/unicode"); + return string.substring(0, 17) === "There was nothing"; + }); + }, +}); + +gTests.push({ + desc: "drag selection", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gTextArea.focus(); + gTextArea.selectionStart = gTextArea.selectionEnd = 0; + + let scrollPromise = waitForEvent(gWindow, "scroll"); + gWindow.scrollBy(0, 200); + yield scrollPromise; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(275, 463); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + is(getTrimmedSelection(gTextArea).toString(), "wondered", "selection test"); + + // end marker to the right + let xpos = SelectionHelperUI.endMark.xPos; + let ypos = SelectionHelperUI.endMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, xpos + 150, ypos); + yield waitForCondition(function () { + return getTrimmedSelection(gTextArea).toString() == + "wondered at this,"; + }, 6000, 2000); + touchdrag.end(); + + yield waitForCondition(function () { + return !SelectionHelperUI.hasActiveDrag; + }, kCommonWaitMs, kCommonPollMs); + yield SelectionHelperUI.pingSelectionHandler(); + + // start marker up and to the left + let xpos = SelectionHelperUI.startMark.xPos; + let ypos = SelectionHelperUI.startMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, 40, 300); + yield waitForCondition(function () { + return getTrimmedSelection(gTextArea).toString().substring(0, 17) == + "There was nothing"; + }, 6000, 2000); + touchdrag.end(); + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(250, 440); + yield promise; + + checkContextUIMenuItemVisibility(["context-cut", + "context-copy"]); + + let menuItem = document.getElementById("context-copy"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + let string = ""; + yield waitForCondition(function () { + string = SpecialPowers.getClipboardData("text/unicode"); + return string.substring(0, 17) === "There was nothing"; + }); + }, +}); + +function test() { + if (!isLandscapeMode()) { + todo(false, "browser_selection_tests need landscape mode to run."); + return; + } + + requestLongerTimeout(3); + runTests(); +} diff --git a/browser/metro/base/tests/mochitest/browser_selection_inputs.html b/browser/metro/base/tests/mochitest/browser_selection_inputs.html new file mode 100644 index 000000000000..d182a291990f --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_selection_inputs.html @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/browser/metro/base/tests/mochitest/browser_selection_inputs.js b/browser/metro/base/tests/mochitest/browser_selection_inputs.js new file mode 100644 index 000000000000..8fb4b297fceb --- /dev/null +++ b/browser/metro/base/tests/mochitest/browser_selection_inputs.js @@ -0,0 +1,205 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +let gWindow = null; +var gInput = null; + +const kMarkerOffsetY = 12; +const kCommonWaitMs = 5000; +const kCommonPollMs = 100; + +/////////////////////////////////////////////////// +// form input tests +/////////////////////////////////////////////////// + +function setUpAndTearDown() { + emptyClipboard(); + if (gWindow) + clearSelection(gWindow); + if (gInput) + clearSelection(gInput); + yield waitForCondition(function () { + return !SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); +} + +/* + 5px top margin + 25px tall text input + 300px wide +*/ + +gTests.push({ + desc: "normalize browser", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + info(chromeRoot + "browser_selection_inputs.html"); + yield addTab(chromeRoot + "browser_selection_inputs.html"); + + yield waitForCondition(function () { + return !StartUI.isStartPageVisible; + }, 10000, 100); + + yield hideContextUI(); + + gWindow = Browser.selectedTab.browser.contentWindow; + gInput = gWindow.document.getElementById("a"); + InputSourceHelper.isPrecise = false; + }, +}); + +gTests.push({ + desc: "basic text input selection", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gInput.focus(); + gInput.selectionStart = gInput.selectionEnd = 0; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(200, 17); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + is(getTrimmedSelection(gInput).toString(), "went", "selection test"); + }, +}); + +gTests.push({ + desc: "drag left to scroll", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gInput.selectionStart = gInput.selectionEnd = gInput.value.length; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(190, 17); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + // check text selection + is(getTrimmedSelection(gInput).toString(), "way", "selection test"); + + // to the left + let xpos = SelectionHelperUI.startMark.xPos; + let ypos = SelectionHelperUI.startMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, 10, ypos); + yield waitForCondition(function () { + return getTrimmedSelection(gInput).toString() == + "The rabbit-hole went straight on like a tunnel for some way"; + }, 6000, 2000); + touchdrag.end(); + + yield waitForCondition(function () { + return !SelectionHelperUI.hasActiveDrag; + }, kCommonWaitMs, kCommonPollMs); + yield SelectionHelperUI.pingSelectionHandler(); + }, +}); + +gTests.push({ + desc: "drag right to scroll and bug 862025", + setUp: setUpAndTearDown, + tearDown: setUpAndTearDown, + run: function test() { + gInput.selectionStart = gInput.selectionEnd = 0; + + let promise = waitForEvent(document, "popupshown"); + sendContextMenuClick(230, 17); + yield promise; + + checkContextUIMenuItemVisibility(["context-select", + "context-select-all"]); + + let menuItem = document.getElementById("context-select"); + ok(menuItem, "menu item exists"); + ok(!menuItem.hidden, "menu item visible"); + let popupPromise = waitForEvent(document, "popuphidden"); + EventUtils.synthesizeMouse(menuItem, 10, 10, {}, gWindow); + yield popupPromise; + + yield waitForCondition(function () { + return SelectionHelperUI.isSelectionUIVisible; + }, kCommonWaitMs, kCommonPollMs); + + // check text selection + is(getTrimmedSelection(gInput).toString(), "straight", "selection test"); + + // to the right + let xpos = SelectionHelperUI.endMark.xPos; + let ypos = SelectionHelperUI.endMark.yPos + 10; + var touchdrag = new TouchDragAndHold(); + yield touchdrag.start(gWindow, xpos, ypos, 510, ypos); + yield waitForCondition(function () { + return getTrimmedSelection(gInput).toString() == + "straight on like a tunnel for some way and then dipped suddenly down"; + }, 6000, 2000); + touchdrag.end(); + + yield waitForCondition(function () { + return !SelectionHelperUI.hasActiveDrag; + }, kCommonWaitMs, kCommonPollMs); + yield SelectionHelperUI.pingSelectionHandler(); + + // down - selection shouldn't change + let xpos = SelectionHelperUI.endMark.xPos; + let ypos = SelectionHelperUI.endMark.yPos + 10; + yield touchdrag.start(gWindow, xpos, ypos, xpos, ypos + 150); + yield waitForMs(2000); + touchdrag.end(); + + is(getTrimmedSelection(gInput).toString(), "straight on like a tunnel for some way and then dipped suddenly down", "selection test"); + + // left and up - selection should shrink + let xpos = SelectionHelperUI.endMark.xPos; + let ypos = SelectionHelperUI.endMark.yPos + 10; + yield touchdrag.start(gWindow, xpos, ypos, 105, 25); + yield waitForCondition(function () { + return getTrimmedSelection(gInput).toString() == + "straight on like a tunnel for"; + }, 6000, 2000); + touchdrag.end(); + }, +}); + +function test() { + if (!isLandscapeMode()) { + todo(false, "browser_selection_tests need landscape mode to run."); + return; + } + + requestLongerTimeout(3); + runTests(); +} diff --git a/browser/metro/base/tests/mochitest/browser_selection_textarea.js b/browser/metro/base/tests/mochitest/browser_selection_textarea.js index 8c8d16f8fb6c..8d8b07537991 100644 --- a/browser/metro/base/tests/mochitest/browser_selection_textarea.js +++ b/browser/metro/base/tests/mochitest/browser_selection_textarea.js @@ -25,7 +25,6 @@ function setUpAndTearDown() { yield waitForCondition(function () { return !SelectionHelperUI.isSelectionUIVisible; }, kCommonWaitMs, kCommonPollMs); - yield hideContextUI(); } gTests.push({ @@ -40,6 +39,8 @@ gTests.push({ return !StartUI.isStartPageVisible; }, 10000, 100); + yield hideContextUI(); + gWindow = Browser.selectedTab.browser.contentWindow; InputSourceHelper.isPrecise = false; }, @@ -143,11 +144,6 @@ gTests.push({ }); function test() { - if (isDebugBuild()) { - todo(false, "selection tests can't run in debug builds."); - return; - } - if (!isLandscapeMode()) { todo(false, "browser_selection_tests need landscape mode to run."); return; diff --git a/browser/metro/base/tests/mochitest/head.js b/browser/metro/base/tests/mochitest/head.js index bda4c8c5d8f7..1a99b78d7fa7 100644 --- a/browser/metro/base/tests/mochitest/head.js +++ b/browser/metro/base/tests/mochitest/head.js @@ -55,6 +55,24 @@ function checkContextUIMenuItemVisibility(aVisibleList) is(errors, 0, "context menu item list visibility"); } +function checkMonoclePositionRange(aMonocle, aMinX, aMaxX, aMinY, aMaxY) +{ + let monocle = null; + if (aMonocle == "start") + monocle = SelectionHelperUI._startMark; + else if (aMonocle == "end") + monocle = SelectionHelperUI._endMark; + else if (aMonocle == "caret") + monocle = SelectionHelperUI._caretMark; + else + ok(false, "bad monocle id"); + + ok(monocle.xPos > aMinX && monocle.xPos < aMaxX, + "X position is " + monocle.xPos + ", expected between " + aMinX + " and " + aMaxX); + ok(monocle.yPos > aMinY && monocle.yPos < aMaxY, + "Y position is " + monocle.yPos + ", expected between " + aMinY + " and " + aMaxY); +} + /* * showNotification - displays a test notification with the current * browser and waits for the noticiation to be fully displayed. @@ -184,7 +202,7 @@ function addTab(aUrl) { return Task.spawn(function() { info("Opening "+aUrl+" in a new tab"); let tab = Browser.addTab(aUrl, true); - yield waitForEvent(tab.browser, "pageshow"); + yield tab.pageShowPromise; is(tab.browser.currentURI.spec, aUrl, aUrl + " is loaded"); registerCleanupFunction(function() Browser.closeTab(tab)); @@ -213,7 +231,6 @@ function addTab(aUrl) { * @returns a Promise that resolves to the received event, or to an Error */ function waitForEvent(aSubject, aEventName, aTimeoutMs) { - info("waitForEvent: on " + aSubject + " event: " + aEventName); let eventDeferred = Promise.defer(); let timeoutMs = aTimeoutMs || kDefaultWait; let timerID = setTimeout(function wfe_canceller() { @@ -548,8 +565,11 @@ TouchDragAndHold.prototype = { _timeoutStep: 2, _numSteps: 50, _debug: false, + _win: null, callback: function callback() { + if (this._win == null) + return; if (++this._step.steps >= this._numSteps) { EventUtils.synthesizeTouchAtPoint(this._endPoint.xPos, this._endPoint.yPos, { type: "touchmove" }, this._win); diff --git a/browser/metro/base/tests/mochitest/res/textarea01.html b/browser/metro/base/tests/mochitest/res/textarea01.html new file mode 100644 index 000000000000..b391c8c494fc --- /dev/null +++ b/browser/metro/base/tests/mochitest/res/textarea01.html @@ -0,0 +1,24 @@ + + + + + + + + + \ No newline at end of file diff --git a/browser/metro/base/tests/mochitest/text-block.html b/browser/metro/base/tests/mochitest/res/textblock01.html similarity index 100% rename from browser/metro/base/tests/mochitest/text-block.html rename to browser/metro/base/tests/mochitest/res/textblock01.html diff --git a/browser/metro/base/tests/mochitest/res/textinput01.html b/browser/metro/base/tests/mochitest/res/textinput01.html new file mode 100644 index 000000000000..06af960d0882 --- /dev/null +++ b/browser/metro/base/tests/mochitest/res/textinput01.html @@ -0,0 +1,14 @@ + + + + + + + + + \ No newline at end of file diff --git a/browser/metro/modules/CrossSlide.jsm b/browser/metro/modules/CrossSlide.jsm index 6d105a78040c..38f6e476cf73 100644 --- a/browser/metro/modules/CrossSlide.jsm +++ b/browser/metro/modules/CrossSlide.jsm @@ -145,7 +145,7 @@ CrossSlideHandler.prototype = { } }, - cancel: function(){ + cancel: function(aEvent){ this._fireProgressEvent("cancelled", aEvent); this.drag = null; }, @@ -183,7 +183,7 @@ CrossSlideHandler.prototype = { if (aEvent.touches.length!==1) { // cancel if another touch point gets involved - return this.cancel(); + return this.cancel(aEvent); } let startPt = this.drag.origin; @@ -202,7 +202,7 @@ CrossSlideHandler.prototype = { if (-1 == newState) { // out of bounds, cancel the event always - return this.cancel(); + return this.cancel(aEvent); } let isWithinCone = withinCone(crossAxisDistance, scrollAxisDistance); @@ -213,7 +213,7 @@ CrossSlideHandler.prototype = { if (currState >= CrossSlidingState.SELECTING && !isWithinCone) { // we're committed to a cross-slide gesture, // so going out of bounds at this point means aborting - return this.cancel(); + return this.cancel(aEvent); } if (currState > newState) { @@ -232,7 +232,7 @@ CrossSlideHandler.prototype = { aEvent.stopPropagation(); if (this.drag.state < CrossSlidingState.SELECTING) { - return this.cancel(); + return this.cancel(aEvent); } this._fireProgressEvent("completed", aEvent); diff --git a/browser/themes/linux/Toolbar-small.png b/browser/themes/linux/Toolbar-small.png index ca448250e601..bcc8f63dcd9f 100644 Binary files a/browser/themes/linux/Toolbar-small.png and b/browser/themes/linux/Toolbar-small.png differ diff --git a/browser/themes/linux/Toolbar.png b/browser/themes/linux/Toolbar.png index 5ae54323a35b..2851657ecaf3 100644 Binary files a/browser/themes/linux/Toolbar.png and b/browser/themes/linux/Toolbar.png differ diff --git a/browser/themes/linux/browser.css b/browser/themes/linux/browser.css index 054cbe48f15c..62ea4dd19c11 100644 --- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -476,10 +476,6 @@ menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) { list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png"); } -#BMB_bookmarkThisPage { - list-style-image: url("chrome://browser/skin/places/starPage.png"); -} - #BMB_unsortedBookmarks { list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png"); } @@ -654,19 +650,10 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button { -moz-image-region: rect(0px 48px 24px 24px); } -#bookmarks-button, -#bookmarks-menu-button { +#bookmarks-button { -moz-image-region: rect(0px 72px 24px 48px); } -#bookmarks-menu-button.bookmark-item { - list-style-image: url("chrome://browser/skin/Toolbar-small.png"); -} - -#bookmarks-menu-button.toolbarbutton-1 { - -moz-box-orient: horizontal; -} - #print-button { list-style-image: url("moz-icon://stock/gtk-print?size=toolbar"); } @@ -727,8 +714,8 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button { -moz-image-region: rect(0px 168px 24px 144px); } -#feed-button[disabled="true"] > .toolbarbutton-icon { - opacity: .3; +#feed-button[disabled] > .toolbarbutton-icon { + opacity: .4; } #webrtc-status-button { @@ -820,9 +807,7 @@ toolbar[iconsize="small"] #history-button { -moz-image-region: rect(0px 32px 16px 16px); } -toolbar[iconsize="small"] #bookmarks-button, -toolbar[iconsize="small"] #bookmarks-menu-button, -#bookmarks-menu-button.bookmark-item { +toolbar[iconsize="small"] #bookmarks-button { -moz-image-region: rect(0px 48px 16px 32px); } @@ -1351,7 +1336,8 @@ toolbar[iconsize="small"] #webrtc-status-button { .ac-result-type-bookmark, .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) { - list-style-image: url("chrome://browser/skin/places/pageStarred.png"); + list-style-image: url("chrome://browser/skin/places/star-icons.png"); + -moz-image-region: rect(0px 32px 16px 16px); width: 16px; height: 16px; } @@ -1493,13 +1479,34 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- margin-top: 1em; } -/* Star button */ -#star-button { - list-style-image: url("chrome://browser/skin/places/starPage.png"); +/* bookmarks menu-button */ + +#bookmarks-menu-button { + list-style-image: url("chrome://browser/skin/Toolbar.png"); + -moz-image-region: rect(0px 216px 24px 192px); } -#star-button[starred="true"] { - list-style-image: url("chrome://browser/skin/places/pageStarred.png"); +#bookmarks-menu-button[starred] { + -moz-image-region: rect(24px 216px 48px 192px); +} + +toolbar[iconsize="small"] #bookmarks-menu-button, +#bookmarks-menu-button.bookmark-item { + list-style-image: url("chrome://browser/skin/Toolbar-small.png"); + -moz-image-region: rect(0px 144px 16px 128px); +} + +toolbar[iconsize="small"] #bookmarks-menu-button[starred], +#bookmarks-menu-button.bookmark-item[starred] { + -moz-image-region: rect(16px 144px 32px 128px); +} + +#bookmarks-menu-button[disabled] > .toolbarbutton-icon, +#bookmarks-menu-button[disabled] > .toolbarbutton-menu-dropmarker, +#bookmarks-menu-button[disabled] > .toolbarbutton-menubutton-dropmarker, +#bookmarks-menu-button[disabled] > .toolbarbutton-menubutton-button > .toolbarbutton-icon, +#bookmarks-menu-button > .toolbarbutton-menubutton-button[disabled] > .toolbarbutton-icon { + opacity: .4; } /* Bookmarking panel */ diff --git a/browser/themes/linux/devtools/common.css b/browser/themes/linux/devtools/common.css index 336e2c543626..8865f23cdb47 100644 --- a/browser/themes/linux/devtools/common.css +++ b/browser/themes/linux/devtools/common.css @@ -130,7 +130,7 @@ -moz-appearance: none !important; list-style-image: url("chrome://browser/skin/devtools/dropmarker.png"); -moz-box-align: center; - margin: 0 3px; + padding: 0 3px; } /* Text input */ diff --git a/browser/themes/linux/devtools/netmonitor.css b/browser/themes/linux/devtools/netmonitor.css index 56925dfcd724..22e8b3038ce2 100644 --- a/browser/themes/linux/devtools/netmonitor.css +++ b/browser/themes/linux/devtools/netmonitor.css @@ -93,11 +93,13 @@ } .requests-menu-file { - width: 14em; + width: 20vw; + min-width: 4em; } .requests-menu-domain { - width: 14em; + width: 14vw; + min-width: 10em; } .requests-menu-type { @@ -303,13 +305,44 @@ #details-pane { max-width: none; margin: 0 !important; - /* To prevent all the margin hacks to hide the sidebar */ + /* To prevent all the margin hacks to hide the sidebar. */ + } + + .requests-menu-status-and-method { + width: 14vw; + } + + .requests-menu-file, + .requests-menu-domain { + width: 30vw; + min-width: 10em; + } + + .requests-menu-type { + width: 8vw; } .requests-menu-size { - border-width: 0px !important; + width: 16vw; + border-width: 0 !important; box-shadow: none !important; - /* !important are required here because Timeline is not visible and thus - the right border and box-shadow of Size column should be hidden */ + /* The "Timeline" header is not visible anymore, and thus the + right border and box-shadow of "Size" column should be hidden. */ + } +} + +@media (min-width: 701px) { + #network-table[type-overflows] .requests-menu-domain { + border-width: 0 !important; + box-shadow: none !important; + /* The "Type" header is not visible anymore, and thus the + right border and box-shadow of "Domain" column should be hidden. */ + } + + #network-table[domain-overflows] .requests-menu-file { + border-width: 0 !important; + box-shadow: none !important; + /* The "Domain" header is not visible anymore, and thus the + right border and box-shadow of "File" column should be hidden. */ } } diff --git a/browser/themes/linux/downloads/downloads.css b/browser/themes/linux/downloads/downloads.css index bb597a8916f1..240e37d860dd 100644 --- a/browser/themes/linux/downloads/downloads.css +++ b/browser/themes/linux/downloads/downloads.css @@ -155,7 +155,7 @@ richlistitem[type="download"]:last-child { /*** Highlighted list items ***/ -#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover { +#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover { border-radius: 3px; border-top: 1px solid hsla(0,0%,100%,.3); border-bottom: 1px solid hsla(0,0%,0%,.2); diff --git a/browser/themes/linux/jar.mn b/browser/themes/linux/jar.mn index 3d00542ba472..2e2ad9ae4773 100644 --- a/browser/themes/linux/jar.mn +++ b/browser/themes/linux/jar.mn @@ -83,14 +83,13 @@ browser.jar: skin/classic/browser/places/calendar.png (places/calendar.png) * skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css) skin/classic/browser/places/livemark-item.png (places/livemark-item.png) - skin/classic/browser/places/pageStarred.png (places/pageStarred.png) + skin/classic/browser/places/star-icons.png (places/star-icons.png) skin/classic/browser/places/starred48.png (places/starred48.png) skin/classic/browser/places/unstarred48.png (places/unstarred48.png) skin/classic/browser/places/places.css (places/places.css) skin/classic/browser/places/organizer.css (places/organizer.css) skin/classic/browser/places/organizer.xml (places/organizer.xml) skin/classic/browser/places/query.png (places/query.png) - skin/classic/browser/places/starPage.png (places/starPage.png) skin/classic/browser/places/tag.png (places/tag.png) skin/classic/browser/places/toolbarDropMarker.png (places/toolbarDropMarker.png) skin/classic/browser/places/unsortedBookmarks.png (places/unsortedBookmarks.png) diff --git a/browser/themes/linux/places/pageStarred.png b/browser/themes/linux/places/pageStarred.png deleted file mode 100644 index 61a9f90e05b1..000000000000 Binary files a/browser/themes/linux/places/pageStarred.png and /dev/null differ diff --git a/browser/themes/linux/places/star-icons.png b/browser/themes/linux/places/star-icons.png new file mode 100644 index 000000000000..2f50c6ab973e Binary files /dev/null and b/browser/themes/linux/places/star-icons.png differ diff --git a/browser/themes/linux/places/starPage.png b/browser/themes/linux/places/starPage.png deleted file mode 100644 index 3193a3535f1e..000000000000 Binary files a/browser/themes/linux/places/starPage.png and /dev/null differ diff --git a/browser/themes/osx/Toolbar-lion.png b/browser/themes/osx/Toolbar-lion.png index 8b83fdc84177..a91ba7bfd66d 100644 Binary files a/browser/themes/osx/Toolbar-lion.png and b/browser/themes/osx/Toolbar-lion.png differ diff --git a/browser/themes/osx/Toolbar-lion@2x.png b/browser/themes/osx/Toolbar-lion@2x.png index f721e5057c8a..10b799485857 100644 Binary files a/browser/themes/osx/Toolbar-lion@2x.png and b/browser/themes/osx/Toolbar-lion@2x.png differ diff --git a/browser/themes/osx/Toolbar.png b/browser/themes/osx/Toolbar.png index 521c6c3887b4..c1868929dd8e 100644 Binary files a/browser/themes/osx/Toolbar.png and b/browser/themes/osx/Toolbar.png differ diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css index 54e80328abe1..3737c2fed26b 100644 --- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -438,7 +438,8 @@ toolbarbutton.bookmark-item > menupopup { list-style-image: url("chrome://browser/skin/Toolbar@2x.png"); } - :-moz-any(@primaryToolbarButtons@):not(#tabview-button) > .toolbarbutton-icon { + :-moz-any(@primaryToolbarButtons@):not(#tabview-button) > .toolbarbutton-icon, + :-moz-any(@primaryToolbarButtons@):not(#tabview-button) > .toolbarbutton-menubutton-button > .toolbarbutton-icon { width: 20px; } } @@ -474,7 +475,7 @@ toolbar:not([mode="icons"]) #restore-button { .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon, .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, -.toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon, +.toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon, #restore-button[disabled="true"] > .toolbarbutton-icon { opacity: .4; } @@ -482,7 +483,7 @@ toolbar:not([mode="icons"]) #restore-button { @media (-moz-mac-lion-theme) { .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon, .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, - .toolbarbutton-1[type="menu-button"] > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon, + .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon, #restore-button[disabled="true"] > .toolbarbutton-icon, .toolbarbutton-1[disabled="true"] > .toolbarbutton-menu-dropmarker, .toolbarbutton-1[disabled="true"] > .toolbarbutton-menubutton-dropmarker, @@ -490,13 +491,14 @@ toolbar:not([mode="icons"]) #restore-button { .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-badge-container > .toolbarbutton-icon, #restore-button:not(:hover):-moz-window-inactive > .toolbarbutton-icon, .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menu-dropmarker, - .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-dropmarker { + .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-dropmarker, + .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-button > .toolbarbutton-icon { opacity: .5; } .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-icon, .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, - .toolbarbutton-1:-moz-window-inactive[type="menu-button"] > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon, + .toolbarbutton-1:-moz-window-inactive > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon, #restore-button:-moz-window-inactive[disabled="true"] > .toolbarbutton-icon { opacity: .25; } @@ -524,8 +526,7 @@ toolbar:not([mode="icons"]) #restore-button { } .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { - width: 14px; - padding-top: 2px; + padding: 2px 4px 0; -moz-border-start: none !important; } @@ -916,8 +917,7 @@ toolbar[mode="icons"] #forward-button:-moz-lwtheme { /* bookmark sidebar & menu buttons */ -#bookmarks-button, -#bookmarks-menu-button { +#bookmarks-button { -moz-image-region: rect(0, 180px, 20px, 160px); } @@ -925,33 +925,14 @@ toolbar[mode="icons"] #forward-button:-moz-lwtheme { -moz-image-region: rect(20px, 180px, 40px, 160px); } -#bookmarks-menu-button.bookmark-item { - -moz-image-region: rect(2px, 178px, 18px, 162px); - list-style-image: url("chrome://browser/skin/Toolbar.png"); -} - @media (min-resolution: 2dppx) { - #bookmarks-button, - #bookmarks-menu-button { + #bookmarks-button { -moz-image-region: rect(0, 360px, 40px, 320px); } #bookmarks-button[checked="true"] { -moz-image-region: rect(40px, 360px, 80px, 320px); } - - #bookmarks-menu-button.bookmark-item { - -moz-image-region: rect(4px, 356px, 36px, 324px); - list-style-image: url("chrome://browser/skin/Toolbar@2x.png"); - } - - #bookmarks-menu-button.bookmark-item > .toolbarbutton-icon { - width: 16px; - } -} - -#bookmarks-menu-button.toolbarbutton-1 { - -moz-box-orient: horizontal; } /* print button */ @@ -1736,35 +1717,49 @@ window[tabsontop="false"] richlistitem[type~="action"][actiontype="switchtab"][s margin-top: 1em; } -/* STAR BUTTON */ -#star-button { +/* bookmarks menu-button */ + +#bookmarks-menu-button { + -moz-image-region: rect(0px 500px 20px 480px); +} + +#bookmarks-menu-button[starred] { + -moz-image-region: rect(20px 500px 40px 480px); +} + +#bookmarks-menu-button.bookmark-item { list-style-image: url("chrome://browser/skin/places/star-icons.png"); - -moz-image-region: rect(0, 16px, 16px, 0); + -moz-image-region: rect(0px 16px 16px 0px); } -#star-button:hover:active, -#star-button[starred="true"] { - -moz-image-region: rect(0, 32px, 16px, 16px); +#bookmarks-menu-button.bookmark-item[starred] { + -moz-image-region: rect(0px 32px 16px 16px); } -#star-button:hover:active[starred="true"] { - -moz-image-region: rect(0, 48px, 16px, 32px); +#bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button { + padding: 0; } @media (min-resolution: 2dppx) { - #star-button { + #bookmarks-menu-button { + -moz-image-region: rect(0px, 1000px, 40px, 960px); + } + + #bookmarks-menu-button[starred] { + -moz-image-region: rect(40px, 1000px, 80px, 960px); + } + + #bookmarks-menu-button.bookmark-item { list-style-image: url("chrome://browser/skin/places/star-icons@2x.png"); - -moz-image-region: rect(0, 32px, 32px, 0); - width: 22px; + -moz-image-region: rect(0px 32px 32px 0px); } - #star-button:hover:active, - #star-button[starred="true"] { - -moz-image-region: rect(0, 64px, 32px, 32px); + #bookmarks-menu-button.bookmark-item[starred] { + -moz-image-region: rect(0px 64px 32px 32px); } - #star-button:hover:active[starred="true"] { - -moz-image-region: rect(0, 96px, 32px, 64px); + #bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button > .toolbarbutton-icon { + width: 16px; } } @@ -2517,12 +2512,16 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker { } :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([type="menu-button"]), -:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button, -:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { +:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button { margin: 0; padding: 0 1px; } +:-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { + padding-left: 4px; + padding-right: 4px; +} + .tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover, .tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover, :-moz-any(#TabsToolbar, #addon-bar) .toolbarbutton-1:not([type="menu-button"]):not([disabled]):not([open]):hover, diff --git a/browser/themes/osx/devtools/common.css b/browser/themes/osx/devtools/common.css index e7b29c118f9b..8aa99db592a8 100644 --- a/browser/themes/osx/devtools/common.css +++ b/browser/themes/osx/devtools/common.css @@ -134,7 +134,6 @@ .devtools-toolbarbutton[type=menu-button] > .toolbarbutton-menubutton-dropmarker { -moz-appearance: none !important; list-style-image: url("chrome://browser/skin/devtools/dropmarker.png"); - margin: 0 3px; border: 0; } diff --git a/browser/themes/osx/devtools/netmonitor.css b/browser/themes/osx/devtools/netmonitor.css index 270fc5cf9b49..de362627ed09 100644 --- a/browser/themes/osx/devtools/netmonitor.css +++ b/browser/themes/osx/devtools/netmonitor.css @@ -93,11 +93,13 @@ } .requests-menu-file { - width: 16em; + width: 20vw; + min-width: 4em; } .requests-menu-domain { - width: 16em; + width: 14vw; + min-width: 10em; } .requests-menu-type { @@ -303,13 +305,44 @@ #details-pane { max-width: none; margin: 0 !important; - /* To prevent all the margin hacks to hide the sidebar */ + /* To prevent all the margin hacks to hide the sidebar. */ + } + + .requests-menu-status-and-method { + width: 14vw; + } + + .requests-menu-file, + .requests-menu-domain { + width: 30vw; + min-width: 10em; + } + + .requests-menu-type { + width: 8vw; } .requests-menu-size { - border-width: 0px !important; + width: 16vw; + border-width: 0 !important; box-shadow: none !important; - /* !important are required here because Timeline is not visible and thus - the right border and box-shadow of Size column should be hidden */ + /* The "Timeline" header is not visible anymore, and thus the + right border and box-shadow of "Size" column should be hidden. */ + } +} + +@media (min-width: 701px) { + #network-table[type-overflows] .requests-menu-domain { + border-width: 0 !important; + box-shadow: none !important; + /* The "Type" header is not visible anymore, and thus the + right border and box-shadow of "Domain" column should be hidden. */ + } + + #network-table[domain-overflows] .requests-menu-file { + border-width: 0 !important; + box-shadow: none !important; + /* The "Domain" header is not visible anymore, and thus the + right border and box-shadow of "File" column should be hidden. */ } } diff --git a/browser/themes/osx/downloads/downloads.css b/browser/themes/osx/downloads/downloads.css index 9a8f0620ed6f..59fb5eb573ab 100644 --- a/browser/themes/osx/downloads/downloads.css +++ b/browser/themes/osx/downloads/downloads.css @@ -177,7 +177,7 @@ richlistitem[type="download"]:last-child { /*** Highlighted list items ***/ #downloadsPanel[keyfocus] > #downloadsListBox:focus > richlistitem[type="download"][selected], -#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover { +#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover { border-radius: 3px; border-top: 1px solid hsla(0,0%,100%,.2); border-bottom: 1px solid hsla(0,0%,0%,.4); @@ -185,7 +185,7 @@ richlistitem[type="download"]:last-child { color: HighlightText; } -#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover { +#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover { cursor: pointer; } diff --git a/browser/themes/windows/Toolbar-inverted.png b/browser/themes/windows/Toolbar-inverted.png index d6efac70dad7..2c3253fe80f9 100644 Binary files a/browser/themes/windows/Toolbar-inverted.png and b/browser/themes/windows/Toolbar-inverted.png differ diff --git a/browser/themes/windows/Toolbar.png b/browser/themes/windows/Toolbar.png index 35f0caaaea22..b84f6ddadf1a 100644 Binary files a/browser/themes/windows/Toolbar.png and b/browser/themes/windows/Toolbar.png differ diff --git a/browser/themes/windows/browser-aero.css b/browser/themes/windows/browser-aero.css index 1f919fd2a19b..defe92dc86cb 100644 --- a/browser/themes/windows/browser-aero.css +++ b/browser/themes/windows/browser-aero.css @@ -159,8 +159,8 @@ border-right-style: none !important; } - #navigator-toolbox[tabsontop=false] > :-moz-any(#toolbar-menubar, #nav-bar) :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme), - #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme) { + #navigator-toolbox[tabsontop=false] > :-moz-any(#toolbar-menubar, #nav-bar) :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#sync-button[status],#bookmarks-menu-button)) > .toolbarbutton-icon:not(:-moz-lwtheme), + #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status],#bookmarks-menu-button)) > .toolbarbutton-icon:not(:-moz-lwtheme) { list-style-image: url("chrome://browser/skin/Toolbar-inverted.png"); } diff --git a/browser/themes/windows/browser.css b/browser/themes/windows/browser.css index 75236df0ed49..0f64209738fd 100644 --- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -647,7 +647,7 @@ toolbarbutton.bookmark-item[open="true"] { -moz-padding-end: 2px; } -.bookmark-item:not(#bookmarks-menu-button) > .toolbarbutton-icon { +.bookmark-item > .toolbarbutton-icon { width: 16px; height: 16px; } @@ -764,7 +764,8 @@ menuitem.bookmark-item { .toolbarbutton-1[disabled=true] > .toolbarbutton-icon, .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker, .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker, -.toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon { +.toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon, +.toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled] > .toolbarbutton-icon { opacity: .4; } @@ -866,7 +867,7 @@ toolbar[mode=full] .toolbarbutton-1 > .toolbarbutton-menubutton-button { } @navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon { - padding: 8px 3px 7px; + padding: 8px 5px 7px; } @navbarLargeIcons@ .toolbarbutton-1:not(:hover):not(:active):not([open]) > .toolbarbutton-menubutton-dropmarker::before, @@ -1115,16 +1116,9 @@ toolbar[mode=full] .toolbarbutton-1 > .toolbarbutton-menubutton-button { -moz-image-region: rect(0, 126px, 18px, 108px); } -#bookmarks-button, -#bookmarks-menu-button { +#bookmarks-button { -moz-image-region: rect(0, 144px, 18px, 126px); } -#bookmarks-menu-button.bookmark-item { - list-style-image: url("chrome://browser/skin/Toolbar.png"); -} -#bookmarks-menu-button.bookmark-item:-moz-lwtheme-brighttext { - list-style-image: url("chrome://browser/skin/Toolbar-inverted.png"); -} #print-button { -moz-image-region: rect(0, 162px, 18px, 144px); @@ -1580,8 +1574,8 @@ html|*.urlbar-input:-moz-lwtheme::-moz-placeholder, .ac-result-type-bookmark, .autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) { - list-style-image: url("chrome://browser/skin/places/editBookmark.png"); - -moz-image-region: rect(0px 16px 16px 0px); + list-style-image: url("chrome://browser/skin/places/bookmark.png"); + -moz-image-region: rect(0px 48px 16px 32px); width: 16px; height: 16px; } @@ -1792,28 +1786,31 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action- margin-top: 1em; } -/* star button */ +/* bookmarks menu-button */ -#star-button { +#bookmarks-menu-button { + -moz-image-region: rect(0px 378px 18px 360px); +} + +#bookmarks-menu-button[starred] { + -moz-image-region: rect(18px 378px 36px 360px); +} + +#bookmarks-menu-button.bookmark-item { list-style-image: url("chrome://browser/skin/places/bookmark.png"); -moz-image-region: rect(0px 16px 16px 0px); } -#star-button:hover { - background-image: radial-gradient(circle closest-side, hsla(45,100%,73%,.3), hsla(45,100%,73%,0)); - -moz-image-region: rect(0px 32px 16px 16px); -} - -#star-button:hover:active { - background-image: radial-gradient(circle closest-side, hsla(45,100%,73%,.1), hsla(45,100%,73%,0)); +#bookmarks-menu-button.bookmark-item[starred] { -moz-image-region: rect(0px 48px 16px 32px); } -#star-button[starred="true"] { - list-style-image: url("chrome://browser/skin/places/editBookmark.png"); +#bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button> .toolbarbutton-icon { + -moz-margin-start: 5px; } /* bookmarking panel */ + #editBookmarkPanelStarIcon { list-style-image: url("chrome://browser/skin/places/starred48.png"); width: 48px; diff --git a/browser/themes/windows/devtools/netmonitor.css b/browser/themes/windows/devtools/netmonitor.css index 270fc5cf9b49..de362627ed09 100644 --- a/browser/themes/windows/devtools/netmonitor.css +++ b/browser/themes/windows/devtools/netmonitor.css @@ -93,11 +93,13 @@ } .requests-menu-file { - width: 16em; + width: 20vw; + min-width: 4em; } .requests-menu-domain { - width: 16em; + width: 14vw; + min-width: 10em; } .requests-menu-type { @@ -303,13 +305,44 @@ #details-pane { max-width: none; margin: 0 !important; - /* To prevent all the margin hacks to hide the sidebar */ + /* To prevent all the margin hacks to hide the sidebar. */ + } + + .requests-menu-status-and-method { + width: 14vw; + } + + .requests-menu-file, + .requests-menu-domain { + width: 30vw; + min-width: 10em; + } + + .requests-menu-type { + width: 8vw; } .requests-menu-size { - border-width: 0px !important; + width: 16vw; + border-width: 0 !important; box-shadow: none !important; - /* !important are required here because Timeline is not visible and thus - the right border and box-shadow of Size column should be hidden */ + /* The "Timeline" header is not visible anymore, and thus the + right border and box-shadow of "Size" column should be hidden. */ + } +} + +@media (min-width: 701px) { + #network-table[type-overflows] .requests-menu-domain { + border-width: 0 !important; + box-shadow: none !important; + /* The "Type" header is not visible anymore, and thus the + right border and box-shadow of "Domain" column should be hidden. */ + } + + #network-table[domain-overflows] .requests-menu-file { + border-width: 0 !important; + box-shadow: none !important; + /* The "Domain" header is not visible anymore, and thus the + right border and box-shadow of "File" column should be hidden. */ } } diff --git a/browser/themes/windows/downloads/downloads-aero.css b/browser/themes/windows/downloads/downloads-aero.css index b9848ac9bd6a..68dbfc1117dc 100644 --- a/browser/themes/windows/downloads/downloads-aero.css +++ b/browser/themes/windows/downloads/downloads-aero.css @@ -12,7 +12,7 @@ border-bottom: 1px solid hsl(213,40%,90%); } - #downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover { + #downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover { border: 1px solid hsl(213,45%,65%); box-shadow: 0 0 0 1px hsla(0,0%,100%,.5) inset, 0 1px 0 hsla(0,0%,100%,.3) inset; diff --git a/browser/themes/windows/downloads/downloads.css b/browser/themes/windows/downloads/downloads.css index 8e65d5f2c0be..bd7503862798 100644 --- a/browser/themes/windows/downloads/downloads.css +++ b/browser/themes/windows/downloads/downloads.css @@ -166,7 +166,7 @@ richlistitem[type="download"]:first-child { /*** Highlighted list items ***/ -#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"]:hover { +#downloadsPanel:not([keyfocus]) > #downloadsListBox > richlistitem[type="download"][state="1"][exists]:hover { border-radius: 3px; border-top: 1px solid hsla(0,0%,100%,.2); border-bottom: 1px solid hsla(0,0%,0%,.2); diff --git a/browser/themes/windows/jar.mn b/browser/themes/windows/jar.mn index 0d21ef168895..d3bbadbdf1d0 100644 --- a/browser/themes/windows/jar.mn +++ b/browser/themes/windows/jar.mn @@ -98,7 +98,6 @@ browser.jar: skin/classic/browser/places/places.css (places/places.css) * skin/classic/browser/places/organizer.css (places/organizer.css) skin/classic/browser/places/bookmark.png (places/bookmark.png) - skin/classic/browser/places/editBookmark.png (places/editBookmark.png) skin/classic/browser/places/query.png (places/query.png) skin/classic/browser/places/bookmarksMenu.png (places/bookmarksMenu.png) skin/classic/browser/places/bookmarksToolbar.png (places/bookmarksToolbar.png) @@ -357,7 +356,6 @@ browser.jar: * skin/classic/aero/browser/places/places.css (places/places-aero.css) * skin/classic/aero/browser/places/organizer.css (places/organizer-aero.css) skin/classic/aero/browser/places/bookmark.png (places/bookmark.png) - skin/classic/aero/browser/places/editBookmark.png (places/editBookmark.png) skin/classic/aero/browser/places/query.png (places/query-aero.png) skin/classic/aero/browser/places/bookmarksMenu.png (places/bookmarksMenu-aero.png) skin/classic/aero/browser/places/bookmarksToolbar.png (places/bookmarksToolbar-aero.png) diff --git a/browser/themes/windows/places/editBookmark.png b/browser/themes/windows/places/editBookmark.png deleted file mode 100644 index fbca0523df16..000000000000 Binary files a/browser/themes/windows/places/editBookmark.png and /dev/null differ diff --git a/config/system-headers b/config/system-headers index 2ea8d2fa8682..c3906a82c6a8 100644 --- a/config/system-headers +++ b/config/system-headers @@ -1073,6 +1073,8 @@ conic/conicstatisticsevent.h #endif #if MOZ_NATIVE_LIBEVENT==1 event.h +#else +sys/event.h #endif #ifdef MOZ_ENABLE_LIBPROXY proxy.h diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index a5526b9117d4..53e1fde61e8d 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -425,7 +425,7 @@ public: * * @return The document or null if no JS Context. */ - static nsIDOMDocument *GetDocumentFromCaller(); + static nsIDocument* GetDocumentFromCaller(); /** * Get the document through the JS context that's currently on the stack. @@ -434,7 +434,7 @@ public: * * @return The document or null if no JS context */ - static nsIDOMDocument *GetDocumentFromContext(); + static nsIDocument* GetDocumentFromContext(); // Check if a node is in the document prolog, i.e. before the document // element. diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 5207f657b51f..4adfb3e69c3b 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -1709,7 +1709,7 @@ nsContentUtils::GetWindowFromCaller() return nullptr; } -nsIDOMDocument * +nsIDocument* nsContentUtils::GetDocumentFromCaller() { JSContext *cx = nullptr; @@ -1725,10 +1725,10 @@ nsContentUtils::GetDocumentFromCaller() return nullptr; } - return win->GetExtantDocument(); + return win->GetExtantDoc(); } -nsIDOMDocument * +nsIDocument* nsContentUtils::GetDocumentFromContext() { JSContext *cx = nullptr; @@ -1740,7 +1740,7 @@ nsContentUtils::GetDocumentFromContext() if (sgo) { nsCOMPtr pwin = do_QueryInterface(sgo); if (pwin) { - return pwin->GetExtantDocument(); + return pwin->GetExtantDoc(); } } } diff --git a/content/base/src/nsDOMFile.cpp b/content/base/src/nsDOMFile.cpp index 10490dc1b940..ac82f1868ed0 100644 --- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -260,8 +260,7 @@ nsDOMFileBase::MozSlice(int64_t aStart, int64_t aEnd, if (sgo) { nsCOMPtr window = do_QueryInterface(sgo); if (window) { - nsCOMPtr document = - do_QueryInterface(window->GetExtantDocument()); + nsCOMPtr document = window->GetExtantDoc(); if (document) { document->WarnOnceAbout(nsIDocument::eMozSlice); } @@ -276,7 +275,7 @@ nsDOMFileBase::GetInternalStream(nsIInputStream **aStream) { // Must be overridden NS_NOTREACHED("Must override GetInternalStream"); - + return NS_ERROR_NOT_IMPLEMENTED; } diff --git a/content/base/src/nsDOMMutationObserver.h b/content/base/src/nsDOMMutationObserver.h index a9694ad82342..f778942ecc6b 100644 --- a/content/base/src/nsDOMMutationObserver.h +++ b/content/base/src/nsDOMMutationObserver.h @@ -408,7 +408,7 @@ protected: bool Suppressed() { if (mOwner) { - nsCOMPtr d = do_QueryInterface(mOwner->GetExtantDocument()); + nsCOMPtr d = mOwner->GetExtantDoc(); return d && d->IsInSyncOperation(); } return false; diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index 1259e9b15ec9..b29df07f304c 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -2373,7 +2373,7 @@ CSPErrorQueue::Flush(nsIDocument* aDocument) for (uint32_t i = 0; i < mErrors.Length(); i++) { nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "CSP", aDocument, - nsContentUtils::eDOM_PROPERTIES, + nsContentUtils::eSECURITY_PROPERTIES, mErrors[i]); } mErrors.Clear(); @@ -2530,22 +2530,11 @@ nsDocument::InitCSP(nsIChannel* aChannel) // If the old header is present, warn that it will be deprecated. if (!cspOldHeaderValue.IsEmpty() || !cspOldROHeaderValue.IsEmpty()) { - nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, - "CSP", this, - nsContentUtils::eDOM_PROPERTIES, - "OldCSPHeaderDeprecated"); - - // Additionally log deprecated warning to Web Console. mCSPWebConsoleErrorQueue.Add("OldCSPHeaderDeprecated"); // Also, if the new headers AND the old headers were present, warn // that the old headers will be ignored. if (cspSpecCompliant) { - nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, - "CSP", this, - nsContentUtils::eDOM_PROPERTIES, - "BothCSPHeadersPresent"); - // Additionally log to Web Console. mCSPWebConsoleErrorQueue.Add("BothCSPHeadersPresent"); } } @@ -2582,11 +2571,6 @@ nsDocument::InitCSP(nsIChannel* aChannel) // CSP policies are present since CSP only allows one policy and it can't // be partially report-only. if (applyAppDefaultCSP || applyCSPFromHeader) { - nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, - "CSP", this, - nsContentUtils::eDOM_PROPERTIES, - "ReportOnlyCSPIgnored"); - // Additionally log to Web Console. mCSPWebConsoleErrorQueue.Add("ReportOnlyCSPIgnored"); #ifdef PR_LOGGING PR_LOG(gCspPRLog, PR_LOG_DEBUG, @@ -6239,8 +6223,6 @@ nsDocument::GetAnimationController() return mAnimationController; } -static const char* dirAttributes[] = { "ltr", "rtl", "auto", 0 }; - /** * Retrieve the "direction" property of the document. * @@ -6259,14 +6241,7 @@ nsIDocument::GetDir(nsAString& aDirection) const aDirection.Truncate(); Element* rootElement = GetHtmlElement(); if (rootElement) { - nsAutoString dir; - rootElement->GetAttr(kNameSpaceID_None, nsGkAtoms::dir, dir); - for (uint32_t i = 0; dirAttributes[i]; ++i) { - if (dir.LowerCaseEqualsASCII(dirAttributes[i])) { - aDirection.AssignASCII(dirAttributes[i]); - return; - } - } + static_cast(rootElement)->GetDir(aDirection); } } @@ -11261,7 +11236,7 @@ nsAutoSyncOperation::nsAutoSyncOperation(nsIDocument* aDoc) win->GetTop(getter_AddRefs(topWindow)); nsCOMPtr top = do_QueryInterface(topWindow); if (top) { - nsCOMPtr doc = do_QueryInterface(top->GetExtantDocument()); + nsCOMPtr doc = top->GetExtantDoc(); MarkDocumentTreeToBeInSyncOperation(doc, &mDocuments); } } diff --git a/content/base/src/nsFrameLoader.cpp b/content/base/src/nsFrameLoader.cpp index 0dbdc172c92b..6c24715495ab 100644 --- a/content/base/src/nsFrameLoader.cpp +++ b/content/base/src/nsFrameLoader.cpp @@ -1095,10 +1095,8 @@ nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, SameCOMIdentity(otherChromeEventHandler, otherContent), "How did that happen, exactly?"); - nsCOMPtr ourChildDocument = - do_QueryInterface(ourWindow->GetExtantDocument()); - nsCOMPtr otherChildDocument = - do_QueryInterface(otherWindow->GetExtantDocument()); + nsCOMPtr ourChildDocument = ourWindow->GetExtantDoc(); + nsCOMPtr otherChildDocument = otherWindow ->GetExtantDoc(); if (!ourChildDocument || !otherChildDocument) { // This shouldn't be happening return NS_ERROR_NOT_IMPLEMENTED; diff --git a/content/base/src/nsObjectLoadingContent.cpp b/content/base/src/nsObjectLoadingContent.cpp index 759b0b1d9530..831be08a7052 100644 --- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -745,6 +745,8 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading) // Flush layout so that the frame is created if possible and the plugin is // initialized with the latest information. doc->FlushPendingNotifications(Flush_Layout); + // Flushing layout may have re-entered and loaded something underneath us + NS_ENSURE_TRUE(mInstantiating, NS_OK); if (!thisContent->GetPrimaryFrame()) { LOG(("OBJLC [%p]: Not instantiating plugin with no frame", this)); @@ -767,16 +769,42 @@ nsObjectLoadingContent::InstantiatePluginInstance(bool aIsLoading) appShell->SuspendNative(); } + nsRefPtr newOwner; rv = pluginHost->InstantiatePluginInstance(mContentType.get(), mURI.get(), this, - getter_AddRefs(mInstanceOwner)); + getter_AddRefs(newOwner)); + // XXX(johns): We don't suspend native inside stopping plugins... if (appShell) { appShell->ResumeNative(); } - if (NS_FAILED(rv)) { - return rv; + if (!mInstantiating || NS_FAILED(rv)) { + LOG(("OBJLC [%p]: Plugin instantiation failed or re-entered, " + "killing old instance", this)); + // XXX(johns): This needs to be de-duplicated with DoStopPlugin, but we + // don't want to touch the protochain or delayed stop. + // (Bug 767635) + if (newOwner) { + nsRefPtr inst; + newOwner->GetInstance(getter_AddRefs(inst)); + newOwner->SetFrame(nullptr); + if (inst) { + pluginHost->StopPluginInstance(inst); + } + newOwner->Destroy(); + } + return NS_OK; + } + + mInstanceOwner = newOwner; + + // Ensure the frame did not change during instantiation re-entry (common). + // HasNewFrame would not have mInstanceOwner yet, so the new frame would be + // dangling. (Bug 854082) + nsIFrame* frame = thisContent->GetPrimaryFrame(); + if (frame && mInstanceOwner) { + mInstanceOwner->SetFrame(static_cast(frame)); } // Set up scripting interfaces. @@ -1011,16 +1039,10 @@ nsObjectLoadingContent::HasNewFrame(nsIObjectFrame* aFrame) } // Otherwise, we're just changing frames - mInstanceOwner->SetFrame(nullptr); - // Set up relationship between instance owner and frame. nsObjectFrame *objFrame = static_cast(aFrame); mInstanceOwner->SetFrame(objFrame); - // Set up new frame to draw. - objFrame->FixupWindow(objFrame->GetContentRectRelativeToSelf().Size()); - objFrame->InvalidateFrame(); - return NS_OK; } @@ -2156,6 +2178,10 @@ nsObjectLoadingContent::UnloadObject(bool aResetState) mOriginalContentType.Truncate(); } + // InstantiatePluginInstance checks this after re-entrant calls and aborts if + // it was cleared from under it + mInstantiating = false; + mScriptRequested = false; // This call should be last as it may re-enter @@ -2294,7 +2320,6 @@ nsObjectLoadingContent::PluginDestroyed() // plugins in plugin host. Invalidate instance owner / prototype but otherwise // don't take any action. TeardownProtoChain(); - mInstanceOwner->SetFrame(nullptr); mInstanceOwner->Destroy(); mInstanceOwner = nullptr; return NS_OK; @@ -2578,6 +2603,8 @@ nsObjectLoadingContent::StopPluginInstance() CloseChannel(); } + // We detach the instance owner's frame before destruction, but don't destroy + // the instance owner until the plugin is stopped. mInstanceOwner->SetFrame(nullptr); bool delayedStop = false; diff --git a/content/base/src/nsXMLHttpRequest.cpp b/content/base/src/nsXMLHttpRequest.cpp index aa552f135c0b..e36112e9018e 100644 --- a/content/base/src/nsXMLHttpRequest.cpp +++ b/content/base/src/nsXMLHttpRequest.cpp @@ -390,7 +390,7 @@ nsXMLHttpRequest::InitParameters(bool aAnon, bool aSystem) // Chrome is always allowed access, so do the permission check only // for non-chrome pages. if (!nsContentUtils::IsCallerChrome()) { - nsCOMPtr doc = do_QueryInterface(window->GetExtantDocument()); + nsCOMPtr doc = window->GetExtantDoc(); if (!doc) { return; } @@ -560,7 +560,7 @@ static void LogMessage(const char* aWarning, nsPIDOMWindow* aWindow) { nsCOMPtr doc; if (aWindow) { - doc = do_QueryInterface(aWindow->GetExtantDocument()); + doc = aWindow->GetExtantDoc(); } nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM", doc, @@ -2861,7 +2861,7 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable& aBody) nsCOMPtr suspendedWindow(do_QueryInterface(topWindow)); if (suspendedWindow && (suspendedWindow = suspendedWindow->GetCurrentInnerWindow())) { - suspendedDoc = do_QueryInterface(suspendedWindow->GetExtantDocument()); + suspendedDoc = suspendedWindow->GetExtantDoc(); if (suspendedDoc) { suspendedDoc->SuppressEventHandling(); } diff --git a/content/base/test/file_x-frame-options_main.html b/content/base/test/file_x-frame-options_main.html index cefcc650f964..e6d7fec80e34 100644 --- a/content/base/test/file_x-frame-options_main.html +++ b/content/base/test/file_x-frame-options_main.html @@ -21,6 +21,8 @@ window.addEventListener('load', parent.testFramesLoaded, false);


+
+
diff --git a/content/media/webaudio/test/test_delayNodeWithGain.html b/content/media/webaudio/test/test_delayNodeWithGain.html new file mode 100644 index 000000000000..9ae2a616639a --- /dev/null +++ b/content/media/webaudio/test/test_delayNodeWithGain.html @@ -0,0 +1,62 @@ + + + + Test DelayNode with a GainNode + + + + +
+
+
+
+ + diff --git a/content/media/webaudio/test/test_gainNode.html b/content/media/webaudio/test/test_gainNode.html index bdecacf6612b..c772811ce11e 100644 --- a/content/media/webaudio/test/test_gainNode.html +++ b/content/media/webaudio/test/test_gainNode.html @@ -3,6 +3,7 @@ Test GainNode + @@ -18,6 +19,10 @@ addLoadEvent(function() { for (var i = 0; i < 2048; ++i) { buffer.getChannelData(0)[i] = Math.sin(440 * 2 * Math.PI * i / context.sampleRate); } + var expectedBuffer = context.createBuffer(1, 2048, context.sampleRate); + for (var i = 0; i < 2048; ++i) { + expectedBuffer.getChannelData(0)[i] = buffer.getChannelData(0)[i] / 2; + } var destination = context.destination; @@ -31,7 +36,9 @@ addLoadEvent(function() { source.buffer = buffer; source.connect(gain); - gain.connect(destination); + var sp = context.createScriptProcessor(2048, 1); + gain.connect(sp); + sp.connect(destination); ok(gain.gain, "The audioparam member must exist"); is(gain.gain.value, 1.0, "Correct initial value"); @@ -41,14 +48,15 @@ addLoadEvent(function() { is(gain.gain.defaultValue, 1.0, "Correct default value"); source.start(0); - SimpleTest.executeSoon(function() { - source.stop(0); - source.disconnect(); - gain.disconnect(); + sp.onaudioprocess = function(e) { + is(e.inputBuffer.numberOfChannels, 1, "Correct input channel count"); + compareBuffers(e.inputBuffer.getChannelData(0), expectedBuffer.getChannelData(0)); + + sp.onaudioprocess = null; SpecialPowers.clearUserPref("media.webaudio.enabled"); SimpleTest.finish(); - }); + }; }); diff --git a/content/media/webaudio/test/webaudio.js b/content/media/webaudio/test/webaudio.js index 22c9b95f4715..fb42a455b661 100644 --- a/content/media/webaudio/test/webaudio.js +++ b/content/media/webaudio/test/webaudio.js @@ -24,7 +24,7 @@ function expectTypeError(func) { } function fuzzyCompare(a, b) { - return Math.abs(a - b) < 1e-5; + return Math.abs(a - b) < 5e-5; } function compareBuffers(buf1, buf2, diff --git a/content/xbl/src/nsXBLProtoImplMethod.cpp b/content/xbl/src/nsXBLProtoImplMethod.cpp index 9f001564e1ff..902dcc202917 100644 --- a/content/xbl/src/nsXBLProtoImplMethod.cpp +++ b/content/xbl/src/nsXBLProtoImplMethod.cpp @@ -312,7 +312,8 @@ nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement) // Make sure to do this before entering the compartment, since pushing Push() // may call JS_SaveFrameChain(), which puts us back in an unentered state. nsCxPusher pusher; - NS_ENSURE_STATE(pusher.Push(aBoundElement)); + if (!pusher.Push(aBoundElement)) + return NS_ERROR_UNEXPECTED; MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext()); JS::Rooted thisObject(cx, &v.toObject()); diff --git a/content/xml/document/src/XMLDocument.cpp b/content/xml/document/src/XMLDocument.cpp index 443e899b6fb3..ed3deae6fab0 100644 --- a/content/xml/document/src/XMLDocument.cpp +++ b/content/xml/document/src/XMLDocument.cpp @@ -320,8 +320,7 @@ XMLDocument::Load(const nsAString& aUrl, ErrorResult& aRv) ReportUseOfDeprecatedMethod(this, "UseOfDOM3LoadMethodWarning"); - nsCOMPtr callingDoc = - do_QueryInterface(nsContentUtils::GetDocumentFromContext()); + nsCOMPtr callingDoc = nsContentUtils::GetDocumentFromContext(); nsIURI *baseURI = mDocumentURI; nsAutoCString charset; diff --git a/content/xul/templates/src/nsXULTemplateBuilder.cpp b/content/xul/templates/src/nsXULTemplateBuilder.cpp index 5401dd38604e..a397e73e60e0 100644 --- a/content/xul/templates/src/nsXULTemplateBuilder.cpp +++ b/content/xul/templates/src/nsXULTemplateBuilder.cpp @@ -1074,8 +1074,7 @@ nsXULTemplateBuilder::Observe(nsISupports* aSubject, if (!strcmp(aTopic, DOM_WINDOW_DESTROYED_TOPIC)) { nsCOMPtr window = do_QueryInterface(aSubject); if (window) { - nsCOMPtr doc = - do_QueryInterface(window->GetExtantDocument()); + nsCOMPtr doc = window->GetExtantDoc(); if (doc && doc == mObservedDocument) NodeWillBeDestroyed(doc); } diff --git a/docshell/base/nsDSURIContentListener.cpp b/docshell/base/nsDSURIContentListener.cpp index 9d67f646e6ae..036a64a255d6 100644 --- a/docshell/base/nsDSURIContentListener.cpp +++ b/docshell/base/nsDSURIContentListener.cpp @@ -260,7 +260,7 @@ nsDSURIContentListener::SetParentContentListener(nsIURIContentListener* return NS_OK; } -bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIRequest *request, +bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIHttpChannel *httpChannel, const nsAString& policy) { static const char allowFrom[] = "allow-from"; const uint32_t allowFromLen = ArrayLength(allowFrom) - 1; @@ -273,11 +273,6 @@ bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIRequest *request, !isAllowFrom) return true; - nsCOMPtr httpChannel = do_QueryInterface(request); - if (!httpChannel) { - return true; - } - nsCOMPtr uri; httpChannel->GetURI(getter_AddRefs(uri)); @@ -402,7 +397,20 @@ bool nsDSURIContentListener::CheckOneFrameOptionsPolicy(nsIRequest *request, // in the request (comma-separated in a header, multiple headers, etc). bool nsDSURIContentListener::CheckFrameOptions(nsIRequest *request) { - nsCOMPtr httpChannel = do_QueryInterface(request); + nsresult rv; + nsCOMPtr chan = do_QueryInterface(request); + if (!chan) { + return true; + } + + nsCOMPtr httpChannel = do_QueryInterface(chan); + if (!httpChannel) { + // check if it is hiding in a multipart channel + rv = mDocShell->GetHttpChannel(chan, getter_AddRefs(httpChannel)); + if (NS_FAILED(rv)) + return false; + } + if (!httpChannel) { return true; } @@ -421,7 +429,7 @@ bool nsDSURIContentListener::CheckFrameOptions(nsIRequest *request) nsCharSeparatedTokenizer tokenizer(xfoHeaderValue, ','); while (tokenizer.hasMoreTokens()) { const nsSubstring& tok = tokenizer.nextToken(); - if (!CheckOneFrameOptionsPolicy(request, tok)) { + if (!CheckOneFrameOptionsPolicy(httpChannel, tok)) { // cancel the load and display about:blank httpChannel->Cancel(NS_BINDING_ABORTED); if (mDocShell) { diff --git a/docshell/base/nsDSURIContentListener.h b/docshell/base/nsDSURIContentListener.h index 5247c17fd382..1c17a793f363 100644 --- a/docshell/base/nsDSURIContentListener.h +++ b/docshell/base/nsDSURIContentListener.h @@ -14,6 +14,7 @@ class nsDocShell; class nsIWebNavigationInfo; +class nsIHttpChannel; class nsDSURIContentListener : public nsIURIContentListener, @@ -38,7 +39,7 @@ protected: // Determine if X-Frame-Options allows content to be framed // as a subdocument bool CheckFrameOptions(nsIRequest* request); - bool CheckOneFrameOptionsPolicy(nsIRequest* request, + bool CheckOneFrameOptionsPolicy(nsIHttpChannel* httpChannel, const nsAString& policy); enum XFOHeader { diff --git a/docshell/base/nsDocShell.cpp b/docshell/base/nsDocShell.cpp index b0a4945a5f92..ffdc798c1f1c 100644 --- a/docshell/base/nsDocShell.cpp +++ b/docshell/base/nsDocShell.cpp @@ -7048,10 +7048,7 @@ nsDocShell::CanSavePresentation(uint32_t aLoadType, // If the document does not want its presentation cached, then don't. nsCOMPtr doc = mScriptGlobal->GetExtantDoc(); - if (!doc || !doc->CanSavePresentation(aNewRequest)) - return false; - - return true; + return doc && doc->CanSavePresentation(aNewRequest); } void @@ -8677,8 +8674,7 @@ nsDocShell::InternalLoad(nsIURI * aURI, // document in |newWin|, if any. nsCOMPtr piNewWin = do_QueryInterface(newWin); if (piNewWin) { - nsCOMPtr newDoc = - do_QueryInterface(piNewWin->GetExtantDocument()); + nsCOMPtr newDoc = piNewWin->GetExtantDoc(); if (!newDoc || newDoc->IsInitialDocument()) { isNewWindow = true; aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD; diff --git a/docshell/base/nsWebNavigationInfo.cpp b/docshell/base/nsWebNavigationInfo.cpp index 6be73dce4b7b..4ff477fbd652 100644 --- a/docshell/base/nsWebNavigationInfo.cpp +++ b/docshell/base/nsWebNavigationInfo.cpp @@ -57,7 +57,7 @@ nsWebNavigationInfo::IsTypeSupported(const nsACString& aType, if (pluginHost) { // false will ensure that currently running plugins will not // be shut down - rv = pluginHost->ReloadPlugins(false); + rv = pluginHost->ReloadPlugins(); if (NS_SUCCEEDED(rv)) { // OK, we reloaded plugins and there were new ones // (otherwise NS_ERROR_PLUGINS_PLUGINSNOTCHANGED would have diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index a1c05f60c570..c751670b644a 100644 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -1969,6 +1969,7 @@ this.DOMApplicationRegistry = { _nextLocalId: function() { let id = Services.prefs.getIntPref("dom.mozApps.maxLocalId") + 1; Services.prefs.setIntPref("dom.mozApps.maxLocalId", id); + Services.prefs.savePrefFile(null); return id; }, diff --git a/dom/audiochannel/AudioChannelAgent.cpp b/dom/audiochannel/AudioChannelAgent.cpp index feadde9fe9f0..9b0cfaca4540 100644 --- a/dom/audiochannel/AudioChannelAgent.cpp +++ b/dom/audiochannel/AudioChannelAgent.cpp @@ -8,7 +8,15 @@ using namespace mozilla::dom; -NS_IMPL_ISUPPORTS1(AudioChannelAgent, nsIAudioChannelAgent) +NS_IMPL_CYCLE_COLLECTION_1(AudioChannelAgent, mCallback) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AudioChannelAgent) + NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgent) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(AudioChannelAgent) +NS_IMPL_CYCLE_COLLECTING_RELEASE(AudioChannelAgent) AudioChannelAgent::AudioChannelAgent() : mAudioChannelType(AUDIO_AGENT_CHANNEL_ERROR) diff --git a/dom/audiochannel/AudioChannelAgent.h b/dom/audiochannel/AudioChannelAgent.h index 9704561f77fd..a4900f72b337 100644 --- a/dom/audiochannel/AudioChannelAgent.h +++ b/dom/audiochannel/AudioChannelAgent.h @@ -8,6 +8,7 @@ #define mozilla_dom_audio_channel_agent_h__ #include "nsIAudioChannelAgent.h" +#include "nsCycleCollectionParticipant.h" #include "nsCOMPtr.h" #include "nsWeakPtr.h" @@ -23,9 +24,11 @@ namespace dom { class AudioChannelAgent : public nsIAudioChannelAgent { public: - NS_DECL_ISUPPORTS + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_NSIAUDIOCHANNELAGENT + NS_DECL_CYCLE_COLLECTION_CLASS(AudioChannelAgent) + AudioChannelAgent(); virtual void NotifyAudioChannelStateChanged(); diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index ecf11f09c01b..4d4eaa61bab5 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -481,7 +481,7 @@ Navigator::GetCookieEnabled(bool* aCookieEnabled) return NS_OK; } - nsCOMPtr doc = do_QueryInterface(win->GetExtantDocument()); + nsCOMPtr doc = win->GetExtantDoc(); if (!doc) { return NS_OK; } @@ -626,17 +626,16 @@ namespace { class VibrateWindowListener : public nsIDOMEventListener { public: - VibrateWindowListener(nsIDOMWindow *aWindow, nsIDOMDocument *aDocument) + VibrateWindowListener(nsIDOMWindow* aWindow, nsIDocument* aDocument) { mWindow = do_GetWeakReference(aWindow); mDocument = do_GetWeakReference(aDocument); - nsCOMPtr target = do_QueryInterface(aDocument); NS_NAMED_LITERAL_STRING(visibilitychange, "visibilitychange"); - target->AddSystemEventListener(visibilitychange, - this, /* listener */ - true, /* use capture */ - false /* wants untrusted */); + aDocument->AddSystemEventListener(visibilitychange, + this, /* listener */ + true, /* use capture */ + false /* wants untrusted */); } virtual ~VibrateWindowListener() @@ -754,12 +753,9 @@ Navigator::Vibrate(const JS::Value& aPattern, JSContext* cx) nsCOMPtr win = do_QueryReferent(mWindow); NS_ENSURE_TRUE(win, NS_OK); - nsCOMPtr domDoc = win->GetExtantDocument(); - NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE); - - bool hidden = true; - domDoc->GetHidden(&hidden); - if (hidden) { + nsCOMPtr doc = win->GetExtantDoc(); + NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); + if (doc->Hidden()) { // Hidden documents cannot start or stop a vibration. return NS_OK; } @@ -819,7 +815,7 @@ Navigator::Vibrate(const JS::Value& aPattern, JSContext* cx) else { gVibrateWindowListener->RemoveListener(); } - gVibrateWindowListener = new VibrateWindowListener(win, domDoc); + gVibrateWindowListener = new VibrateWindowListener(win, doc); nsCOMPtr domWindow = do_QueryInterface(static_cast(win)); diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index fd36a09d4a14..f71dc86d2df1 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -3268,7 +3268,7 @@ nsWindowSH::GlobalScopePolluterNewResolve(JSContext *cx, JSHandleObject obj, // The rest of this function is for HTML documents only. // nsCOMPtr htmlDoc = - do_QueryInterface(win->GetExtantDocument()); + do_QueryInterface(win->GetExtantDoc()); if (!htmlDoc) return true; nsHTMLDocument *document = static_cast(htmlDoc.get()); @@ -6200,7 +6200,7 @@ nsDocumentSH::PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx, return NS_OK; } - nsIDOMDocument* currentDoc = win->GetExtantDocument(); + nsIDocument* currentDoc = win->GetExtantDoc(); if (SameCOMIdentity(doc, currentDoc)) { jsval winVal; diff --git a/dom/base/nsDOMException.cpp b/dom/base/nsDOMException.cpp index c203bac61ac7..3f9f710cb5a6 100644 --- a/dom/base/nsDOMException.cpp +++ b/dom/base/nsDOMException.cpp @@ -183,8 +183,7 @@ nsDOMException::GetCode(uint16_t* aCode) // Warn only when the code was changed (other than DOM Core) // or the code is useless (zero) if (NS_ERROR_GET_MODULE(mResult) != NS_ERROR_MODULE_DOM || !mCode) { - nsCOMPtr doc = - do_QueryInterface(nsContentUtils::GetDocumentFromCaller()); + nsCOMPtr doc = nsContentUtils::GetDocumentFromCaller(); if (doc) { doc->WarnOnceAbout(nsIDocument::eDOMExceptionCode); } diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index b7556076478c..a5e7c4c2530d 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -188,14 +188,14 @@ nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName, nsCOMPtr window = do_QueryReferent(mWindow); if (window) { - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); if (doc) { nsCOMPtr name = do_GetAtom(aName); doc->GetHeaderData(name, aValue); return NS_OK; } } - + aValue.Truncate(); return NS_OK; } @@ -266,7 +266,7 @@ nsDOMWindowUtils::GetViewportInfo(uint32_t aDisplayWidth, nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_STATE(doc); nsViewportInfo info = nsContentUtils::GetViewportInfo(doc, aDisplayWidth, aDisplayHeight); @@ -1247,7 +1247,7 @@ nsDOMWindowUtils::ElementFromPoint(float aX, float aY, nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_STATE(doc); Element* el = @@ -1272,7 +1272,7 @@ nsDOMWindowUtils::NodesFromRect(float aX, float aY, nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_STATE(doc); return doc->NodesFromRectHelper(aX, aY, aTopSize, aRightSize, aBottomSize, aLeftSize, @@ -1424,7 +1424,7 @@ nsDOMWindowUtils::SuppressEventHandling(bool aSuppress) nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); if (aSuppress) { @@ -1446,7 +1446,7 @@ nsDOMWindowUtils::GetScrollXY(bool aFlushLayout, int32_t* aScrollX, int32_t* aSc nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_STATE(doc); if (aFlushLayout) { @@ -2460,15 +2460,15 @@ nsDOMWindowUtils::GetCursorType(int16_t *aCursor) NS_ENSURE_ARG_POINTER(aCursor); nsCOMPtr window = do_QueryReferent(mWindow); - NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); bool isSameDoc = false; - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); do { - if (nsEventStateManager::sMouseOverDocument == doc.get()) { + if (nsEventStateManager::sMouseOverDocument == doc) { isSameDoc = true; break; } @@ -2937,17 +2937,13 @@ nsDOMWindowUtils::GetPlugins(JSContext* cx, JS::Value* aPlugins) nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsIDOMDocument* ddoc = window->GetExtantDocument(); - - nsresult rv; - nsCOMPtr doc = do_QueryInterface(ddoc, &rv); - NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr doc = window->GetExtantDoc(); nsTArray plugins; doc->GetPlugins(plugins); JSObject* jsPlugins = nullptr; - rv = nsTArrayToJSArray(cx, plugins, &jsPlugins); + nsresult rv = nsTArrayToJSArray(cx, plugins, &jsPlugins); NS_ENSURE_SUCCESS(rv, rv); *aPlugins = OBJECT_TO_JSVAL(jsPlugins); @@ -3014,7 +3010,7 @@ nsDOMWindowUtils::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement, nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_STATE(doc); doc->RemoteFrameFullscreenChanged(aFrameElement, aNewOrigin); @@ -3031,7 +3027,7 @@ nsDOMWindowUtils::RemoteFrameFullscreenReverted() nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); NS_ENSURE_STATE(doc); doc->RemoteFrameFullscreenReverted(); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index bbac0e89bf2b..9d62e6c7346c 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -168,6 +168,7 @@ #endif #include "xpcprivate.h" +#include "nsDOMEvent.h" #ifdef NS_PRINTING #include "nsIPrintSettings.h" @@ -1213,7 +1214,6 @@ nsGlobalWindow::~nsGlobalWindow() } } - mDocument = nullptr; // Forces Release mDoc = nullptr; NS_ASSERTION(!mArguments, "mArguments wasn't cleaned up properly!"); @@ -1465,9 +1465,7 @@ nsGlobalWindow::FreeInnerObjects() mScreen = nullptr; } - if (mDocument) { - NS_ASSERTION(mDoc, "Why is mDoc null?"); - + if (mDoc) { // Remember the document's principal and URI. mDocumentPrincipal = mDoc->NodePrincipal(); mDocumentURI = mDoc->GetDocumentURI(); @@ -1475,7 +1473,6 @@ nsGlobalWindow::FreeInnerObjects() } // Remove our reference to the document and the document principal. - mDocument = nullptr; mDoc = nullptr; mFocusedNode = nullptr; @@ -1647,7 +1644,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsGlobalWindow) // Traverse stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrameElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioContexts) @@ -1694,7 +1690,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow) // Unlink stuff from nsPIDOMWindow NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler) NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrameElement) NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedNode) NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioContexts) @@ -2143,8 +2138,8 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, } NS_ASSERTION(!GetCurrentInnerWindow() || - GetCurrentInnerWindow()->GetExtantDocument() == mDocument, - "Uh, mDocument doesn't match the current inner window " + GetCurrentInnerWindow()->GetExtantDoc() == mDoc, + "Uh, mDoc doesn't match the current inner window " "document!"); bool wouldReuseInnerWindow = WouldReuseInnerWindow(aDocument); @@ -2156,7 +2151,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, return NS_ERROR_UNEXPECTED; } - nsCOMPtr oldDoc(do_QueryInterface(mDocument)); + nsCOMPtr oldDoc = mDoc; nsIScriptContext *scx = GetContextInternal(); NS_ENSURE_TRUE(scx, NS_ERROR_NOT_INITIALIZED); @@ -2171,7 +2166,7 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, } #endif - if (!mDocument) { + if (!mDoc) { // First document load. // Get our private root. If it is equal to us, then we need to @@ -2198,10 +2193,9 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, nsresult rv = NS_OK; - // Set mDocument even if this is an outer window to avoid + // Set mDoc even if this is an outer window to avoid // having to *always* reach into the inner window to find the // document. - mDocument = do_QueryInterface(aDocument); mDoc = aDocument; #ifdef DEBUG @@ -2471,7 +2465,6 @@ nsGlobalWindow::SetNewDocument(nsIDocument* aDocument, if (!aState) { if (reUseInnerWindow) { if (newInnerWindow->mDoc != aDocument) { - newInnerWindow->mDocument = do_QueryInterface(aDocument); newInnerWindow->mDoc = aDocument; // We're reusing the inner window for a new document. In this @@ -2559,12 +2552,12 @@ nsGlobalWindow::PreloadLocalStorage() void nsGlobalWindow::DispatchDOMWindowCreated() { - if (!mDoc || !mDocument) { + if (!mDoc) { return; } // Fire DOMWindowCreated at chrome event listeners - nsContentUtils::DispatchChromeEvent(mDoc, mDocument, NS_LITERAL_STRING("DOMWindowCreated"), + nsContentUtils::DispatchChromeEvent(mDoc, mDoc, NS_LITERAL_STRING("DOMWindowCreated"), true /* bubbles */, false /* not cancellable */); @@ -2605,7 +2598,6 @@ nsGlobalWindow::InnerSetNewDocument(nsIDocument* aDocument) } #endif - mDocument = do_QueryInterface(aDocument); mDoc = aDocument; mFocusedNode = nullptr; mLocalStorage = nullptr; @@ -2701,14 +2693,13 @@ nsGlobalWindow::DetachFromDocShell() if (currentInner) { NS_ASSERTION(mDoc, "Must have doc!"); - + // Remember the document's principal and URI. mDocumentPrincipal = mDoc->NodePrincipal(); mDocumentURI = mDoc->GetDocumentURI(); mDocBaseURI = mDoc->GetDocBaseURI(); // Release our document reference - mDocument = nullptr; mDoc = nullptr; mFocusedNode = nullptr; } @@ -3043,8 +3034,7 @@ nsGlobalWindow::PostHandleEvent(nsEventChainPostVisitor& aVisitor) aVisitor.mEvent->mFlags.mIsTrusted) { // Execute bindingdetached handlers before we tear ourselves // down. - if (mDocument) { - NS_ASSERTION(mDoc, "Must have doc"); + if (mDoc) { mDoc->BindingManager()->ExecuteDetachedHandlers(); } mIsDocumentLoaded = false; @@ -3229,7 +3219,7 @@ nsPIDOMWindow::MaybeCreateDoc() { MOZ_ASSERT(!mDoc); if (nsIDocShell* docShell = GetDocShell()) { - // Note that |document| here is the same thing as our mDocument, but we + // Note that |document| here is the same thing as our mDoc, but we // don't have to explicitly set the member variable because the docshell // has already called SetNewDocument(). nsCOMPtr document = do_GetInterface(docShell); @@ -3745,9 +3735,8 @@ nsGlobalWindow::GetApplicationCache(nsIDOMOfflineResourceList **aApplicationCach nsresult rv = webNav->GetCurrentURI(getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr doc = do_QueryInterface(mDocument); nsCOMPtr manifestURI; - nsContentUtils::GetOfflineAppManifest(doc, getter_AddRefs(manifestURI)); + nsContentUtils::GetOfflineAppManifest(mDoc, getter_AddRefs(manifestURI)); nsRefPtr applicationCache = new nsDOMOfflineResourceList(manifestURI, uri, this); @@ -4520,8 +4509,7 @@ nsGlobalWindow::CheckSecurityWidthAndHeight(int32_t* aWidth, int32_t* aHeight) #ifdef MOZ_XUL if (!nsContentUtils::IsCallerChrome()) { // if attempting to resize the window, hide any open popups - nsCOMPtr doc(do_QueryInterface(mDocument)); - nsContentUtils::HidePopupsInDocument(doc); + nsContentUtils::HidePopupsInDocument(mDoc); } #endif @@ -4586,8 +4574,7 @@ nsGlobalWindow::CheckSecurityLeftAndTop(int32_t* aLeft, int32_t* aTop) if (!nsContentUtils::IsCallerChrome()) { #ifdef MOZ_XUL // if attempting to move the window, hide any open popups - nsCOMPtr doc(do_QueryInterface(mDocument)); - nsContentUtils::HidePopupsInDocument(doc); + nsContentUtils::HidePopupsInDocument(mDoc); #endif nsGlobalWindow* rootWindow = @@ -4798,8 +4785,7 @@ bool nsGlobalWindow::DispatchCustomEvent(const char *aEventName) { bool defaultActionEnabled = true; - nsCOMPtr doc(do_QueryInterface(mDocument)); - nsContentUtils::DispatchTrustedEvent(doc, + nsContentUtils::DispatchTrustedEvent(mDoc, GetOuterWindow(), NS_ConvertASCIItoUTF16(aEventName), true, true, &defaultActionEnabled); @@ -4961,8 +4947,7 @@ nsGlobalWindow::SetFullScreenInternal(bool aFullScreen, bool aRequireTrust) // DOM full-screen mode and the user exits full-screen mode with // the browser full-screen mode toggle keyboard-shortcut, we'll detect // that and leave DOM API full-screen mode too. - nsCOMPtr doc(do_QueryInterface(mDocument)); - nsIDocument::ExitFullscreen(doc, /* async */ false); + nsIDocument::ExitFullscreen(mDoc, /* async */ false); } if (!mWakeLock && mFullScreen) { @@ -5516,10 +5501,8 @@ nsGlobalWindow::Focus() mDocShell->GetItemType(&itemType); if (itemType == nsIDocShellTreeItem::typeChrome && GetPrivateRoot() == static_cast(this) && - mDocument) { - nsCOMPtr doc(do_QueryInterface(mDocument)); - NS_ASSERTION(doc, "Bogus doc?"); - nsIURI* ourURI = doc->GetDocumentURI(); + mDoc) { + nsIURI* ourURI = mDoc->GetDocumentURI(); if (ourURI) { lookForPresShell = !NS_IsAboutBlank(ourURI); } @@ -5542,8 +5525,7 @@ nsGlobalWindow::Focus() if (!parentdoc) return NS_OK; - nsCOMPtr doc = do_QueryInterface(mDocument); - nsIContent* frame = parentdoc->FindContentForSubDocument(doc); + nsIContent* frame = parentdoc->FindContentForSubDocument(mDoc); nsCOMPtr frameElement = do_QueryInterface(frame); if (frameElement) { uint32_t flags = nsIFocusManager::FLAG_NOSCROLL; @@ -5585,12 +5567,11 @@ nsGlobalWindow::Blur() // if the root is focused, clear the focus nsIFocusManager* fm = nsFocusManager::GetFocusManager(); - nsCOMPtr doc = do_QueryInterface(mDocument); - if (fm && mDocument) { + if (fm && mDoc) { nsCOMPtr element; fm->GetFocusedElementForWindow(this, false, nullptr, getter_AddRefs(element)); nsCOMPtr content = do_QueryInterface(element); - if (content == doc->GetRootElement()) + if (content == mDoc->GetRootElement()) fm->ClearFocus(this); } } @@ -6099,7 +6080,7 @@ nsGlobalWindow::SetResizable(bool aResizable) static void ReportUseOfDeprecatedMethod(nsGlobalWindow* aWindow, const char* aWarning) { - nsCOMPtr doc = do_QueryInterface(aWindow->GetExtantDocument()); + nsCOMPtr doc = aWindow->GetExtantDoc(); nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM Events", doc, nsContentUtils::eDOM_PROPERTIES, @@ -6140,7 +6121,7 @@ nsGlobalWindow::DisableExternalCapture() } static -bool IsPopupBlocked(nsIDOMDocument* aDoc) +bool IsPopupBlocked(nsIDocument* aDoc) { nsCOMPtr pm = do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID); @@ -6149,20 +6130,18 @@ bool IsPopupBlocked(nsIDOMDocument* aDoc) return false; } - bool blocked = true; - nsCOMPtr doc(do_QueryInterface(aDoc)); - - if (doc) { - uint32_t permission = nsIPopupWindowManager::ALLOW_POPUP; - pm->TestPermission(doc->NodePrincipal(), &permission); - blocked = (permission == nsIPopupWindowManager::DENY_POPUP); + if (!aDoc) { + return true; } - return blocked; + + uint32_t permission = nsIPopupWindowManager::ALLOW_POPUP; + pm->TestPermission(aDoc->NodePrincipal(), &permission); + return permission == nsIPopupWindowManager::DENY_POPUP; } /* static */ -void -nsGlobalWindow::FirePopupBlockedEvent(nsIDOMDocument* aDoc, +void +nsGlobalWindow::FirePopupBlockedEvent(nsIDocument* aDoc, nsIDOMWindow *aRequestingWindow, nsIURI *aPopupURI, const nsAString &aPopupWindowName, const nsAString &aPopupWindowFeatures) @@ -6170,9 +6149,9 @@ nsGlobalWindow::FirePopupBlockedEvent(nsIDOMDocument* aDoc, if (aDoc) { // Fire a "DOMPopupBlocked" event so that the UI can hear about // blocked popups. + nsCOMPtr doc = do_QueryInterface(aDoc); nsCOMPtr event; - aDoc->CreateEvent(NS_LITERAL_STRING("PopupBlockedEvents"), - getter_AddRefs(event)); + doc->CreateEvent(NS_LITERAL_STRING("PopupBlockedEvents"), getter_AddRefs(event)); if (event) { nsCOMPtr pbev(do_QueryInterface(event)); pbev->InitPopupBlockedEvent(NS_LITERAL_STRING("DOMPopupBlocked"), @@ -6181,9 +6160,8 @@ nsGlobalWindow::FirePopupBlockedEvent(nsIDOMDocument* aDoc, aPopupWindowFeatures); event->SetTrusted(true); - nsCOMPtr targ(do_QueryInterface(aDoc)); bool defaultActionEnabled; - targ->DispatchEvent(event, &defaultActionEnabled); + aDoc->DispatchEvent(event, &defaultActionEnabled); } } } @@ -6214,7 +6192,7 @@ nsGlobalWindow::CanSetProperty(const char *aPrefName) bool nsGlobalWindow::PopupWhitelisted() { - if (!IsPopupBlocked(mDocument)) + if (!IsPopupBlocked(mDoc)) return true; nsCOMPtr parent; @@ -6324,9 +6302,11 @@ nsGlobalWindow::FireAbuseEvents(bool aBlocked, bool aWindow, getter_AddRefs(popupURI)); // fire an event chock full of informative URIs - if (aBlocked) - FirePopupBlockedEvent(topDoc, this, popupURI, aPopupWindowName, + if (aBlocked) { + nsCOMPtr topDocument = do_QueryInterface(topDoc); + FirePopupBlockedEvent(topDocument, this, popupURI, aPopupWindowName, aPopupWindowFeatures); + } if (aWindow) FirePopupWindowEvent(topDoc); } @@ -6756,12 +6736,13 @@ PostMessageEvent::Run() } // Create the event - nsCOMPtr domDoc = do_QueryInterface(targetWindow->mDocument); - if (!domDoc) + nsIDocument* doc = targetWindow->mDoc; + if (!doc) return NS_OK; + + nsCOMPtr domDoc = do_QueryInterface(doc); nsCOMPtr event; - domDoc->CreateEvent(NS_LITERAL_STRING("MessageEvent"), - getter_AddRefs(event)); + domDoc->CreateEvent(NS_LITERAL_STRING("MessageEvent"), getter_AddRefs(event)); if (!event) return NS_OK; @@ -6853,7 +6834,7 @@ nsGlobalWindow::PostMessageMoz(const JS::Value& aMessage, } else if (callerInnerWin) { // otherwise use the URI of the document to generate origin - nsCOMPtr doc = do_QueryInterface(callerInnerWin->GetExtantDocument()); + nsCOMPtr doc = callerInnerWin->GetExtantDoc(); if (!doc) return NS_OK; callerOuterURI = doc->GetDocumentURI(); @@ -7173,7 +7154,7 @@ nsGlobalWindow::EnterModalState() if (topWin->mModalStateDepth == 0) { NS_ASSERTION(!mSuspendedDoc, "Shouldn't have mSuspendedDoc here!"); - mSuspendedDoc = do_QueryInterface(topWin->GetExtantDocument()); + mSuspendedDoc = topWin->GetExtantDoc(); if (mSuspendedDoc && mSuspendedDoc->EventHandlingSuppressed()) { mSuspendedDoc->SuppressEventHandling(); } else { @@ -7286,8 +7267,7 @@ nsGlobalWindow::LeaveModalState(nsIDOMWindow *aCallerWin) NS_WARNING("failed to dispatch pending timeout runnable"); if (mSuspendedDoc) { - nsCOMPtr currentDoc = - do_QueryInterface(topWin->GetExtantDocument()); + nsCOMPtr currentDoc = topWin->GetExtantDoc(); mSuspendedDoc->UnsuppressEventHandlingAndFireEvents(currentDoc == mSuspendedDoc); mSuspendedDoc = nullptr; } @@ -7777,7 +7757,7 @@ nsGlobalWindow::UpdateCommands(const nsAString& anAction) return NS_OK; nsCOMPtr xulDoc = - do_QueryInterface(rootWindow->GetExtantDocument()); + do_QueryInterface(rootWindow->GetExtantDoc()); // See if we contain a XUL document. if (xulDoc) { // Retrieve the command dispatcher and call updateCommands on it. @@ -8694,11 +8674,10 @@ nsGlobalWindow::UpdateCanvasFocus(bool aFocusChanged, nsIContent* aNewContent) return; nsCOMPtr presShell = docShell->GetPresShell(); - if (!presShell || !mDocument) + if (!presShell || !mDoc) return; - nsCOMPtr doc(do_QueryInterface(mDocument)); - Element *rootElement = doc->GetRootElement(); + Element *rootElement = mDoc->GetRootElement(); if (rootElement) { if ((mHasFocus || aFocusChanged) && (mFocusedNode == rootElement || aNewContent == rootElement)) { @@ -8836,8 +8815,8 @@ nsGlobalWindow::GetSessionStorage(nsIDOMStorage ** aSessionStorage) *aSessionStorage = nullptr; nsString documentURI; - if (mDocument) { - mDocument->GetDocumentURI(documentURI); + if (mDoc) { + mDoc->GetDocumentURI(documentURI); } // If the document has the sandboxed origin flag set @@ -8921,8 +8900,8 @@ nsGlobalWindow::GetLocalStorage(nsIDOMStorage ** aLocalStorage) } nsString documentURI; - if (mDocument) { - mDocument->GetDocumentURI(documentURI); + if (mDoc) { + mDoc->GetDocumentURI(documentURI); } nsIDocShell* docShell = GetDocShell(); @@ -9095,9 +9074,8 @@ nsGlobalWindow::FireOfflineStatusEvent() } } else { - nsCOMPtr documentElement; - mDocument->GetDocumentElement(getter_AddRefs(documentElement)); - if(documentElement) { + Element* documentElement = mDoc->GetDocumentElement(); + if(documentElement) { eventTarget = documentElement; } } @@ -9939,10 +9917,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, #ifdef DEBUG_jst { nsCOMPtr pidomwin(do_QueryInterface(*aReturn)); - - nsIDOMDocument *temp = pidomwin->GetExtantDocument(); - - NS_ASSERTION(temp, "No document in new window!!!"); + NS_ASSERTION(pidomwin->GetExtantDoc(), "No document in new window!!!"); } #endif @@ -9950,7 +9925,7 @@ nsGlobalWindow::OpenInternal(const nsAString& aUrl, const nsAString& aName, (*aReturn)->GetDocument(getter_AddRefs(doc)); } } - + if (checkForPopup) { if (abuseLevel >= openControlled) { nsGlobalWindow *opened = static_cast(*aReturn); @@ -9998,7 +9973,7 @@ nsGlobalWindow::SetTimeoutOrInterval(nsIScriptTimeoutHandler *aHandler, // If we don't have a document (we could have been unloaded since // the call to setTimeout was made), do nothing. - if (!mDocument) { + if (!mDoc) { return NS_OK; } @@ -10821,8 +10796,8 @@ nsGlobalWindow::BuildURIfromBase(const char *aURL, nsIURI **aBuiltURI, // get JSContext NS_ASSERTION(scx, "opening window missing its context"); - NS_ASSERTION(mDocument, "opening window missing its document"); - if (!scx || !mDocument) + NS_ASSERTION(mDoc, "opening window missing its document"); + if (!scx || !mDoc) return NS_ERROR_FAILURE; nsCOMPtr chrome_win = do_QueryObject(this); diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 336896427fe3..5769b9bfb81b 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -584,7 +584,7 @@ public: mCleanedUp); } - static void FirePopupBlockedEvent(nsIDOMDocument* aDoc, + static void FirePopupBlockedEvent(nsIDocument* aDoc, nsIDOMWindow *aRequestingWindow, nsIURI *aPopupURI, const nsAString &aPopupWindowName, const nsAString &aPopupWindowFeatures); diff --git a/dom/base/nsGlobalWindowCommands.cpp b/dom/base/nsGlobalWindowCommands.cpp index e9917bd6826d..a4571b4e4a54 100644 --- a/dom/base/nsGlobalWindowCommands.cpp +++ b/dom/base/nsGlobalWindowCommands.cpp @@ -345,7 +345,7 @@ nsClipboardCommand::IsCommandEnabled(const char* aCommandName, nsISupports *aCon nsCOMPtr window = do_QueryInterface(aContext); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); - nsCOMPtr doc = do_QueryInterface(window->GetExtantDocument()); + nsCOMPtr doc = window->GetExtantDoc(); *outCmdEnabled = nsCopySupport::CanCopy(doc); return NS_OK; } diff --git a/dom/base/nsHistory.cpp b/dom/base/nsHistory.cpp index 65c8c5f4312b..d2f08a0a99b3 100644 --- a/dom/base/nsHistory.cpp +++ b/dom/base/nsHistory.cpp @@ -231,8 +231,7 @@ nsHistory::Go(int32_t aDelta) // trick to work around gecko reflow bugs, and this should have // the same effect. - nsCOMPtr doc = - do_QueryInterface(window->GetExtantDocument()); + nsCOMPtr doc = window->GetExtantDoc(); nsIPresShell *shell; nsPresContext *pcx; @@ -338,8 +337,7 @@ nsHistory::GetState(nsIVariant **aState) if (!nsContentUtils::CanCallerAccess(win->GetOuterWindow())) return NS_ERROR_DOM_SECURITY_ERR; - nsCOMPtr doc = - do_QueryInterface(win->GetExtantDocument()); + nsCOMPtr doc = win->GetExtantDoc(); if (!doc) return NS_ERROR_NOT_AVAILABLE; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 40e8d2e8a0f9..abeca0165732 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -620,7 +620,7 @@ PrintWinURI(nsGlobalWindow *win) return; } - nsCOMPtr doc = do_QueryInterface(win->GetExtantDocument()); + nsCOMPtr doc = win->GetExtantDoc(); if (!doc) { printf("No document in the window.\n"); return; diff --git a/dom/base/nsJSTimeoutHandler.cpp b/dom/base/nsJSTimeoutHandler.cpp index 740eb46c5375..d5d4147639f6 100644 --- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -241,7 +241,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval, if (expr) { // if CSP is enabled, and setTimeout/setInterval was called with a string // or object, disable the registration and log an error - nsCOMPtr doc = do_QueryInterface(aWindow->GetExtantDocument()); + nsCOMPtr doc = aWindow->GetExtantDoc(); if (doc) { nsCOMPtr csp; diff --git a/dom/base/nsLocation.cpp b/dom/base/nsLocation.cpp index e2d7b7820de7..6d26b31f1035 100644 --- a/dom/base/nsLocation.cpp +++ b/dom/base/nsLocation.cpp @@ -787,7 +787,7 @@ nsLocation::Reload(bool aForceget) // page since some sites may use this trick to work around gecko // reflow bugs, and this should have the same effect. - nsCOMPtr doc(do_QueryInterface(window->GetExtantDocument())); + nsCOMPtr doc = window->GetExtantDoc(); nsIPresShell *shell; nsPresContext *pcx; diff --git a/dom/base/nsPIDOMWindow.h b/dom/base/nsPIDOMWindow.h index 92ec99f3b783..d891dca59561 100644 --- a/dom/base/nsPIDOMWindow.h +++ b/dom/base/nsPIDOMWindow.h @@ -172,11 +172,6 @@ public: virtual void MaybeUpdateTouchState() {} virtual void UpdateTouchState() {} - // GetExtantDocument provides a backdoor to the DOM GetDocument accessor - nsIDOMDocument* GetExtantDocument() const - { - return mDocument; - } nsIDocument* GetExtantDoc() const { return mDoc; @@ -675,8 +670,7 @@ protected: // value on both the outer window and the current inner window. Make // sure you keep them in sync! nsCOMPtr mChromeEventHandler; // strong - nsCOMPtr mDocument; // strong - nsCOMPtr mDoc; // strong, for fast access + nsCOMPtr mDoc; // strong // Cache the URI when mDoc is cleared. nsCOMPtr mDocumentURI; // strong nsCOMPtr mDocBaseURI; // strong diff --git a/dom/base/nsPluginArray.cpp b/dom/base/nsPluginArray.cpp index acda27a29b12..23a2537b395a 100644 --- a/dom/base/nsPluginArray.cpp +++ b/dom/base/nsPluginArray.cpp @@ -211,7 +211,7 @@ nsPluginArray::Refresh(bool aReloadDocuments) if(mPluginHost) { res = GetLength(¤tPluginCount); NS_ENSURE_SUCCESS(res, res); - nsresult reloadResult = mPluginHost->ReloadPlugins(aReloadDocuments); + nsresult reloadResult = mPluginHost->ReloadPlugins(); // currentPluginCount is as reported by nsPluginHost. mPluginCount is // essentially a cache of this value, and may be out of date. pluginsNotChanged = (reloadResult == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED && diff --git a/dom/base/nsWindowMemoryReporter.cpp b/dom/base/nsWindowMemoryReporter.cpp index 6fe4cc7accb4..eb70158f70af 100644 --- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -58,7 +58,7 @@ GetWindowURI(nsIDOMWindow *aWindow) nsCOMPtr pWindow = do_QueryInterface(aWindow); NS_ENSURE_TRUE(pWindow, NULL); - nsCOMPtr doc = do_QueryInterface(pWindow->GetExtantDocument()); + nsCOMPtr doc = pWindow->GetExtantDoc(); nsCOMPtr uri; if (doc) { diff --git a/dom/bluetooth/BluetoothOppManager.cpp b/dom/bluetooth/BluetoothOppManager.cpp index 7847e768d66d..aec315cdadfc 100644 --- a/dom/bluetooth/BluetoothOppManager.cpp +++ b/dom/bluetooth/BluetoothOppManager.cpp @@ -29,7 +29,9 @@ #include "nsIOutputStream.h" #include "nsNetUtil.h" -#define TARGET_FOLDER "/sdcard/downloads/bluetooth/" +#define TARGET_ROOT "/sdcard/" +#define TARGET_SUBDIR "downloads/bluetooth/" +#define TARGET_FOLDER TARGET_ROOT TARGET_SUBDIR USING_BLUETOOTH_NAMESPACE using namespace mozilla; @@ -561,6 +563,11 @@ BluetoothOppManager::CreateFile() */ f->GetLeafName(sFileName); + nsString fullFileName; + f->GetPath(fullFileName); + MOZ_ASSERT(StringBeginsWith(fullFileName, NS_LITERAL_STRING(TARGET_ROOT))); + nsDependentSubstring storagePath = Substring(fullFileName, strlen(TARGET_ROOT)); + mDsFile = nullptr; nsCOMPtr mimeSvc = do_GetService(NS_MIMESERVICE_CONTRACTID); @@ -570,11 +577,11 @@ BluetoothOppManager::CreateFile() if (NS_SUCCEEDED(rv)) { if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("image/"))) { - mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("pictures"), f); + mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("pictures"), storagePath); } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("video/"))) { - mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("movies"), f); + mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("movies"), storagePath); } else if (StringBeginsWith(mimeType, NS_LITERAL_CSTRING("audio/"))) { - mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("music"), f); + mDsFile = new DeviceStorageFile(NS_LITERAL_STRING("music"), storagePath); } else { NS_WARNING("Couldn't recognize the mimetype of received file."); } diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index bf5a08ffc886..85907d7ca0f0 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -905,7 +905,8 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) */ nsCOMPtr filename = aStartRecording->mFolder; filename->AppendRelativePath(aStartRecording->mFilename); - mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"), filename); + mVideoFile = new DeviceStorageFile(NS_LITERAL_STRING("videos"), + aStartRecording->mFilename); nsAutoCString nativeFilename; filename->GetNativePath(nativeFilename); diff --git a/dom/contacts/fallback/ContactDB.jsm b/dom/contacts/fallback/ContactDB.jsm index b85815b104a5..0b2441632279 100644 --- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -26,62 +26,73 @@ const CHUNK_SIZE = 20; const CHUNK_INTERVAL = 500; function ContactDispatcher(aContacts, aFullContacts, aCallback, aNewTxn, aClearDispatcher) { - this.nextIndex = 0; + let nextIndex = 0; + let interval; - this.cancelTimeout = function() { - if (this.interval) { - clearTimeout(this.interval); - this.interval = null; + function cancelTimeout() { + if (interval) { + clearTimeout(interval); + interval = null; } - }; + } + let sendChunk; + let count = 0; if (aFullContacts) { - this.sendChunk = function() { - if (aContacts.length > 0) { - aCallback(aContacts.splice(0, CHUNK_SIZE)); - this.interval = setTimeout(this.sendChunk, CHUNK_INTERVAL); - } else { - aCallback(null); - this.cancelTimeout(); + sendChunk = function() { + try { + if (aContacts.length > 0) { + aCallback(aContacts.splice(0, CHUNK_SIZE)); + interval = setTimeout(sendChunk, CHUNK_INTERVAL); + } else { + aCallback(null); + cancelTimeout(); + aClearDispatcher(); + } + } catch (e) { aClearDispatcher(); } - }.bind(this); + } } else { this.count = 0; - this.sendChunk = function() { - let chunk = []; - aNewTxn("readonly", STORE_NAME, function(txn, store) { - for (let i = this.nextIndex; i < Math.min(this.nextIndex+CHUNK_SIZE, aContacts.length); ++i) { - store.get(aContacts[i]).onsuccess = function(e) { - chunk.push(e.target.result); - this.count++; - if (this.count == aContacts.length) { - aCallback(chunk) - aCallback(null); - this.cancelTimeout(); - aClearDispatcher(); - } else if (chunk.length == CHUNK_SIZE) { - aCallback(chunk); - chunk.length = 0; - this.nextIndex += CHUNK_SIZE; - this.interval = setTimeout(this.sendChunk, CHUNK_INTERVAL); + sendChunk = function() { + try { + let chunk = []; + aNewTxn("readonly", STORE_NAME, function(txn, store) { + for (let i = nextIndex; i < Math.min(nextIndex+CHUNK_SIZE, aContacts.length); ++i) { + store.get(aContacts[i]).onsuccess = function(e) { + chunk.push(e.target.result); + count++; + if (count === aContacts.length) { + aCallback(chunk) + aCallback(null); + cancelTimeout(); + aClearDispatcher(); + } else if (chunk.length === CHUNK_SIZE) { + aCallback(chunk); + chunk.length = 0; + nextIndex += CHUNK_SIZE; + interval = setTimeout(this.sendChunk, CHUNK_INTERVAL); + } } - }.bind(this); - } - }.bind(this)); - }.bind(this); + } + }); + } catch (e) { + aClearDispatcher(); + } + } } - this.sendChunk(0); + sendChunk(0); + + return { + sendNow: function() { + cancelTimeout(); + interval = setTimeout(sendChunk, 0); + } + }; } -ContactDispatcher.prototype = { - sendNow: function() { - this.cancelTimeout(); - this.interval = setTimeout(this.sendChunk, 0); - } -}; - this.ContactDB = function ContactDB(aGlobal) { if (DEBUG) debug("Constructor"); this._global = aGlobal; diff --git a/dom/contacts/fallback/ContactService.jsm b/dom/contacts/fallback/ContactService.jsm index b67633d217b7..73b76dfb4d5d 100644 --- a/dom/contacts/fallback/ContactService.jsm +++ b/dom/contacts/fallback/ContactService.jsm @@ -114,7 +114,12 @@ let ContactService = { } this._db.getAll( function(aContacts) { - mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contacts: aContacts}); + try { + mm.sendAsyncMessage("Contacts:GetAll:Next", {cursorId: msg.cursorId, contacts: aContacts}); + } catch (e) { + if (DEBUG) debug("Child is dead, DB should stop sending contacts"); + throw e; + } }, function(aErrorMsg) { mm.sendAsyncMessage("Contacts:Find:Return:KO", { errorMsg: aErrorMsg }); }, msg.findOptions, msg.cursorId); diff --git a/dom/devicestorage/DeviceStorage.h b/dom/devicestorage/DeviceStorage.h index 4d478306548a..78d0d64d9228 100644 --- a/dom/devicestorage/DeviceStorage.h +++ b/dom/devicestorage/DeviceStorage.h @@ -20,10 +20,18 @@ public: nsCOMPtr mFile; nsString mPath; nsString mStorageType; + nsString mRootDir; bool mEditable; - DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile, const nsAString& aPath); - DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile); + // Used when the path will be set later via SetPath. + DeviceStorageFile(const nsAString& aStorageType); + // Used for non-enumeration purposes. + DeviceStorageFile(const nsAString& aStorageType, const nsAString& aPath); + // Used for enumerations. When you call Enumerate, you can pass in a directory to enumerate + // and the results that are returned are relative to that directory, files related to an + // enumeration need to know the "root of the enumeration" directory. + DeviceStorageFile(const nsAString& aStorageType, const nsAString& aRootDir, const nsAString& aPath); + void SetPath(const nsAString& aPath); void SetEditable(bool aEditable); @@ -32,6 +40,7 @@ public: // we want to make sure that the names of file can't reach // outside of the type of storage the user asked for. bool IsSafePath(); + bool IsSafePath(const nsAString& aPath); nsresult Remove(); nsresult Write(nsIInputStream* aInputStream); @@ -40,10 +49,13 @@ public: void collectFilesInternal(nsTArray > &aFiles, PRTime aSince, nsAString& aRootPath); static void DirectoryDiskUsage(nsIFile* aFile, uint64_t* aSoFar, const nsAString& aStorageType); - + static void GetRootDirectoryForType(const nsAString& aType, + const nsAString& aVolName, + nsIFile** aFile); private: + void Init(const nsAString& aStorageType); void NormalizeFilePath(); - void AppendRelativePath(); + void AppendRelativePath(const nsAString& aPath); }; /* diff --git a/dom/devicestorage/DeviceStorageRequestChild.cpp b/dom/devicestorage/DeviceStorageRequestChild.cpp index 9b5ef97f29ee..9c90591c3b42 100644 --- a/dom/devicestorage/DeviceStorageRequestChild.cpp +++ b/dom/devicestorage/DeviceStorageRequestChild.cpp @@ -100,14 +100,9 @@ DeviceStorageRequestChild::Recv__delete__(const DeviceStorageResponseValue& aVal uint32_t count = r.paths().Length(); for (uint32_t i = 0; i < count; i++) { - nsCOMPtr f; - nsresult rv = NS_NewLocalFile(r.paths()[i].fullpath(), false, getter_AddRefs(f)); - if (NS_FAILED(rv)) { - continue; - } - - nsRefPtr dsf = new DeviceStorageFile(r.paths()[i].type(), f); - dsf->SetPath(r.paths()[i].name()); + nsRefPtr dsf = new DeviceStorageFile(r.type(), + r.relpath(), + r.paths()[i].name()); cursor->mFiles.AppendElement(dsf); } diff --git a/dom/devicestorage/DeviceStorageRequestParent.cpp b/dom/devicestorage/DeviceStorageRequestParent.cpp index e046024e962f..8f438445614a 100644 --- a/dom/devicestorage/DeviceStorageRequestParent.cpp +++ b/dom/devicestorage/DeviceStorageRequestParent.cpp @@ -34,10 +34,7 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageAddParams p = mParams; - nsCOMPtr f; - NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f)); - - nsRefPtr dsf = new DeviceStorageFile(p.type(), f); + nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); BlobParent* bp = static_cast(p.blobParent()); nsCOMPtr blob = bp->GetBlob(); @@ -56,12 +53,7 @@ DeviceStorageRequestParent::Dispatch() case DeviceStorageParams::TDeviceStorageGetParams: { DeviceStorageGetParams p = mParams; - - nsCOMPtr f; - NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f)); - - nsRefPtr dsf = new DeviceStorageFile(p.type(), f); - dsf->SetPath(p.name()); + nsRefPtr dsf = new DeviceStorageFile(p.type(), p.rootDir(), p.relpath()); nsRefPtr r = new ReadFileEvent(this, dsf); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -74,10 +66,7 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageDeleteParams p = mParams; - nsCOMPtr f; - NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f)); - - nsRefPtr dsf = new DeviceStorageFile(p.type(), f); + nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); nsRefPtr r = new DeleteFileEvent(this, dsf); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -90,10 +79,7 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageFreeSpaceParams p = mParams; - nsCOMPtr f; - NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f)); - - nsRefPtr dsf = new DeviceStorageFile(p.type(), f); + nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); nsRefPtr r = new FreeSpaceFileEvent(this, dsf); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -106,10 +92,7 @@ DeviceStorageRequestParent::Dispatch() { DeviceStorageUsedSpaceParams p = mParams; - nsCOMPtr f; - NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f)); - - nsRefPtr dsf = new DeviceStorageFile(p.type(), f); + nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); nsRefPtr r = new UsedSpaceFileEvent(this, dsf); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -121,7 +104,9 @@ DeviceStorageRequestParent::Dispatch() case DeviceStorageParams::TDeviceStorageAvailableParams: { DeviceStorageAvailableParams p = mParams; - nsRefPtr r = new PostAvailableResultEvent(this, p.fullpath()); + + nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath()); + nsRefPtr r = new PostAvailableResultEvent(this, dsf); NS_DispatchToMainThread(r); break; } @@ -129,11 +114,7 @@ DeviceStorageRequestParent::Dispatch() case DeviceStorageParams::TDeviceStorageEnumerationParams: { DeviceStorageEnumerationParams p = mParams; - - nsCOMPtr f; - NS_NewLocalFile(p.fullpath(), false, getter_AddRefs(f)); - - nsRefPtr dsf = new DeviceStorageFile(p.type(), f); + nsRefPtr dsf = new DeviceStorageFile(p.type(), p.relpath(), NS_LITERAL_STRING("")); nsRefPtr r = new EnumerateFileEvent(this, dsf, p.since()); nsCOMPtr target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); @@ -382,8 +363,12 @@ DeviceStorageRequestParent::PostBlobSuccessEvent::CancelableRun() { } DeviceStorageRequestParent::PostEnumerationSuccessEvent::PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, + const nsAString& aStorageType, + const nsAString& aRelPath, InfallibleTArray& aPaths) : CancelableRunnable(aParent) + , mStorageType(aStorageType) + , mRelPath(aRelPath) , mPaths(aPaths) { } @@ -394,7 +379,7 @@ nsresult DeviceStorageRequestParent::PostEnumerationSuccessEvent::CancelableRun() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - EnumerationResponse response(mPaths); + EnumerationResponse response(mStorageType, mRelPath, mPaths); unused << mParent->Send__delete__(mParent, response); return NS_OK; } @@ -621,13 +606,11 @@ DeviceStorageRequestParent::EnumerateFileEvent::CancelableRun() uint32_t count = files.Length(); for (uint32_t i = 0; i < count; i++) { - nsString fullpath; - files[i]->mFile->GetPath(fullpath); - DeviceStorageFileValue dsvf(mFile->mStorageType, files[i]->mPath, fullpath); + DeviceStorageFileValue dsvf(files[i]->mPath); values.AppendElement(dsvf); } - r = new PostEnumerationSuccessEvent(mParent, values); + r = new PostEnumerationSuccessEvent(mParent, mFile->mStorageType, mFile->mRootDir, values); NS_DispatchToMainThread(r); return NS_OK; } @@ -655,9 +638,9 @@ DeviceStorageRequestParent::PostPathResultEvent::CancelableRun() } DeviceStorageRequestParent::PostAvailableResultEvent::PostAvailableResultEvent(DeviceStorageRequestParent* aParent, - const nsAString &aPath) + DeviceStorageFile* aFile) : CancelableRunnable(aParent) - , mPath(aPath) + , mFile(aFile) { } @@ -673,7 +656,7 @@ DeviceStorageRequestParent::PostAvailableResultEvent::CancelableRun() nsString state; state.Assign(NS_LITERAL_STRING("available")); #ifdef MOZ_WIDGET_GONK - nsresult rv = GetSDCardStatus(mPath, state); + nsresult rv = GetSDCardStatus(mFile->mPath, state); if (NS_FAILED(rv)) { state.Assign(NS_LITERAL_STRING("unavailable")); } diff --git a/dom/devicestorage/DeviceStorageRequestParent.h b/dom/devicestorage/DeviceStorageRequestParent.h index 7a124a71b56f..5aee8dce45ef 100644 --- a/dom/devicestorage/DeviceStorageRequestParent.h +++ b/dom/devicestorage/DeviceStorageRequestParent.h @@ -105,10 +105,15 @@ private: class PostEnumerationSuccessEvent : public CancelableRunnable { public: - PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, InfallibleTArray& aPaths); + PostEnumerationSuccessEvent(DeviceStorageRequestParent* aParent, + const nsAString& aStorageType, + const nsAString& aRelPath, + InfallibleTArray& aPaths); virtual ~PostEnumerationSuccessEvent(); virtual nsresult CancelableRun(); private: + const nsString mStorageType; + const nsString mRelPath; InfallibleTArray mPaths; }; @@ -211,11 +216,11 @@ private: class PostAvailableResultEvent : public CancelableRunnable { public: - PostAvailableResultEvent(DeviceStorageRequestParent* aParent, const nsAString& aPath); + PostAvailableResultEvent(DeviceStorageRequestParent* aParent, DeviceStorageFile* aFile); virtual ~PostAvailableResultEvent(); virtual nsresult CancelableRun(); private: - nsString mPath; + nsRefPtr mFile; }; protected: diff --git a/dom/devicestorage/PDeviceStorageRequest.ipdl b/dom/devicestorage/PDeviceStorageRequest.ipdl index 6d0073f745f5..a79ae1b3e042 100644 --- a/dom/devicestorage/PDeviceStorageRequest.ipdl +++ b/dom/devicestorage/PDeviceStorageRequest.ipdl @@ -27,13 +27,13 @@ struct BlobResponse struct DeviceStorageFileValue { - nsString type; nsString name; - nsString fullpath; }; struct EnumerationResponse { + nsString type; + nsString relpath; DeviceStorageFileValue[] paths; }; diff --git a/dom/devicestorage/nsDeviceStorage.cpp b/dom/devicestorage/nsDeviceStorage.cpp index 1a0b19fc175e..9d8358b0f091 100644 --- a/dom/devicestorage/nsDeviceStorage.cpp +++ b/dom/devicestorage/nsDeviceStorage.cpp @@ -67,6 +67,21 @@ using namespace mozilla::dom::devicestorage; #include "nsDirectoryServiceDefs.h" +class GlobalDirs : public RefCounted +{ +public: +#if !defined(MOZ_WIDGET_GONK) + nsCOMPtr pictures; + nsCOMPtr videos; + nsCOMPtr music; + nsCOMPtr apps; + nsCOMPtr sdcard; +#endif + nsCOMPtr temp; +}; + +static StaticRefPtr sDirs; + nsAutoPtr DeviceStorageTypeChecker::sDeviceStorageTypeChecker; DeviceStorageTypeChecker::DeviceStorageTypeChecker() @@ -311,35 +326,182 @@ private: }; DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, - nsIFile* aFile, + const nsAString& aRootDir, + const nsAString& aPath) + : mPath(aPath) + , mStorageType(aStorageType) + , mRootDir(aRootDir) + , mEditable(false) +{ + Init(aStorageType); + AppendRelativePath(mRootDir); + if (!mPath.EqualsLiteral("")) { + AppendRelativePath(mPath); + } + NormalizeFilePath(); +} + +DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, const nsAString& aPath) : mPath(aPath) , mStorageType(aStorageType) , mEditable(false) { - NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile"); - // always take a clone - nsCOMPtr file; - aFile->Clone(getter_AddRefs(mFile)); - - AppendRelativePath(); + Init(aStorageType); + AppendRelativePath(aPath); NormalizeFilePath(); +} + +DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType) + : mStorageType(aStorageType) + , mEditable(false) +{ + Init(aStorageType); +} + +void +DeviceStorageFile::Init(const nsAString& aStorageType) +{ + // The hard-coded sdcard below will change as part of bug 858416 + DeviceStorageFile::GetRootDirectoryForType(aStorageType, + NS_LITERAL_STRING("sdcard"), + getter_AddRefs(mFile)); DebugOnly typeChecker = DeviceStorageTypeChecker::CreateOrGet(); NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null"); } -DeviceStorageFile::DeviceStorageFile(const nsAString& aStorageType, nsIFile* aFile) - : mStorageType(aStorageType) - , mEditable(false) +static void +InitDirs() { - NS_ASSERTION(aFile, "Must not create a DeviceStorageFile with a null nsIFile"); - // always take a clone - nsCOMPtr file; - aFile->Clone(getter_AddRefs(mFile)); + if (sDirs) { + return; + } + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + sDirs = new GlobalDirs; + ClearOnShutdown(&sDirs); - DebugOnly typeChecker = DeviceStorageTypeChecker::CreateOrGet(); - NS_ASSERTION(typeChecker, "DeviceStorageTypeChecker is null"); + nsCOMPtr dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); + NS_ASSERTION(dirService, "Must have directory service"); + +#if !defined(MOZ_WIDGET_GONK) + +#if defined (MOZ_WIDGET_COCOA) + dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures)); + dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos)); + dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music)); +#elif defined (XP_UNIX) + dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures)); + dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos)); + dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music)); +#elif defined (XP_WIN) + dirService->Get(NS_WIN_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->pictures)); + dirService->Get(NS_WIN_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->videos)); + dirService->Get(NS_WIN_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->music)); +#endif + + dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->apps)); + if (sDirs->apps) { + sDirs->apps->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps")); + } + + // Eventually, on desktop, we want to do something smarter -- for example, + // detect when an sdcard is inserted, and use that instead of this. + dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->sdcard)); + if (sDirs->sdcard) { + sDirs->sdcard->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard")); + } +#endif // !MOZ_WIDGET_GONK + + if (mozilla::Preferences::GetBool("device.storage.testing", false)) { + dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(sDirs->temp)); + if (sDirs->temp) { + sDirs->temp->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing")); + sDirs->temp->Create(nsIFile::DIRECTORY_TYPE, 0777); + sDirs->temp->Normalize(); + } + } +} + +void +DeviceStorageFile::GetRootDirectoryForType(const nsAString &aType, const + nsAString &aVolName, + nsIFile** aFile) +{ + nsCOMPtr f; + + InitDirs(); + +#ifdef MOZ_WIDGET_GONK + nsString volMountPoint(NS_LITERAL_STRING("/sdcard")); + if (!aVolName.EqualsLiteral("")) { + nsCOMPtr vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID); + if (vs) { + nsresult rv; + nsCOMPtr vol; + rv = vs->GetVolumeByName(aVolName, getter_AddRefs(vol)); + if (NS_SUCCEEDED(rv)) { + vol->GetMountPoint(volMountPoint); + } + } + } +#endif + + // Picture directory + if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { +#ifdef MOZ_WIDGET_GONK + NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); +#else + f = sDirs->pictures; +#endif + } + + // Video directory + else if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { +#ifdef MOZ_WIDGET_GONK + NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); +#else + f = sDirs->videos; +#endif + } + + // Music directory + else if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { +#ifdef MOZ_WIDGET_GONK + NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); +#else + f = sDirs->music; +#endif + } + + // Apps directory + else if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) { +#ifdef MOZ_WIDGET_GONK + NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(f)); +#else + f = sDirs->apps; +#endif + } + + // default SDCard + else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) { +#ifdef MOZ_WIDGET_GONK + NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); +#else + f = sDirs->sdcard; +#endif + } + + // in testing, we default all device storage types to a temp directory + if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) { + f = sDirs->temp; + } + + if (f) { + f->Clone(aFile); + } else { + *aFile = nullptr; + } } void @@ -357,21 +519,27 @@ DeviceStorageFile::SetEditable(bool aEditable) { // outside of the type of storage the user asked for. bool DeviceStorageFile::IsSafePath() +{ + return IsSafePath(mRootDir) && IsSafePath(mPath); +} + +bool +DeviceStorageFile::IsSafePath(const nsAString& aPath) { nsAString::const_iterator start, end; - mPath.BeginReading(start); - mPath.EndReading(end); + aPath.BeginReading(start); + aPath.EndReading(end); // if the path is a '~' or starts with '~/', return false. NS_NAMED_LITERAL_STRING(tilde, "~"); NS_NAMED_LITERAL_STRING(tildeSlash, "~/"); - if (mPath.Equals(tilde) || - StringBeginsWith(mPath, tildeSlash)) { + if (aPath.Equals(tilde) || + StringBeginsWith(aPath, tildeSlash)) { NS_WARNING("Path name starts with tilde!"); return false; } // split on /. if any token is "", ., or .., return false. - NS_ConvertUTF16toUTF8 cname(mPath); + NS_ConvertUTF16toUTF8 cname(aPath); char* buffer = cname.BeginWriting(); const char* token; @@ -398,12 +566,12 @@ DeviceStorageFile::NormalizeFilePath() { } void -DeviceStorageFile::AppendRelativePath() { +DeviceStorageFile::AppendRelativePath(const nsAString& aPath) { #if defined(XP_WIN) // replace forward slashes with backslashes, // since nsLocalFileWin chokes on them nsString temp; - temp.Assign(mPath); + temp.Assign(aPath); PRUnichar* cur = temp.BeginWriting(); PRUnichar* end = temp.EndWriting(); @@ -414,7 +582,7 @@ DeviceStorageFile::AppendRelativePath() { } mFile->AppendRelativePath(temp); #else - mFile->AppendRelativePath(mPath); + mFile->AppendRelativePath(aPath); #endif } @@ -536,7 +704,6 @@ DeviceStorageFile::CollectFiles(nsTArray > &aFiles, if (NS_FAILED(rv)) { return; } - return collectFilesInternal(aFiles, aSince, rootPath); } @@ -585,12 +752,10 @@ DeviceStorageFile::collectFilesInternal(nsTArray > & nsDependentSubstring newPath = Substring(fullpath, len); if (isDir) { - DeviceStorageFile dsf(mStorageType, f); - dsf.SetPath(newPath); + DeviceStorageFile dsf(mStorageType, mRootDir, newPath); dsf.collectFilesInternal(aFiles, aSince, aRootPath); } else if (isFile) { - nsRefPtr dsf = new DeviceStorageFile(mStorageType, f); - dsf->SetPath(newPath); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDir, newPath); aFiles.AppendElement(dsf); } } @@ -706,11 +871,13 @@ UnregisterForSDCardChanges(nsIObserver* aObserver) #endif void -nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType, const nsAString& aVolName) +nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType, + const nsAString& aVolName) { nsCOMPtr f; - nsCOMPtr dirService = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID); - NS_ASSERTION(dirService, "Must have directory service"); + DeviceStorageFile::GetRootDirectoryForType(aType, + aVolName, + getter_AddRefs(f)); mVolumeName = NS_LITERAL_STRING(""); #ifdef MOZ_WIDGET_GONK @@ -727,84 +894,7 @@ nsDOMDeviceStorage::SetRootDirectoryForType(const nsAString& aType, const nsAStr } } } -#endif - // Picture directory - if (aType.EqualsLiteral(DEVICESTORAGE_PICTURES)) { -#ifdef MOZ_WIDGET_GONK - NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); -#elif defined (MOZ_WIDGET_COCOA) - dirService->Get(NS_OSX_PICTURE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#elif defined (XP_UNIX) - dirService->Get(NS_UNIX_XDG_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#elif defined (XP_WIN) - dirService->Get(NS_WIN_PICTURES_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#endif - } - - // Video directory - else if (aType.EqualsLiteral(DEVICESTORAGE_VIDEOS)) { -#ifdef MOZ_WIDGET_GONK - NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); -#elif defined (MOZ_WIDGET_COCOA) - dirService->Get(NS_OSX_MOVIE_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#elif defined (XP_UNIX) - dirService->Get(NS_UNIX_XDG_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#elif defined (XP_WIN) - dirService->Get(NS_WIN_VIDEOS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#endif - } - - // Music directory - else if (aType.EqualsLiteral(DEVICESTORAGE_MUSIC)) { -#ifdef MOZ_WIDGET_GONK - NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); -#elif defined (MOZ_WIDGET_COCOA) - dirService->Get(NS_OSX_MUSIC_DOCUMENTS_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#elif defined (XP_UNIX) - dirService->Get(NS_UNIX_XDG_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#elif defined (XP_WIN) - dirService->Get(NS_WIN_MUSIC_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); -#endif - } - - // Apps directory - else if (aType.EqualsLiteral(DEVICESTORAGE_APPS)) { -#ifdef MOZ_WIDGET_GONK - NS_NewLocalFile(NS_LITERAL_STRING("/data"), false, getter_AddRefs(f)); -#else - dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); - if (f) { - f->AppendRelativeNativePath(NS_LITERAL_CSTRING("webapps")); - } -#endif - } - - // default SDCard - else if (aType.EqualsLiteral(DEVICESTORAGE_SDCARD)) { -#ifdef MOZ_WIDGET_GONK - NS_NewLocalFile(volMountPoint, false, getter_AddRefs(f)); -#else - // Eventually, on desktop, we want to do something smarter -- for example, - // detect when an sdcard is inserted, and use that instead of this. - dirService->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); - if (f) { - f->AppendRelativeNativePath(NS_LITERAL_CSTRING("fake-sdcard")); - } -#endif - } - - // in testing, we default all device storage types to a temp directory - if (f && mozilla::Preferences::GetBool("device.storage.testing", false)) { - dirService->Get(NS_OS_TEMP_DIR, NS_GET_IID(nsIFile), getter_AddRefs(f)); - if (f) { - f->AppendRelativeNativePath(NS_LITERAL_CSTRING("device-storage-testing")); - f->Create(nsIFile::DIRECTORY_TYPE, 0777); - f->Normalize(); - } - } - -#ifdef MOZ_WIDGET_GONK RegisterForSDCardChanges(this); #endif @@ -1026,20 +1116,13 @@ ContinueCursorEvent::Continue() return; } - nsString fullpath; - nsresult rv = file->mFile->GetPath(fullpath); - if (NS_FAILED(rv)) { - NS_ASSERTION(false, "GetPath failed to return a valid path"); - return; - } - nsDOMDeviceStorageCursor* cursor = static_cast(mRequest.get()); nsString cursorStorageType; cursor->GetStorageType(cursorStorageType); DeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, file); child->SetCallback(cursor); - DeviceStorageGetParams params(cursorStorageType, file->mPath, fullpath); + DeviceStorageGetParams params(cursorStorageType, file->mRootDir, file->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); mRequest = nullptr; } @@ -1180,17 +1263,8 @@ nsDOMDeviceStorageCursor::Allow() } if (XRE_GetProcessType() != GeckoProcessType_Default) { - - nsString fullpath; - nsresult rv = mFile->mFile->GetPath(fullpath); - - if (NS_FAILED(rv)) { - // just do nothing - return NS_OK; - } - PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(this, mFile); - DeviceStorageEnumerationParams params(mFile->mStorageType, fullpath, mSince); + DeviceStorageEnumerationParams params(mFile->mStorageType, mFile->mRootDir, mSince); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1650,14 +1724,6 @@ public: return NS_ERROR_FAILURE; } - nsString fullpath; - nsresult rv = mFile->mFile->GetPath(fullpath); - - if (NS_FAILED(rv)) { - // just do nothing - return NS_OK; - } - switch(mRequestType) { case DEVICE_STORAGE_REQUEST_CREATE: { @@ -1687,8 +1753,7 @@ public: DeviceStorageAddParams params; params.blobChild() = actor; params.type() = mFile->mStorageType; - params.name() = mFile->mPath; - params.fullpath() = fullpath; + params.relpath() = mFile->mPath; PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); @@ -1714,7 +1779,7 @@ public: if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageGetParams params(mFile->mStorageType, mFile->mPath, fullpath); + DeviceStorageGetParams params(mFile->mStorageType, mFile->mRootDir, mFile->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1738,7 +1803,7 @@ public: if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageDeleteParams params(mFile->mStorageType, fullpath); + DeviceStorageDeleteParams params(mFile->mStorageType, mFile->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1750,7 +1815,7 @@ public: { if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageFreeSpaceParams params(mFile->mStorageType, fullpath); + DeviceStorageFreeSpaceParams params(mFile->mStorageType, mFile->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1762,7 +1827,7 @@ public: { if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageUsedSpaceParams params(mFile->mStorageType, fullpath); + DeviceStorageUsedSpaceParams params(mFile->mStorageType, mFile->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -1774,7 +1839,7 @@ public: { if (XRE_GetProcessType() != GeckoProcessType_Default) { PDeviceStorageRequestChild* child = new DeviceStorageRequestChild(mRequest, mFile); - DeviceStorageAvailableParams params(mFile->mStorageType, fullpath); + DeviceStorageAvailableParams params(mFile->mStorageType, mFile->mPath); ContentChild::GetSingleton()->SendPDeviceStorageRequestConstructor(child, params); return NS_OK; } @@ -2024,7 +2089,7 @@ nsDOMDeviceStorage::AddNamed(nsIDOMBlob *aBlob, nsCOMPtr r; - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory, aPath); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, aPath); if (!dsf->IsSafePath()) { r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); } else if (!typeChecker->Check(mStorageType, dsf->mFile) || @@ -2080,7 +2145,7 @@ nsDOMDeviceStorage::GetInternal(const JS::Value & aPath, return NS_OK; } - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, path); dsf->SetEditable(aEditable); if (!dsf->IsSafePath()) { r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); @@ -2113,7 +2178,7 @@ nsDOMDeviceStorage::Delete(const JS::Value & aPath, JSContext* aCx, nsIDOMDOMReq return NS_OK; } - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, path); if (!dsf->IsSafePath()) { r = new PostErrorEvent(request, POST_ERROR_EVENT_PERMISSION_DENIED); @@ -2137,7 +2202,7 @@ nsDOMDeviceStorage::FreeSpace(nsIDOMDOMRequest** aRetval) nsRefPtr request = new DOMRequest(win); NS_ADDREF(*aRetval = request); - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory); + nsRefPtr dsf = new DeviceStorageFile(mStorageType); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_FREE_SPACE, win, mPrincipal, @@ -2158,7 +2223,7 @@ nsDOMDeviceStorage::UsedSpace(nsIDOMDOMRequest** aRetval) nsRefPtr request = new DOMRequest(win); NS_ADDREF(*aRetval = request); - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory); + nsRefPtr dsf = new DeviceStorageFile(mStorageType); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_USED_SPACE, win, mPrincipal, @@ -2179,7 +2244,7 @@ nsDOMDeviceStorage::Available(nsIDOMDOMRequest** aRetval) nsRefPtr request = new DOMRequest(win); NS_ADDREF(*aRetval = request); - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory); + nsRefPtr dsf = new DeviceStorageFile(mStorageType); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_AVAILABLE, win, mPrincipal, @@ -2279,7 +2344,7 @@ nsDOMDeviceStorage::EnumerateInternal(const JS::Value & aName, since = ExtractDateFromOptions(aCx, aOptions); } - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory, path); + nsRefPtr dsf = new DeviceStorageFile(mStorageType, path, NS_LITERAL_STRING("")); dsf->SetEditable(aEditable); nsRefPtr cursor = new nsDOMDeviceStorageCursor(win, mPrincipal, @@ -2453,7 +2518,7 @@ nsDOMDeviceStorage::AddEventListener(const nsAString & aType, } nsRefPtr request = new DOMRequest(win); - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory); + nsRefPtr dsf = new DeviceStorageFile(mStorageType); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH, win, mPrincipal, dsf, request, this); NS_DispatchToMainThread(r); @@ -2474,7 +2539,7 @@ nsDOMDeviceStorage::AddEventListener(const nsAString & aType, } nsRefPtr request = new DOMRequest(win); - nsRefPtr dsf = new DeviceStorageFile(mStorageType, mRootDirectory); + nsRefPtr dsf = new DeviceStorageFile(mStorageType); nsCOMPtr r = new DeviceStorageRequest(DEVICE_STORAGE_REQUEST_WATCH, win, mPrincipal, dsf, request, this); NS_DispatchToMainThread(r); diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index 016508fb7387..af6a5e8ba0ce 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -102,8 +102,7 @@ public: return nullptr; } - nsCOMPtr doc = - do_QueryInterface(GetOwner()->GetExtantDocument()); + nsCOMPtr doc = GetOwner()->GetExtantDoc(); return doc.forget(); } diff --git a/dom/interfaces/events/nsIDOMKeyEvent.idl b/dom/interfaces/events/nsIDOMKeyEvent.idl index bd1d6e991317..f17bceeca1f9 100644 --- a/dom/interfaces/events/nsIDOMKeyEvent.idl +++ b/dom/interfaces/events/nsIDOMKeyEvent.idl @@ -5,7 +5,7 @@ #include "nsIDOMUIEvent.idl" -[scriptable, builtinclass, uuid(ffbe684c-ca90-4b58-aa8c-9727f997f86d)] +[scriptable, builtinclass, uuid(91a3d7f2-223b-4e09-a566-634e7ee0a31d)] interface nsIDOMKeyEvent : nsIDOMUIEvent { const unsigned long DOM_VK_CANCEL = 0x03; @@ -252,4 +252,6 @@ interface nsIDOMKeyEvent : nsIDOMUIEvent const unsigned long DOM_KEY_LOCATION_JOYSTICK = 0x05; readonly attribute unsigned long location; + + readonly attribute DOMString key; }; diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 1e86af0b3092..a186189b0226 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -1176,10 +1176,7 @@ ContentChild::RecvLastPrivateDocShellDestroyed() bool ContentChild::RecvFilePathUpdate(const nsString& type, const nsString& path, const nsCString& aReason) { - nsCOMPtr file; - NS_NewLocalFile(path, false, getter_AddRefs(file)); - - nsRefPtr dsf = new DeviceStorageFile(type, file); + nsRefPtr dsf = new DeviceStorageFile(type, path); nsString reason; CopyASCIItoUTF16(aReason, reason); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index ea2fc83feb8f..1134faee6b39 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1531,10 +1531,7 @@ ContentParent::Observe(nsISupports* aSubject, CopyUTF16toUTF8(aData, creason); DeviceStorageFile* file = static_cast(aSubject); - nsString path; - file->mFile->GetPath(path); - - unused << SendFilePathUpdate(file->mStorageType, path, creason); + unused << SendFilePathUpdate(file->mStorageType, file->mPath, creason); } #ifdef MOZ_WIDGET_GONK else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) { @@ -2349,15 +2346,11 @@ ContentParent::RecvAsyncMessage(const nsString& aMsg, } bool -ContentParent::RecvFilePathUpdateNotify(const nsString& aType, const nsString& aFilePath, const nsCString& aReason) +ContentParent::RecvFilePathUpdateNotify(const nsString& aType, + const nsString& aFilePath, + const nsCString& aReason) { - nsCOMPtr file; - nsresult rv = NS_NewLocalFile(aFilePath, false, getter_AddRefs(file)); - if (NS_FAILED(rv)) { - // ignore - return true; - } - nsRefPtr dsf = new DeviceStorageFile(aType, file); + nsRefPtr dsf = new DeviceStorageFile(aType, aFilePath); nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) { diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index f3a5064a3d24..ebcd9612a28e 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -63,46 +63,45 @@ struct FontListEntry { struct DeviceStorageFreeSpaceParams { nsString type; - nsString fullpath; + nsString relpath; }; struct DeviceStorageUsedSpaceParams { nsString type; - nsString fullpath; + nsString relpath; }; struct DeviceStorageAvailableParams { nsString type; - nsString fullpath; + nsString relpath; }; struct DeviceStorageAddParams { nsString type; + nsString relpath; PBlob blob; - nsString name; - nsString fullpath; }; struct DeviceStorageGetParams { nsString type; - nsString name; - nsString fullpath; + nsString rootDir; + nsString relpath; }; struct DeviceStorageDeleteParams { nsString type; - nsString fullpath; + nsString relpath; }; struct DeviceStorageEnumerationParams { nsString type; - nsString fullpath; + nsString relpath; uint64_t since; }; diff --git a/dom/locales/en-US/chrome/dom/dom.properties b/dom/locales/en-US/chrome/dom/dom.properties index 291ee789d149..4e00643ba7e0 100644 --- a/dom/locales/en-US/chrome/dom/dom.properties +++ b/dom/locales/en-US/chrome/dom/dom.properties @@ -83,7 +83,6 @@ RemovedFullScreenElement=Exited full-screen because full-screen element was remo FocusedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was focused. HTMLSyncXHRWarning=HTML parsing in XMLHttpRequest is not supported in the synchronous mode. InvalidRedirectChannelWarning=Unable to redirect to %S because the channel doesn't implement nsIWritablePropertyBag2. -ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied. ResponseTypeSyncXHRWarning=Use of XMLHttpRequest's responseType attribute is no longer supported in the synchronous mode in window context. WithCredentialsSyncXHRWarning=Use of XMLHttpRequest's withCredentials attribute is no longer supported in the synchronous mode in window context. TimeoutSyncXHRWarning=Use of XMLHttpRequest's timeout attribute is not supported in the synchronous mode in window context. @@ -128,10 +127,6 @@ PluginHangUIWaitButton=Continue PluginHangUIStopButton=Stop plugin # LOCALIZATION NOTE: Do not translate "mozHidden", "mozVisibilityState", "hidden", or "visibilityState" PrefixedVisibilityApiWarning='mozHidden' and 'mozVisibilityState' are deprecated. Please use the unprefixed 'hidden' and 'visibilityState' instead. -# LOCALIZATION NOTE: Do not translate "X-Content-Security-Policy", "X-Content-Security-Policy-Report-Only", "Content-Security-Policy" or "Content-Security-Policy-Report-Only" -OldCSPHeaderDeprecated=The X-Content-Security-Policy and X-Content-Security-Report-Only headers will be deprecated in the future. Please use the Content-Security-Policy and Content-Security-Report-Only headers with CSP spec compliant syntax instead. -# LOCALIZATION NOTE: Do not translate "X-Content-Security-Policy/Report-Only" or "Content-Security-Policy/Report-Only" -BothCSPHeadersPresent=This site specified both an X-Content-Security-Policy/Report-Only header and a Content-Security-Policy/Report-Only header. The X-Content-Security-Policy/Report-Only header(s) will be ignored. # LOCALIZATION NOTE: Do not translate "NodeIterator" or "detach()". NodeIteratorDetachWarning=Calling detach() on a NodeIterator no longer has an effect. # LOCALIZATION NOTE: Do not translate "Mozilla Audio Data API" and "Web Audio API". diff --git a/dom/locales/en-US/chrome/security/security.properties b/dom/locales/en-US/chrome/security/security.properties index 67cc80e611b8..93f8387f7c38 100644 --- a/dom/locales/en-US/chrome/security/security.properties +++ b/dom/locales/en-US/chrome/security/security.properties @@ -1,2 +1,10 @@ +# Mixed Content Blocker BlockMixedDisplayContent = Blocked loading mixed display content "%1$S" BlockMixedActiveContent = Blocked loading mixed active content "%1$S" + +# CSP +ReportOnlyCSPIgnored=Report-only CSP policy will be ignored because there are other non-report-only CSP policies applied. +# LOCALIZATION NOTE: Do not translate "X-Content-Security-Policy", "X-Content-Security-Policy-Report-Only", "Content-Security-Policy" or "Content-Security-Policy-Report-Only" +OldCSPHeaderDeprecated=The X-Content-Security-Policy and X-Content-Security-Report-Only headers will be deprecated in the future. Please use the Content-Security-Policy and Content-Security-Report-Only headers with CSP spec compliant syntax instead. +# LOCALIZATION NOTE: Do not translate "X-Content-Security-Policy/Report-Only" or "Content-Security-Policy/Report-Only" +BothCSPHeadersPresent=This site specified both an X-Content-Security-Policy/Report-Only header and a Content-Security-Policy/Report-Only header. The X-Content-Security-Policy/Report-Only header(s) will be ignored. diff --git a/dom/media/MediaManager.cpp b/dom/media/MediaManager.cpp index 4d077f1224df..4f18537ab5f2 100644 --- a/dom/media/MediaManager.cpp +++ b/dom/media/MediaManager.cpp @@ -975,9 +975,8 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow, nsCOMPtr doc = aWindow->GetExtantDoc(); pm->TestPermission(doc->NodePrincipal(), &permission); if (permission == nsIPopupWindowManager::DENY_POPUP) { - nsCOMPtr domDoc = aWindow->GetExtantDocument(); nsGlobalWindow::FirePopupBlockedEvent( - domDoc, aWindow, nullptr, EmptyString(), EmptyString() + doc, aWindow, nullptr, EmptyString(), EmptyString() ); return NS_OK; } diff --git a/dom/network/src/TCPSocket.js b/dom/network/src/TCPSocket.js index d5409d6092b7..04d2b973f51b 100644 --- a/dom/network/src/TCPSocket.js +++ b/dom/network/src/TCPSocket.js @@ -375,7 +375,7 @@ TCPSocket.prototype = { that._socketBridge = Cc["@mozilla.org/tcp-socket-child;1"] .createInstance(Ci.nsITCPSocketChild); that._socketBridge.open(that, host, port, !!that._ssl, - that._binaryType, this.useWin, this); + that._binaryType, this.useWin, this.useWin || this); return that; } diff --git a/dom/network/src/TCPSocketChild.cpp b/dom/network/src/TCPSocketChild.cpp index abab3e787d7b..2eea7b84b9cd 100644 --- a/dom/network/src/TCPSocketChild.cpp +++ b/dom/network/src/TCPSocketChild.cpp @@ -11,6 +11,7 @@ #include "nsContentUtils.h" #include "jsapi.h" #include "jsfriendapi.h" +#include "jswrapper.h" using mozilla::net::gNeckoChild; @@ -82,7 +83,10 @@ TCPSocketChild::Open(nsITCPSocketInternal* aSocket, const nsAString& aHost, { mSocket = aSocket; MOZ_ASSERT(aSocketObj.isObject()); - mSocketObj = &aSocketObj.toObject(); + mSocketObj = js::CheckedUnwrap(&aSocketObj.toObject()); + if (!mSocketObj) { + return NS_ERROR_FAILURE; + } AddIPDLReference(); gNeckoChild->SendPTCPSocketConstructor(this, nsString(aHost), aPort, aUseSSL, nsString(aBinaryType), @@ -133,7 +137,8 @@ TCPSocketChild::RecvCallback(const nsString& aType, if (data.type() == SendableData::TArrayOfuint8_t) { JS::Value val; - IPC::DeserializeArrayBuffer(mSocketObj, data.get_ArrayOfuint8_t(), &val); + bool ok = IPC::DeserializeArrayBuffer(mSocketObj, data.get_ArrayOfuint8_t(), &val); + NS_ENSURE_TRUE(ok, true); rv = mSocket->CallListenerArrayBuffer(aType, val); } else if (data.type() == SendableData::TnsString) { diff --git a/dom/plugins/base/nsIPluginHost.idl b/dom/plugins/base/nsIPluginHost.idl index b79bcab4c0db..5cc165f6b3b9 100644 --- a/dom/plugins/base/nsIPluginHost.idl +++ b/dom/plugins/base/nsIPluginHost.idl @@ -20,17 +20,14 @@ interface nsIPluginPlayPreviewInfo : nsISupports readonly attribute AUTF8String redirectURL; }; -[scriptable, uuid(67ebff01-0dce-48f7-b6a5-6235fc78382b)] +[scriptable, uuid(15f97490-7bdf-4947-885c-9258072af878)] interface nsIPluginHost : nsISupports { /** * Causes the plugins directory to be searched again for new plugin * libraries. - * - * @param reloadPages - indicates whether currently visible pages should - * also be reloaded */ - void reloadPlugins(in boolean reloadPages); + void reloadPlugins(); void getPluginTags([optional] out unsigned long aPluginCount, [retval, array, size_is(aPluginCount)] out nsIPluginTag aResults); diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 563a1a5fe393..7b087f122ffc 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -1145,7 +1145,7 @@ _reloadplugins(NPBool reloadPages) if (!pluginHost) return; - pluginHost->ReloadPlugins(reloadPages); + pluginHost->ReloadPlugins(); } void NP_CALLBACK diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 69f403646aef..c1d4c28e02e2 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -268,52 +268,6 @@ bool ReadSectionHeader(nsPluginManifestLineReader& reader, const char *token) return false; } -// Little helper struct to asynchronously reframe any presentations (embedded) -// or reload any documents (full-page), that contained plugins -// which were shutdown as a result of a plugins.refresh(1) -class nsPluginDocReframeEvent: public nsRunnable { -public: - nsPluginDocReframeEvent(nsTArray >& aDocs) { mDocs.SwapElements(aDocs); } - - NS_DECL_NSIRUNNABLE - - nsTArray > mDocs; -}; - -NS_IMETHODIMP nsPluginDocReframeEvent::Run() { - uint32_t c = mDocs.Length(); - - // for each document (which previously had a running instance), tell - // the frame constructor to rebuild - for (uint32_t i = 0; i < c; i++) { - nsIDocument* doc = mDocs[i]; - if (doc) { - nsIPresShell *shell = doc->GetShell(); - - // if this document has a presentation shell, then it has frames and can be reframed - if (shell) { - /* A reframe will cause a fresh object frame, instance owner, and instance - * to be created. Reframing of the entire document is necessary as we may have - * recently found new plugins and we want a shot at trying to use them instead - * of leaving alternate renderings. - * We do not want to completely reload all the documents that had running plugins - * because we could possibly trigger a script to run in the unload event handler - * which may want to access our defunct plugin and cause us to crash. - */ - - shell->ReconstructFrames(); // causes reframe of document - } else { // no pres shell --> full-page plugin - - NS_NOTREACHED("all plugins should have a pres shell!"); - - } - } - } - - mDocs.Clear(); - return NS_OK; -} - static bool UnloadPluginsASAP() { return Preferences::GetBool("dom.ipc.plugins.unloadASAP", false); @@ -404,11 +358,10 @@ bool nsPluginHost::IsRunningPlugin(nsPluginTag * aPluginTag) return false; } -nsresult nsPluginHost::ReloadPlugins(bool reloadPages) +nsresult nsPluginHost::ReloadPlugins() { PLUGIN_LOG(PLUGIN_LOG_NORMAL, - ("nsPluginHost::ReloadPlugins Begin reloadPages=%d, active_instance_count=%d\n", - reloadPages, mInstances.Length())); + ("nsPluginHost::ReloadPlugins Begin\n")); nsresult rv = NS_OK; @@ -432,14 +385,6 @@ nsresult nsPluginHost::ReloadPlugins(bool reloadPages) if (!pluginschanged) return NS_ERROR_PLUGINS_PLUGINSNOTCHANGED; - nsTArray > instsToReload; - if (reloadPages) { - - // Then stop any running plugin instances but hold on to the documents in the array - // We are going to need to restart the instances in these documents later - DestroyRunningInstances(&instsToReload, nullptr); - } - // shutdown plugins and kill the list if there are no running plugins nsRefPtr prev; nsRefPtr next; @@ -473,18 +418,8 @@ nsresult nsPluginHost::ReloadPlugins(bool reloadPages) // load them again rv = LoadPlugins(); - // If we have shut down any plugin instances, we've now got to restart them. - // Post an event to do the rest as we are going to be destroying the frame tree and we also want - // any posted unload events to finish - if (reloadPages && !instsToReload.IsEmpty()){ - nsCOMPtr ev = new nsPluginDocReframeEvent(instsToReload); - if (ev) - NS_DispatchToCurrentThread(ev); - } - PLUGIN_LOG(PLUGIN_LOG_NORMAL, - ("nsPluginHost::ReloadPlugins End active_instance_count=%d\n", - mInstances.Length())); + ("nsPluginHost::ReloadPlugins End\n")); return rv; } @@ -794,7 +729,7 @@ nsresult nsPluginHost::UnloadPlugins() // we should call nsIPluginInstance::Stop and nsIPluginInstance::SetWindow // for those plugins who want it - DestroyRunningInstances(nullptr, nullptr); + DestroyRunningInstances(nullptr); nsPluginTag *pluginTag; for (pluginTag = mPlugins; pluginTag; pluginTag = pluginTag->mNext) { @@ -1027,7 +962,7 @@ nsresult nsPluginHost::SetUpPluginInstance(const char *aMimeType, mCurrentDocument = do_GetWeakReference(document); // Don't try to set up an instance again if nothing changed. - if (ReloadPlugins(false) == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) { + if (ReloadPlugins() == NS_ERROR_PLUGINS_PLUGINSNOTCHANGED) { return rv; } @@ -2397,15 +2332,6 @@ nsPluginHost::UpdatePluginInfo(nsPluginTag* aPluginTag) return NS_OK; } - nsTArray > instsToReload; - DestroyRunningInstances(&instsToReload, aPluginTag); - - if (!instsToReload.IsEmpty()) { - nsCOMPtr ev = new nsPluginDocReframeEvent(instsToReload); - if (ev) - NS_DispatchToCurrentThread(ev); - } - return NS_OK; } @@ -3765,8 +3691,7 @@ nsPluginHost::InstanceArray() } void -nsPluginHost::DestroyRunningInstances(nsTArray >* aReloadDocs, - nsPluginTag* aPluginTag) +nsPluginHost::DestroyRunningInstances(nsPluginTag* aPluginTag) { for (int32_t i = mInstances.Length(); i > 0; i--) { nsNPAPIPluginInstance *instance = mInstances[i - 1]; @@ -3774,19 +3699,6 @@ nsPluginHost::DestroyRunningInstances(nsTArray >* aReloadD instance->SetWindow(nullptr); instance->Stop(); - // If we've been passed an array to return, lets collect all our documents, - // removing duplicates. These will be reframed (embedded) or reloaded (full-page) later - // to kickstart our instances. - if (aReloadDocs) { - nsRefPtr owner = instance->GetOwner(); - if (owner) { - nsCOMPtr doc; - owner->GetDocument(getter_AddRefs(doc)); - if (doc && !aReloadDocs->Contains(doc)) // don't allow for duplicates - aReloadDocs->AppendElement(doc); - } - } - // Get rid of all the instances without the possibility of caching. nsPluginTag* pluginTag = TagForPlugin(instance->GetPlugin()); instance->SetWindow(nullptr); diff --git a/dom/plugins/base/nsPluginHost.h b/dom/plugins/base/nsPluginHost.h index d27f8ab9ad28..d1748f30eaed 100644 --- a/dom/plugins/base/nsPluginHost.h +++ b/dom/plugins/base/nsPluginHost.h @@ -175,7 +175,7 @@ public: nsTArray< nsRefPtr > *InstanceArray(); - void DestroyRunningInstances(nsTArray >* aReloadDocs, nsPluginTag* aPluginTag); + void DestroyRunningInstances(nsPluginTag* aPluginTag); // Return the tag for |aLibrary| if found, nullptr if not. nsPluginTag* FindTagForLibrary(PRLibrary* aLibrary); diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 4f704827bc87..077ca1ca2d9b 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -2504,8 +2504,7 @@ nsEventStatus nsPluginInstanceOwner::ProcessEvent(const nsGUIEvent& anEvent) nsresult nsPluginInstanceOwner::Destroy() { - if (mObjectFrame) - mObjectFrame->SetInstanceOwner(nullptr); + SetFrame(nullptr); #ifdef XP_MACOSX RemoveFromCARefreshTimer(); @@ -3377,7 +3376,7 @@ void nsPluginInstanceOwner::SetFrame(nsObjectFrame *aFrame) } mObjectFrame->FixupWindow(mObjectFrame->GetContentRectRelativeToSelf().Size()); mObjectFrame->InvalidateFrame(); - + nsFocusManager* fm = nsFocusManager::GetFocusManager(); const nsIContent* content = aFrame->GetContent(); if (fm && content) { diff --git a/dom/plugins/test/mochitest/Makefile.in b/dom/plugins/test/mochitest/Makefile.in index b9c4d65cb0cb..170331b24d0a 100644 --- a/dom/plugins/test/mochitest/Makefile.in +++ b/dom/plugins/test/mochitest/Makefile.in @@ -67,6 +67,9 @@ MOCHITEST_FILES = \ test_bug751809.html \ test_bug813906.html \ test_bug784131.html \ + test_bug854082.html \ + test_bug863792.html \ + file_bug863792.html \ test_enumerate.html \ test_npruntime_construct.html \ 307-xo-redirect.sjs \ diff --git a/dom/plugins/test/mochitest/file_bug863792.html b/dom/plugins/test/mochitest/file_bug863792.html new file mode 100644 index 000000000000..b4d3dc2e074c --- /dev/null +++ b/dom/plugins/test/mochitest/file_bug863792.html @@ -0,0 +1,45 @@ + + + + File for Bug 863792 + + + + + + + + diff --git a/dom/plugins/test/mochitest/test_bug813906.html b/dom/plugins/test/mochitest/test_bug813906.html index 7d5a1814b86d..8dca8db9255d 100644 --- a/dom/plugins/test/mochitest/test_bug813906.html +++ b/dom/plugins/test/mochitest/test_bug813906.html @@ -28,6 +28,9 @@ function f() { diff --git a/dom/plugins/test/mochitest/test_bug854082.html b/dom/plugins/test/mochitest/test_bug854082.html new file mode 100644 index 000000000000..6561d02eef80 --- /dev/null +++ b/dom/plugins/test/mochitest/test_bug854082.html @@ -0,0 +1,39 @@ + + + + Test for Bug 854082 + + + + + + + + + + diff --git a/dom/plugins/test/mochitest/test_bug863792.html b/dom/plugins/test/mochitest/test_bug863792.html new file mode 100644 index 000000000000..0b0b8412a060 --- /dev/null +++ b/dom/plugins/test/mochitest/test_bug863792.html @@ -0,0 +1,38 @@ + + + + Test for Bug 863792 + + + + + + + + + diff --git a/dom/plugins/test/mochitest/test_cocoa_focus.html b/dom/plugins/test/mochitest/test_cocoa_focus.html index 51d41a3d9634..34026fd21b14 100644 --- a/dom/plugins/test/mochitest/test_cocoa_focus.html +++ b/dom/plugins/test/mochitest/test_cocoa_focus.html @@ -6,8 +6,6 @@ + + + + + +Mozilla Bug 858459 +

+ +
+ +
+ + diff --git a/layout/generic/crashtests/862947-1.html b/layout/generic/crashtests/862947-1.html new file mode 100644 index 000000000000..83e1903f43ca --- /dev/null +++ b/layout/generic/crashtests/862947-1.html @@ -0,0 +1,16 @@ + + + + + + + +
+ + + diff --git a/layout/generic/crashtests/crashtests.list b/layout/generic/crashtests/crashtests.list index ed4908107a5b..cd8ff7aea3a6 100644 --- a/layout/generic/crashtests/crashtests.list +++ b/layout/generic/crashtests/crashtests.list @@ -459,3 +459,4 @@ test-pref(layout.css.flexbox.enabled,true) load 847211-1.html load 849603.html test-pref(layout.css.flexbox.enabled,true) load 851396-1.html test-pref(layout.css.flexbox.enabled,true) load 854263-1.html +test-pref(layout.css.flexbox.enabled,true) load 862947-1.html diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 5ff888de6820..8dfe62e9064e 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -1793,6 +1793,27 @@ GetFlexContainer(nsIFrame* aFrame) } #endif // MOZ_FLEXBOX +// Flex items resolve percentage margin & padding against the flex +// container's height (which is the containing block height). +// For everything else: the CSS21 spec requires that margin and padding +// percentage values are calculated with respect to the *width* of the +// containing block, even for margin & padding in the vertical axis. +static nscoord +VerticalOffsetPercentBasis(const nsIFrame* aFrame, + nscoord aContainingBlockWidth, + nscoord aContainingBlockHeight) +{ + if (!aFrame->IsFlexItem()) { + return aContainingBlockWidth; + } + + if (aContainingBlockHeight == NS_AUTOHEIGHT) { + return 0; + } + + return aContainingBlockHeight; +} + // XXX refactor this code to have methods for each set of properties // we are computing: width,height,line-height; margin; offsets @@ -1808,19 +1829,13 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, aContainingBlockWidth, aContainingBlockHeight, aBorder, aPadding); - // If this is the root frame, then set the computed width and + // If this is a reflow root, then set the computed width and // height equal to the available space if (nullptr == parentReflowState) { - MOZ_ASSERT(!frame->IsFlexItem(), - "the root frame can't be a flex item, since being a flex item " - "requires that you have a parent"); - // Note that we pass the containing block width as the percent basis for - // both horizontal *and* vertical margins & padding, in our InitOffsets - // call here. This is correct per CSS 2.1; it'd be incorrect for e.g. flex - // items and grid items, but the root frame can't be either of those. // XXXldb This doesn't mean what it used to! InitOffsets(aContainingBlockWidth, - aContainingBlockWidth, + VerticalOffsetPercentBasis(frame, aContainingBlockWidth, + aContainingBlockHeight), aFrameType, aBorder, aPadding); // Override mComputedMargin since reflow roots start from the // frame's boundary, which is inside the margin. @@ -1868,19 +1883,11 @@ nsHTMLReflowState::InitConstraints(nsPresContext* aPresContext, } } - // Flex containers resolve percentage margin & padding against the flex - // container's height (which is the containing block height). - // For everything else: the CSS21 spec requires that margin and padding - // percentage values are calculated with respect to the *width* of the - // containing block, even for margin & padding in the vertical axis. // XXX Might need to also pass the CB height (not width) for page boxes, // too, if we implement them. - nscoord verticalPercentBasis = aContainingBlockWidth; - if (frame->IsFlexItem()) { - verticalPercentBasis = - aContainingBlockHeight == NS_AUTOHEIGHT ? 0 : aContainingBlockHeight; - } - InitOffsets(aContainingBlockWidth, verticalPercentBasis, + InitOffsets(aContainingBlockWidth, + VerticalOffsetPercentBasis(frame, aContainingBlockWidth, + aContainingBlockHeight), aFrameType, aBorder, aPadding); const nsStyleCoord &height = mStylePosition->mHeight; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index ca614f90676f..396a4aa89503 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -308,14 +308,19 @@ nsObjectFrame::DestroyFrom(nsIFrame* aDestructRoot) // Tell content owner of the instance to disconnect its frame. nsCOMPtr objContent(do_QueryInterface(mContent)); NS_ASSERTION(objContent, "Why not an object loading content?"); + + // The content might not have a reference to the instance owner any longer in + // the case of re-entry during instantiation or teardown, so make sure we're + // dissociated. + if (mInstanceOwner) { + mInstanceOwner->SetFrame(nullptr); + } objContent->HasNewFrame(nullptr); if (mBackgroundSink) { mBackgroundSink->Destroy(); } - SetInstanceOwner(nullptr); - nsObjectFrameSuper::DestroyFrom(aDestructRoot); } @@ -775,6 +780,10 @@ nsObjectFrame::UnregisterPluginForGeometryUpdates() void nsObjectFrame::SetInstanceOwner(nsPluginInstanceOwner* aOwner) { + // The ownership model here is historically fuzzy. This should only be called + // by nsPluginInstanceOwner when it is given a new frame, and + // nsObjectLoadingContent should be arbitrating frame-ownership via its + // HasNewFrame callback. mInstanceOwner = aOwner; if (mInstanceOwner) { return; @@ -874,7 +883,7 @@ nsObjectFrame::DidReflow(nsPresContext* aPresContext, // The view is created hidden; once we have reflowed it and it has been // positioned then we show it. - if (aStatus != nsDidReflowStatus::FINISHED) + if (aStatus != nsDidReflowStatus::FINISHED) return rv; if (HasView()) { diff --git a/layout/generic/nsTextFrameThebes.cpp b/layout/generic/nsTextFrameThebes.cpp index 15a5d95328a8..b8423abd235e 100644 --- a/layout/generic/nsTextFrameThebes.cpp +++ b/layout/generic/nsTextFrameThebes.cpp @@ -83,11 +83,9 @@ #include "GeckoProfiler.h" #ifdef DEBUG -#undef NOISY_BLINK #undef NOISY_REFLOW #undef NOISY_TRIM #else -#undef NOISY_BLINK #undef NOISY_REFLOW #undef NOISY_TRIM #endif diff --git a/layout/reftests/border-radius/reftest.list b/layout/reftests/border-radius/reftest.list index 4de813a0df30..4b92cd00876f 100644 --- a/layout/reftests/border-radius/reftest.list +++ b/layout/reftests/border-radius/reftest.list @@ -76,6 +76,8 @@ skip-if(B2G) random-if(winWidget) HTTP(..) == corner-joins-2.xhtml corner-joins- skip-if(B2G) fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,20) fuzzy-if(Android&&browserIsRemote,7,146) fuzzy-if(Android&&!browserIsRemote,166,248) == scroll-1.html scroll-1-ref.html # see bug 732535 +== transforms-1.html transforms-1-ref.html + == zero-radius-clip-1.html zero-radius-clip-ref.html == iframe-1.html iframe-1-ref.html diff --git a/layout/reftests/border-radius/transforms-1-ref.html b/layout/reftests/border-radius/transforms-1-ref.html new file mode 100644 index 000000000000..2aefee939f1d --- /dev/null +++ b/layout/reftests/border-radius/transforms-1-ref.html @@ -0,0 +1,24 @@ + + + + +
+ + diff --git a/layout/reftests/border-radius/transforms-1.html b/layout/reftests/border-radius/transforms-1.html new file mode 100644 index 000000000000..5b64b3c293c0 --- /dev/null +++ b/layout/reftests/border-radius/transforms-1.html @@ -0,0 +1,25 @@ + + + + +
+ + diff --git a/layout/reftests/forms/input/range/input-75pct-common-ref.html b/layout/reftests/forms/input/range/input-75pct-common-ref.html new file mode 100644 index 000000000000..929887d65a3e --- /dev/null +++ b/layout/reftests/forms/input/range/input-75pct-common-ref.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/layout/reftests/forms/input/range/input-75pct-unthemed-common-ref.html b/layout/reftests/forms/input/range/input-75pct-unthemed-common-ref.html new file mode 100644 index 000000000000..a4b73e23ad6b --- /dev/null +++ b/layout/reftests/forms/input/range/input-75pct-unthemed-common-ref.html @@ -0,0 +1,6 @@ + + + + + + diff --git a/layout/reftests/forms/input/range/input-stepDown-unthemed.html b/layout/reftests/forms/input/range/input-stepDown-unthemed.html new file mode 100644 index 000000000000..fbddb69ada55 --- /dev/null +++ b/layout/reftests/forms/input/range/input-stepDown-unthemed.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/input-stepDown.html b/layout/reftests/forms/input/range/input-stepDown.html new file mode 100644 index 000000000000..8e63ea8b9e76 --- /dev/null +++ b/layout/reftests/forms/input/range/input-stepDown.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/input-stepUp-unthemed.html b/layout/reftests/forms/input/range/input-stepUp-unthemed.html new file mode 100644 index 000000000000..613049e9ef40 --- /dev/null +++ b/layout/reftests/forms/input/range/input-stepUp-unthemed.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/input-stepUp.html b/layout/reftests/forms/input/range/input-stepUp.html new file mode 100644 index 000000000000..11af00b36cc6 --- /dev/null +++ b/layout/reftests/forms/input/range/input-stepUp.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/input-value-prop-unthemed.html b/layout/reftests/forms/input/range/input-value-prop-unthemed.html new file mode 100644 index 000000000000..46387af2ea81 --- /dev/null +++ b/layout/reftests/forms/input/range/input-value-prop-unthemed.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/input-value-prop.html b/layout/reftests/forms/input/range/input-value-prop.html new file mode 100644 index 000000000000..66be8370458f --- /dev/null +++ b/layout/reftests/forms/input/range/input-value-prop.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/input-valueAsNumber-prop-unthemed.html b/layout/reftests/forms/input/range/input-valueAsNumber-prop-unthemed.html new file mode 100644 index 000000000000..a2a6eb6cce75 --- /dev/null +++ b/layout/reftests/forms/input/range/input-valueAsNumber-prop-unthemed.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/input-valueAsNumber-prop.html b/layout/reftests/forms/input/range/input-valueAsNumber-prop.html new file mode 100644 index 000000000000..c00fe8bf4c56 --- /dev/null +++ b/layout/reftests/forms/input/range/input-valueAsNumber-prop.html @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/layout/reftests/forms/input/range/reftest.list b/layout/reftests/forms/input/range/reftest.list index 28ef373f80f1..ffb79ecd5613 100644 --- a/layout/reftests/forms/input/range/reftest.list +++ b/layout/reftests/forms/input/range/reftest.list @@ -14,6 +14,16 @@ default-preferences pref(dom.experimental_forms_range,true) != input-range-different-fraction-of-range-unthemed-1.html input-range-different-fraction-of-range-unthemed-1-notref.html == input-range-same-fraction-of-range-unthemed-1.html input-range-same-fraction-of-range-unthemed-1-ref.html +# dynamic value changes: +== input-value-prop-unthemed.html input-75pct-unthemed-common-ref.html +== input-value-prop.html input-75pct-common-ref.html +== input-valueAsNumber-prop-unthemed.html input-75pct-unthemed-common-ref.html +== input-valueAsNumber-prop.html input-75pct-common-ref.html +== input-stepDown-unthemed.html input-75pct-unthemed-common-ref.html +== input-stepDown.html input-75pct-common-ref.html +== input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html +== input-stepUp.html input-75pct-common-ref.html + # 'direction' property: == input-range-direction-unthemed-1.html input-range-direction-unthemed-1-ref.html diff --git a/layout/style/ImageDocument.css b/layout/style/ImageDocument.css new file mode 100644 index 000000000000..80a7bdeb56e6 --- /dev/null +++ b/layout/style/ImageDocument.css @@ -0,0 +1,32 @@ +/* -*- 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/. */ + +/* + * This CSS stylesheet defines the rules to be applied to any ImageDocuments, + * including those in frames. +*/ + +@media not print { + .overflowing { + cursor: -moz-zoom-out; + } + + .shrinkToFit, + .scaleToDevicePixels { + cursor: -moz-zoom-in; + } +} + +@media print { + /* We must declare the image as a block element. If we stay as + an inline element, our parent LineBox will be inline too and + ignore the available height during reflow. + This is bad during printing, it means tall image frames won't know + the size of the paper and cannot break into continuations along + multiple pages. */ + img { + display: block; + } +} diff --git a/layout/style/Makefile.in b/layout/style/Makefile.in index 286089337c47..85afd586e120 100644 --- a/layout/style/Makefile.in +++ b/layout/style/Makefile.in @@ -87,6 +87,7 @@ LOCAL_INCLUDES += \ _FILES = \ contenteditable.css \ designmode.css \ + ImageDocument.css \ TopLevelImageDocument.css \ TopLevelVideoDocument.css \ $(NULL) diff --git a/layout/style/TopLevelImageDocument.css b/layout/style/TopLevelImageDocument.css index 383365bc140e..e19d9e23bbc9 100644 --- a/layout/style/TopLevelImageDocument.css +++ b/layout/style/TopLevelImageDocument.css @@ -22,16 +22,8 @@ bottom: 0; left: 0; } -} -@media print { - /* We must declare the image as a block element. If we stay as - an inline element, our parent LineBox will be inline too and - ignore the available height during reflow. - This is bad during printing, it means tall image frames won't know - the size of the paper and cannot break into continuations along - multiple pages. */ - img { - display: block; + .completeRotation { + transition: transform 0.3s ease 0s; } } diff --git a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp index 3e685ed9e9d8..37c6ec403b40 100644 --- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -364,23 +364,6 @@ PeerConnectionImpl::CreateRemoteSourceStreamInfo(nsRefPtr doc; - doc = nsContentUtils::GetDocumentFromScriptContext(sc); - if (doc) { - // Passing in line-# 1 hides peerconnection.js (shows document instead) - nsContentUtils::ReportToConsoleNonLocalized(NS_ConvertUTF8toUTF16 (aMsg), - nsIScriptError::warningFlag, logTag, doc, nullptr, EmptyString(), 1); - } - } -} -#endif - /** * In JS, an RTCConfiguration looks like this: * diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 5bfd5eedccdb..67b3731fc96b 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -804,17 +804,20 @@ abstract public class BrowserApp extends GeckoApp protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - // Don't update the url in the toolbar if the activity was cancelled, or if it was launched to pick - // a site. The order of these checks matters because data will be null if the activity was cancelled. - if (resultCode != Activity.RESULT_OK || - data.getStringExtra(AwesomeBar.TARGET_KEY).equals(AwesomeBar.Target.PICK_SITE.toString())) { - // We still need to call fromAwesomeBarSearch to perform the toolbar animation. - mBrowserToolbar.fromAwesomeBarSearch(null); - return; + String url = null; + + // Don't update the url in the toolbar if the activity was cancelled. + if (resultCode == Activity.RESULT_OK && data != null) { + // Don't update the url if the activity was launched to pick a site. + String targetKey = data.getStringExtra(AwesomeBar.TARGET_KEY); + if (!AwesomeBar.Target.PICK_SITE.toString().equals(targetKey)) { + // Update the toolbar with the url that was just entered. + url = data.getStringExtra(AwesomeBar.URL_KEY); + } } - // Otherwise, update the toolbar with the url that was just entered. - mBrowserToolbar.fromAwesomeBarSearch(data.getStringExtra(AwesomeBar.URL_KEY)); + // We always need to call fromAwesomeBarSearch to perform the toolbar animation. + mBrowserToolbar.fromAwesomeBarSearch(url); } public View getActionBarLayout() { diff --git a/mobile/android/base/ClickableWhenDisabledEditText.java b/mobile/android/base/ClickableWhenDisabledEditText.java new file mode 100644 index 000000000000..ba73849e2305 --- /dev/null +++ b/mobile/android/base/ClickableWhenDisabledEditText.java @@ -0,0 +1,25 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.widget.EditText; + +public class ClickableWhenDisabledEditText extends EditText { + public ClickableWhenDisabledEditText(Context context, AttributeSet attrs) { + super(context, attrs); + } + + @Override + public boolean onTouchEvent(MotionEvent event) { + if (!isEnabled() && event.getAction() == MotionEvent.ACTION_UP) { + return performClick(); + } + return super.onTouchEvent(event); + } +} diff --git a/mobile/android/base/CrashReporter.java b/mobile/android/base/CrashReporter.java index b2801ccdeb21..9ecaa81d448e 100644 --- a/mobile/android/base/CrashReporter.java +++ b/mobile/android/base/CrashReporter.java @@ -22,16 +22,20 @@ import java.nio.channels.FileChannel; import java.util.zip.GZIPOutputStream; import android.app.Activity; +import android.app.AlertDialog; import android.app.ProgressDialog; +import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; import android.os.Handler; +import android.text.TextUtils; import android.util.Log; import android.view.View; -import android.widget.Button; import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; public class CrashReporter extends Activity { @@ -108,8 +112,6 @@ public class CrashReporter extends Activity mProgressDialog = new ProgressDialog(this); mProgressDialog.setMessage(getString(R.string.sending_crash_report)); - final Button restartButton = (Button) findViewById(R.id.restart); - final Button closeButton = (Button) findViewById(R.id.close); String passedMinidumpPath = getIntent().getStringExtra(PASSED_MINI_DUMP_KEY); File passedMinidumpFile = new File(passedMinidumpPath); File pendingDir = new File(getFilesDir(), PENDING_SUFFIX); @@ -132,6 +134,66 @@ public class CrashReporter extends Activity editor.putBoolean(GeckoApp.PREFS_WAS_STOPPED, true); editor.putBoolean(GeckoApp.PREFS_CRASHED, true); editor.commit(); + + final CheckBox allowContactCheckBox = (CheckBox) findViewById(R.id.allow_contact); + final CheckBox includeUrlCheckBox = (CheckBox) findViewById(R.id.include_url); + final CheckBox sendReportCheckBox = (CheckBox) findViewById(R.id.send_report); + final EditText commentsEditText = (EditText) findViewById(R.id.comment); + final EditText emailEditText = (EditText) findViewById(R.id.email); + + sendReportCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) { + commentsEditText.setEnabled(isChecked); + commentsEditText.requestFocus(); + + includeUrlCheckBox.setEnabled(isChecked); + allowContactCheckBox.setEnabled(isChecked); + emailEditText.setEnabled(isChecked && allowContactCheckBox.isChecked()); + } + }); + + allowContactCheckBox.setOnCheckedChangeListener(new CheckBox.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton checkbox, boolean isChecked) { + // We need to check isEnabled() here because this listener is + // fired on rotation -- even when the checkbox is disabled. + emailEditText.setEnabled(checkbox.isEnabled() && isChecked); + emailEditText.requestFocus(); + } + }); + + emailEditText.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Even if the email EditText is disabled, allow it to be + // clicked and focused. + if (sendReportCheckBox.isChecked() && !v.isEnabled()) { + allowContactCheckBox.setChecked(true); + v.setEnabled(true); + v.requestFocus(); + } + } + }); + } + + @Override + public void onBackPressed() { + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(R.string.crash_closing_alert); + builder.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + builder.setPositiveButton(R.string.button_ok, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + CrashReporter.this.finish(); + } + }); + builder.show(); } private void backgroundSendReport() { @@ -304,6 +366,16 @@ public class CrashReporter extends Activity sendPart(os, boundary, "Android_Logcat", readLogcat()); } + String comment = ((EditText) findViewById(R.id.comment)).getText().toString(); + if (!TextUtils.isEmpty(comment)) { + sendPart(os, boundary, "Comments", comment); + } + + if (((CheckBox) findViewById(R.id.allow_contact)).isChecked()) { + String email = ((EditText) findViewById(R.id.email)).getText().toString(); + sendPart(os, boundary, "Email", email); + } + sendFile(os, boundary, MINI_DUMP_PATH_KEY, minidumpFile); os.write(("\r\n--" + boundary + "--\r\n").getBytes()); os.flush(); diff --git a/mobile/android/base/GeckoJavaSampler.java b/mobile/android/base/GeckoJavaSampler.java new file mode 100644 index 000000000000..130d5643f97f --- /dev/null +++ b/mobile/android/base/GeckoJavaSampler.java @@ -0,0 +1,184 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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/. */ + +package org.mozilla.gecko; + +import android.util.Log; +import java.lang.Thread; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class GeckoJavaSampler { + private static final String LOGTAG = "JavaSampler"; + private static Thread sSamplingThread = null; + private static SamplingThread sSamplingRunnable = null; + private static Thread sMainThread = null; + + // Use the same timer primitive as the profiler + // to get a perfect sample syncing. + private static native double getProfilerTime(); + + private static class Sample { + public Frame[] mFrames; + public double mTime; + public Sample(StackTraceElement[] aStack) { + mFrames = new Frame[aStack.length]; + mTime = getProfilerTime(); + for (int i = 0; i < aStack.length; i++) { + mFrames[aStack.length - 1 - i] = new Frame(); + mFrames[aStack.length - 1 - i].fileName = aStack[i].getFileName(); + mFrames[aStack.length - 1 - i].lineNo = aStack[i].getLineNumber(); + mFrames[aStack.length - 1 - i].methodName = aStack[i].getMethodName(); + mFrames[aStack.length - 1 - i].className = aStack[i].getClassName(); + } + } + } + private static class Frame { + public String fileName; + public int lineNo; + public String methodName; + public String className; + } + + private static class SamplingThread implements Runnable { + private final int mInterval; + private final int mSampleCount; + + private boolean mPauseSampler = false; + private boolean mStopSampler = false; + + private Map mSamples = new HashMap(); + private int mSamplePos; + + public SamplingThread(final int aInterval, final int aSampleCount) { + // If we sample faster then 10ms we get to many missed samples + mInterval = Math.max(10, aInterval); + mSampleCount = aSampleCount; + } + + public void run() { + synchronized (GeckoJavaSampler.class) { + mSamples.put(0, new Sample[mSampleCount]); + mSamplePos = 0; + + // Find the main thread + Set threadSet = Thread.getAllStackTraces().keySet(); + for (Thread t : threadSet) { + if (t.getName().compareToIgnoreCase("main") == 0) { + sMainThread = t; + break; + } + } + + if (sMainThread == null) { + Log.e(LOGTAG, "Main thread not found"); + return; + } + } + + while (true) { + try { + Thread.sleep(mInterval); + } catch (InterruptedException e) { + e.printStackTrace(); + } + synchronized (GeckoJavaSampler.class) { + if (!mPauseSampler) { + StackTraceElement[] bt = sMainThread.getStackTrace(); + mSamples.get(0)[mSamplePos] = new Sample(bt); + mSamplePos = (mSamplePos+1) % mSamples.get(0).length; + } + if (mStopSampler) { + break; + } + } + } + } + + private Sample getSample(int aThreadId, int aSampleId) { + if (aThreadId < mSamples.size() && aSampleId < mSamples.get(aThreadId).length && + mSamples.get(aThreadId)[aSampleId] != null) { + int startPos = 0; + if (mSamples.get(aThreadId)[mSamplePos] != null) { + startPos = mSamplePos; + } + int readPos = (startPos + aSampleId) % mSamples.get(aThreadId).length; + return mSamples.get(aThreadId)[readPos]; + } + return null; + } + } + + public synchronized static String getThreadName(int aThreadId) { + if (aThreadId == 0 && sMainThread != null) { + return sMainThread.getName(); + } + return null; + } + + private synchronized static Sample getSample(int aThreadId, int aSampleId) { + return sSamplingRunnable.getSample(aThreadId, aSampleId); + } + public synchronized static double getSampleTime(int aThreadId, int aSampleId) { + Sample sample = getSample(aThreadId, aSampleId); + if (sample != null) { + System.out.println("Sample: " + sample.mTime); + return sample.mTime; + } + return 0; + } + public synchronized static String getFrameName(int aThreadId, int aSampleId, int aFrameId) { + Sample sample = getSample(aThreadId, aSampleId); + if (sample != null && aFrameId < sample.mFrames.length) { + Frame frame = sample.mFrames[aFrameId]; + if (frame == null) { + return null; + } + return frame.className + "." + frame.methodName + "()"; + } + return null; + } + + public static void start(int aInterval, int aSamples) { + synchronized (GeckoJavaSampler.class) { + sSamplingRunnable = new SamplingThread(aInterval, aSamples); + sSamplingThread = new Thread(sSamplingRunnable, "Java Sampler"); + sSamplingThread.start(); + } + } + + public static void pause() { + synchronized (GeckoJavaSampler.class) { + sSamplingRunnable.mPauseSampler = true; + } + } + + public static void unpause() { + synchronized (GeckoJavaSampler.class) { + sSamplingRunnable.mPauseSampler = false; + } + } + + public static void stop() { + synchronized (GeckoJavaSampler.class) { + if (sSamplingThread == null) { + return; + } + + sSamplingRunnable.mStopSampler = true; + try { + sSamplingThread.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + sSamplingThread = null; + sSamplingRunnable = null; + } + } +} + + + diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 2877d6de239d..b0c967433431 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -69,6 +69,7 @@ FENNEC_JAVA_FILES = \ CameraVideoResultHandler.java \ CanvasDelegate.java \ CheckableLinearLayout.java \ + ClickableWhenDisabledEditText.java \ SyncPreference.java \ db/BrowserDB.java \ db/LocalBrowserDB.java \ @@ -107,6 +108,7 @@ FENNEC_JAVA_FILES = \ GeckoPopupMenu.java \ GeckoSmsManager.java \ GeckoThread.java \ + GeckoJavaSampler.java \ GlobalHistory.java \ GeckoViewsFactory.java \ HeightChangeAnimation.java \ @@ -1043,6 +1045,7 @@ MOZ_ANDROID_DRAWABLES += \ mobile/android/base/resources/drawable/tab_thumbnail.xml \ mobile/android/base/resources/drawable/tabs_level.xml \ mobile/android/base/resources/drawable/tabs_panel_indicator.xml \ + mobile/android/base/resources/drawable/textbox_bg.xml \ mobile/android/base/resources/drawable/webapp_titlebar_bg.xml \ $(NULL) @@ -1152,6 +1155,7 @@ jars: CLASSES_WITH_JNI= \ org.mozilla.gecko.GeckoAppShell \ + org.mozilla.gecko.GeckoJavaSampler \ $(NULL) ifdef MOZ_WEBSMS_BACKEND diff --git a/mobile/android/base/background/BackgroundService.java b/mobile/android/base/background/BackgroundService.java index 6aa963212092..6e17818b878e 100644 --- a/mobile/android/base/background/BackgroundService.java +++ b/mobile/android/base/background/BackgroundService.java @@ -27,6 +27,13 @@ public abstract class BackgroundService extends IntentService { super(threadName); } + public static void runIntentInService(Context context, Intent intent, Class serviceClass) { + Intent service = new Intent(context, serviceClass); + service.setAction(intent.getAction()); + service.putExtras(intent); + context.startService(service); + } + /** * Returns true if the OS will allow us to perform background * data operations. This logic varies by OS version. diff --git a/mobile/android/base/background/announcements/AnnouncementsBroadcastReceiver.java b/mobile/android/base/background/announcements/AnnouncementsBroadcastReceiver.java index 520950fefbe7..910ccece239e 100644 --- a/mobile/android/base/background/announcements/AnnouncementsBroadcastReceiver.java +++ b/mobile/android/base/background/announcements/AnnouncementsBroadcastReceiver.java @@ -4,6 +4,8 @@ package org.mozilla.gecko.background.announcements; +import org.mozilla.gecko.background.BackgroundService; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -28,9 +30,6 @@ public class AnnouncementsBroadcastReceiver extends BroadcastReceiver { return; } - Intent service = new Intent(context, AnnouncementsBroadcastService.class); - service.putExtras(intent); - service.setAction(intent.getAction()); - context.startService(service); + BackgroundService.runIntentInService(context, intent, AnnouncementsBroadcastService.class); } } diff --git a/mobile/android/base/background/announcements/AnnouncementsStartReceiver.java b/mobile/android/base/background/announcements/AnnouncementsStartReceiver.java index 0c74531857c0..94301af2cf8b 100644 --- a/mobile/android/base/background/announcements/AnnouncementsStartReceiver.java +++ b/mobile/android/base/background/announcements/AnnouncementsStartReceiver.java @@ -4,6 +4,7 @@ package org.mozilla.gecko.background.announcements; +import org.mozilla.gecko.background.BackgroundService; import org.mozilla.gecko.background.common.log.Logger; import android.content.BroadcastReceiver; @@ -24,7 +25,6 @@ public class AnnouncementsStartReceiver extends BroadcastReceiver { } Logger.debug(LOG_TAG, "AnnouncementsStartReceiver.onReceive()."); - Intent service = new Intent(context, AnnouncementsService.class); - context.startService(service); + BackgroundService.runIntentInService(context, intent, AnnouncementsService.class); } } diff --git a/mobile/android/base/db/BrowserProvider.java b/mobile/android/base/db/BrowserProvider.java index 416b68ecec6b..d7f8a08fd569 100644 --- a/mobile/android/base/db/BrowserProvider.java +++ b/mobile/android/base/db/BrowserProvider.java @@ -73,7 +73,7 @@ public class BrowserProvider extends ContentProvider { static final String DATABASE_NAME = "browser.db"; - static final int DATABASE_VERSION = 14; + static final int DATABASE_VERSION = 15; // Maximum age of deleted records to be cleaned up (20 days in ms) static final long MAX_AGE_OF_DELETED_RECORDS = 86400000 * 20; @@ -1676,6 +1676,37 @@ public class BrowserProvider extends ContentProvider { R.string.bookmarks_folder_pinned, 6); } + private void upgradeDatabaseFrom14to15(SQLiteDatabase db) { + Cursor c = null; + try { + // Get all the pinned bookmarks + c = db.query(TABLE_BOOKMARKS, + new String[] { Bookmarks._ID, Bookmarks.URL }, + Bookmarks.PARENT + " = ?", + new String[] { Integer.toString(Bookmarks.FIXED_PINNED_LIST_ID) }, + null, null, null); + + while (c.moveToNext()) { + // Check if this URL can be parsed as a URI with a valid scheme. + String url = c.getString(c.getColumnIndexOrThrow(Bookmarks.URL)); + if (Uri.parse(url).getScheme() != null) { + continue; + } + + // If it can't, update the URL to be an encoded "user-entered" value. + ContentValues values = new ContentValues(1); + String newUrl = Uri.fromParts("user-entered", url, null).toString(); + values.put(Bookmarks.URL, newUrl); + db.update(TABLE_BOOKMARKS, values, Bookmarks._ID + " = ?", + new String[] { Integer.toString(c.getInt(c.getColumnIndexOrThrow(Bookmarks._ID))) }); + } + } finally { + if (c != null) { + c.close(); + } + } + } + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { debug("Upgrading browser.db: " + db.getPath() + " from " + @@ -1736,6 +1767,10 @@ public class BrowserProvider extends ContentProvider { case 14: upgradeDatabaseFrom13to14(db); break; + + case 15: + upgradeDatabaseFrom14to15(db); + break; } } diff --git a/mobile/android/base/jni-generator.py b/mobile/android/base/jni-generator.py index 15ccee4758e1..c7988152b522 100644 --- a/mobile/android/base/jni-generator.py +++ b/mobile/android/base/jni-generator.py @@ -69,7 +69,7 @@ class Generator: returnValue = '' elif returnType == 'jobject': returnValue = 'NULL' - elif returnType in ('jint', 'jfloat'): + elif returnType in ('jint', 'jfloat', 'jdouble'): returnValue = '0' else: raise Exception(('Unsupported JNI return type %s found; ' diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index c5c69a677d18..9786d9ebd880 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -14,15 +14,18 @@ - - - - + + + + + + + + + - - diff --git a/mobile/android/base/resources/drawable/textbox_bg.xml b/mobile/android/base/resources/drawable/textbox_bg.xml new file mode 100644 index 000000000000..bcc884a08ce8 --- /dev/null +++ b/mobile/android/base/resources/drawable/textbox_bg.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/mobile/android/base/resources/layout/crash_reporter.xml b/mobile/android/base/resources/layout/crash_reporter.xml index d8468e19a481..02e7b4ebc619 100644 --- a/mobile/android/base/resources/layout/crash_reporter.xml +++ b/mobile/android/base/resources/layout/crash_reporter.xml @@ -5,61 +5,118 @@ + android:layout_height="fill_parent" + android:fillViewport="true"> + android:background="@color/background_normal"> - - - - - - - + + + + + + + + + + + + + + + + + + + - + android:layout_gravity="bottom"> +