From 8cc485b46d439c887610cd7fde49168f6de64549 Mon Sep 17 00:00:00 2001 From: Mike de Boer Date: Wed, 18 Sep 2013 11:06:07 +0200 Subject: [PATCH 01/56] Bug 870865: update disabled text color on OSX. r=mstange, ui-r=shorlander --- widget/cocoa/nsLookAndFeel.mm | 2 +- widget/tests/test_platform_colors.xul | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/widget/cocoa/nsLookAndFeel.mm b/widget/cocoa/nsLookAndFeel.mm index 938224f881f..2fd0ff2e451 100644 --- a/widget/cocoa/nsLookAndFeel.mm +++ b/widget/cocoa/nsLookAndFeel.mm @@ -249,7 +249,7 @@ nsLookAndFeel::NativeGetColor(ColorID aID, nscolor &aColor) aColor = GetColorFromNSColor([NSColor selectedMenuItemTextColor]); break; case eColorID__moz_mac_disabledtoolbartext: - aColor = NS_RGB(0x3F,0x3F,0x3F); + aColor = GetColorFromNSColor([NSColor disabledControlTextColor]); break; case eColorID__moz_mac_menuselect: aColor = GetColorFromNSColor([NSColor alternateSelectedControlColor]); diff --git a/widget/tests/test_platform_colors.xul b/widget/tests/test_platform_colors.xul index c6777648de9..5a4bc40c7c6 100644 --- a/widget/tests/test_platform_colors.xul +++ b/widget/tests/test_platform_colors.xul @@ -75,7 +75,7 @@ var colors = { "-moz-mac-menushadow": ["rgb(163, 163, 163)"], "-moz-mac-menutextdisable": ["rgb(152, 152, 152)", "rgb(136, 136, 136)"], "-moz-mac-menutextselect": ["rgb(255, 255, 255)"], - "-moz-mac-disabledtoolbartext": ["rgb(63, 63, 63)"], + "-moz-mac-disabledtoolbartext": ["rgb(127, 127, 127)"], "-moz-mac-secondaryhighlight": ["rgb(212, 212, 212)"], "-moz-menuhover": ["rgb(115, 132, 153)", "rgb(127, 127, 127)", "rgb(56, 117, 215)", "rgb(255, 193, 31)", "rgb(243, 70, 72)", "rgb(255, 138, 34)", "rgb(102, 197, 71)", "rgb(140, 78, 184)"], "-moz-menuhovertext": ["rgb(255, 255, 255)", "rgb(255, 254, 254)", "rgb(254, 255, 254)"], From 1797d72f4cf6eea306eab29693c5f49c30f2d17a Mon Sep 17 00:00:00 2001 From: Panos Astithas Date: Wed, 18 Sep 2013 12:58:37 +0300 Subject: [PATCH 02/56] Ignore failures to get the lastPausedUrl when a tab is closing (bug 916458). r=fitzgen --- browser/devtools/debugger/test/Makefile.in | 1 + .../test/browser_dbg_clean-exit-window.js | 90 +++++++++++++++++++ browser/devtools/debugger/test/head.js | 8 +- toolkit/devtools/server/actors/script.js | 16 +++- 4 files changed, 107 insertions(+), 8 deletions(-) create mode 100644 browser/devtools/debugger/test/browser_dbg_clean-exit-window.js diff --git a/browser/devtools/debugger/test/Makefile.in b/browser/devtools/debugger/test/Makefile.in index 275083ae5cd..de15fad5367 100644 --- a/browser/devtools/debugger/test/Makefile.in +++ b/browser/devtools/debugger/test/Makefile.in @@ -22,6 +22,7 @@ MOCHITEST_BROWSER_TESTS = \ browser_dbg_breakpoints-pane.js \ browser_dbg_chrome-debugging.js \ browser_dbg_clean-exit.js \ + browser_dbg_clean-exit-window.js \ browser_dbg_cmd-blackbox.js \ browser_dbg_cmd-break.js \ browser_dbg_cmd-dbg.js \ diff --git a/browser/devtools/debugger/test/browser_dbg_clean-exit-window.js b/browser/devtools/debugger/test/browser_dbg_clean-exit-window.js new file mode 100644 index 00000000000..aa0afc2acf0 --- /dev/null +++ b/browser/devtools/debugger/test/browser_dbg_clean-exit-window.js @@ -0,0 +1,90 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +/** + * Test that closing a window with the debugger in a paused state exits cleanly. + */ + +let gDebuggee, gPanel, gDebugger, gWindow; + +const TAB_URL = EXAMPLE_URL + "doc_inline-debugger-statement.html"; + +function test() { + addWindow(TAB_URL) + .then(win => initDebugger(TAB_URL, win)) + .then(([aTab, aDebuggee, aPanel, aWindow]) => { + gDebuggee = aDebuggee; + gPanel = aPanel; + gDebugger = gPanel.panelWin; + gWindow = aWindow; + + return testCleanExit(gWindow); + }) + .then(null, aError => { + ok(false, "Got an error: " + aError.message + "\n" + aError.stack); + }); +} + +function testCleanExit(aWindow) { + let deferred = promise.defer(); + + gWindow = aWindow; + ok(!!gWindow, "Second window created."); + + gWindow.focus(); + + let topWindow = Services.wm.getMostRecentWindow("navigator:browser"); + is(topWindow, gWindow, + "The second window is on top."); + + let isActive = promise.defer(); + let isLoaded = promise.defer(); + + promise.all([isActive.promise, isLoaded.promise]).then(() => { + gWindow.BrowserChromeTest.runWhenReady(() => { + waitForSourceAndCaretAndScopes(gPanel, ".html", 16).then(() => { + is(gDebugger.gThreadClient.paused, true, + "Should be paused after the debugger statement."); + gWindow.close(); + deferred.resolve(); + finish(); + }); + + gDebuggee.runDebuggerStatement(); + }); + }); + + let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); + if (focusManager.activeWindow != gWindow) { + gWindow.addEventListener("activate", function onActivate(aEvent) { + if (aEvent.target != gWindow) { + return; + } + gWindow.removeEventListener("activate", onActivate, true); + isActive.resolve(); + }, true); + } else { + isActive.resolve(); + } + + let contentLocation = gWindow.content.location.href; + if (contentLocation != TAB_URL) { + gWindow.document.addEventListener("load", function onLoad(aEvent) { + if (aEvent.target.documentURI != TAB_URL) { + return; + } + gWindow.document.removeEventListener("load", onLoad, true); + isLoaded.resolve(); + }, true); + } else { + isLoaded.resolve(); + } + return deferred.promise; +} + +registerCleanupFunction(function() { + gWindow = null; + gDebuggee = null; + gPanel = null; + gDebugger = null; +}); diff --git a/browser/devtools/debugger/test/head.js b/browser/devtools/debugger/test/head.js index 2cfbc601d65..19c1582c831 100644 --- a/browser/devtools/debugger/test/head.js +++ b/browser/devtools/debugger/test/head.js @@ -416,18 +416,18 @@ function backspaceText(aElement, aTimes) { } } -function getTab(aTarget) { +function getTab(aTarget, aWindow) { if (aTarget instanceof XULElement) { return promise.resolve(aTarget); } else { - return addTab(aTarget); + return addTab(aTarget, aWindow); } } function initDebugger(aTarget, aWindow) { info("Initializing a debugger panel."); - return getTab(aTarget).then(aTab => { + return getTab(aTarget, aWindow).then(aTab => { info("Debugee tab added successfully: " + aTarget); let deferred = promise.defer(); @@ -445,7 +445,7 @@ function initDebugger(aTarget, aWindow) { info("Debugger client resumed successfully."); prepareDebugger(debuggerPanel); - deferred.resolve([aTab, debuggee, debuggerPanel]); + deferred.resolve([aTab, debuggee, debuggerPanel, aWindow]); }); }); diff --git a/toolkit/devtools/server/actors/script.js b/toolkit/devtools/server/actors/script.js index 1d9b3692b23..0ae4530dcf6 100644 --- a/toolkit/devtools/server/actors/script.js +++ b/toolkit/devtools/server/actors/script.js @@ -288,9 +288,17 @@ EventLoopStack.prototype = { * The URL of the debuggee who pushed the event loop on top of the stack. */ get lastPausedUrl() { - return this.size > 0 - ? this._inspector.lastNestRequestor.url - : null; + let url = null; + if (this.size > 0) { + try { + url = this._inspector.lastNestRequestor.url + } catch (e) { + // The tab's URL getter may throw if the tab is destroyed by the time + // this code runs, but we don't really care at this point. + dumpn(e); + } + } + return url; }, /** @@ -936,7 +944,7 @@ ThreadActor.prototype = { // In case of multiple nested event loops (due to multiple debuggers open in // different tabs or multiple debugger clients connected to the same tab) // only allow resumption in a LIFO order. - if (this._nestedEventLoops.size + if (this._nestedEventLoops.size && this._nestedEventLoops.lastPausedUrl && this._nestedEventLoops.lastPausedUrl !== this._hooks.url) { return { error: "wrongOrder", From dd88c6e0f30ca3c700bfd717185adbbadd2ab130 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Wed, 18 Sep 2013 22:34:57 +1200 Subject: [PATCH 03/56] Bug 917057 - Reduce Tabs category left indent after moved into General category; r=Unfocused; ui-r=fang --- .../preferences/in-content/main.xul | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/browser/components/preferences/in-content/main.xul b/browser/components/preferences/in-content/main.xul index 1e3460772b6..f960ea71579 100644 --- a/browser/components/preferences/in-content/main.xul +++ b/browser/components/preferences/in-content/main.xul @@ -215,34 +215,27 @@ accesskey="&newWindowsAsTabs.accesskey;" preference="browser.link.open_newwindow" onsyncfrompreference="return gMainPane.readLinkTarget();" - onsynctopreference="return gMainPane.writeLinkTarget();" - class="indent"/> + onsynctopreference="return gMainPane.writeLinkTarget();"/> + preference="browser.tabs.warnOnClose"/> + preference="browser.tabs.warnOnOpen"/> + preference="browser.sessionstore.restore_on_demand"/> + preference="browser.tabs.loadInBackground"/> #ifdef XP_WIN + preference="browser.taskbar.previews.enable"/> #endif - From e295a05f9b13203aa9a10cae6100018d69813242 Mon Sep 17 00:00:00 2001 From: Paolo Amadini Date: Wed, 18 Sep 2013 15:16:00 +0200 Subject: [PATCH 04/56] Bug 906620 - Implement the taskbar indicator for downloads. r=enn --- browser/base/content/browser.js | 29 +-- .../downloads/src/DownloadsTaskbar.jsm | 180 ++++++++++++++++++ browser/components/downloads/src/moz.build | 1 + 3 files changed, 199 insertions(+), 11 deletions(-) create mode 100644 browser/components/downloads/src/DownloadsTaskbar.jsm diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 8ab3b95ade3..552fc8a826c 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1119,18 +1119,25 @@ var gBrowserInit = { // If the user manually opens the download manager before the timeout, the // downloads will start right away, and getting the service again won't hurt. setTimeout(function() { - let DownloadsCommon = - Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon; - if (DownloadsCommon.useJSTransfer) { - // Open the data link without initalizing nsIDownloadManager. - DownloadsCommon.initializeAllDataLinks(); - } else { - // Initalizing nsIDownloadManager will trigger the data link. - Services.downloads; + try { + let DownloadsCommon = + Cu.import("resource:///modules/DownloadsCommon.jsm", {}).DownloadsCommon; + if (DownloadsCommon.useJSTransfer) { + // Open the data link without initalizing nsIDownloadManager. + DownloadsCommon.initializeAllDataLinks(); + let DownloadsTaskbar = + Cu.import("resource:///modules/DownloadsTaskbar.jsm", {}).DownloadsTaskbar; + DownloadsTaskbar.registerIndicator(window); + } else { + // Initalizing nsIDownloadManager will trigger the data link. + Services.downloads; + let DownloadTaskbarProgress = + Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress; + DownloadTaskbarProgress.onBrowserWindowLoad(window); + } + } catch (ex) { + Cu.reportError(ex); } - let DownloadTaskbarProgress = - Cu.import("resource://gre/modules/DownloadTaskbarProgress.jsm", {}).DownloadTaskbarProgress; - DownloadTaskbarProgress.onBrowserWindowLoad(window); }, 10000); // The object handling the downloads indicator is also initialized here in the diff --git a/browser/components/downloads/src/DownloadsTaskbar.jsm b/browser/components/downloads/src/DownloadsTaskbar.jsm new file mode 100644 index 00000000000..18a9fd245b4 --- /dev/null +++ b/browser/components/downloads/src/DownloadsTaskbar.jsm @@ -0,0 +1,180 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */ +/* 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/. */ + +/** + * Handles the download progress indicator in the taskbar. + */ + +"use strict"; + +this.EXPORTED_SYMBOLS = [ + "DownloadsTaskbar", +]; + +//////////////////////////////////////////////////////////////////////////////// +//// Globals + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +XPCOMUtils.defineLazyModuleGetter(this, "Downloads", + "resource://gre/modules/Downloads.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow", + "resource:///modules/RecentWindow.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "Services", + "resource://gre/modules/Services.jsm"); + +XPCOMUtils.defineLazyGetter(this, "gWinTaskbar", function () { + if (!("@mozilla.org/windows-taskbar;1" in Cc)) { + return null; + } + let winTaskbar = Cc["@mozilla.org/windows-taskbar;1"] + .getService(Ci.nsIWinTaskbar); + return winTaskbar.available && winTaskbar; +}); + +XPCOMUtils.defineLazyGetter(this, "gMacTaskbarProgress", function () { + return ("@mozilla.org/widget/macdocksupport;1" in Cc) && + Cc["@mozilla.org/widget/macdocksupport;1"] + .getService(Ci.nsITaskbarProgress); +}); + +//////////////////////////////////////////////////////////////////////////////// +//// DownloadsTaskbar + +/** + * Handles the download progress indicator in the taskbar. + */ +this.DownloadsTaskbar = { + /** + * Underlying DownloadSummary providing the aggregate download information, or + * null if the indicator has never been initialized. + */ + _summary: null, + + /** + * nsITaskbarProgress object to which download information is dispatched. + * This can be null if the indicator has never been initialized or if the + * indicator is currently hidden on Windows. + */ + _taskbarProgress: null, + + /** + * This method is called after a new browser window is opened, and ensures + * that the download progress indicator is displayed in the taskbar. + * + * On Windows, the indicator is attached to the first browser window that + * calls this method. When the window is closed, the indicator is moved to + * another browser window, if available, in no particular order. When there + * are no browser windows visible, the indicator is hidden. + * + * On Mac OS X, the indicator is initialized globally when this method is + * called for the first time. Subsequent calls have no effect. + * + * @param aBrowserWindow + * nsIDOMWindow object of the newly opened browser window to which the + * indicator may be attached. + */ + registerIndicator: function (aBrowserWindow) + { + if (!this._taskbarProgress) { + if (gMacTaskbarProgress) { + // On Mac OS X, we have to register the global indicator only once. + this._taskbarProgress = gMacTaskbarProgress; + // Free the XPCOM reference on shutdown, to prevent detecting a leak. + Services.obs.addObserver(() => { + this._taskbarProgress = null; + gMacTaskbarProgress = null; + }, "quit-application-granted", false); + } else if (gWinTaskbar) { + // On Windows, the indicator is currently hidden because we have no + // previous browser window, thus we should attach the indicator now. + this._attachIndicator(aBrowserWindow); + } else { + // The taskbar indicator is not available on this platform. + return; + } + } + + // Ensure that the DownloadSummary object will be created asynchronously. + if (!this._summary) { + Downloads.getSummary(Downloads.ALL).then(summary => { + // In case the method is re-entered, we simply ignore redundant + // invocations of the callback, instead of keeping separate state. + if (this._summary) { + return; + } + this._summary = summary; + return this._summary.addView(this); + }).then(null, Cu.reportError); + } + }, + + /** + * On Windows, attaches the taskbar indicator to the specified browser window. + */ + _attachIndicator: function (aWindow) + { + // Activate the indicator on the specified window. + let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIXULWindow).docShell; + this._taskbarProgress = gWinTaskbar.getTaskbarProgress(docShell); + + // If the DownloadSummary object has already been created, we should update + // the state of the new indicator, otherwise it will be updated as soon as + // the DownloadSummary view is registered. + if (this._summary) { + this.onSummaryChanged(); + } + + aWindow.addEventListener("unload", () => { + // Locate another browser window, excluding the one being closed. + let browserWindow = RecentWindow.getMostRecentBrowserWindow(); + if (browserWindow) { + // Move the progress indicator to the other browser window. + this._attachIndicator(browserWindow); + } else { + // The last browser window has been closed. We remove the reference to + // the taskbar progress object so that the indicator will be registered + // again on the next browser window that is opened. + this._taskbarProgress = null; + } + }, false); + }, + + ////////////////////////////////////////////////////////////////////////////// + //// DownloadSummary view + + onSummaryChanged: function () + { + // If the last browser window has been closed, we have no indicator anymore. + if (!this._taskbarProgress) { + return; + } + + if (this._summary.allHaveStopped || this._summary.progressTotalBytes == 0) { + this._taskbarProgress.setProgressState( + Ci.nsITaskbarProgress.STATE_NO_PROGRESS, 0, 0); + } else { + // For a brief moment before completion, some download components may + // report more transferred bytes than the total number of bytes. Thus, + // ensure that we never break the expectations of the progress indicator. + let progressCurrentBytes = Math.min(this._summary.progressTotalBytes, + this._summary.progressCurrentBytes); + this._taskbarProgress.setProgressState( + Ci.nsITaskbarProgress.STATE_NORMAL, + progressCurrentBytes, + this._summary.progressTotalBytes); + } + }, +}; diff --git a/browser/components/downloads/src/moz.build b/browser/components/downloads/src/moz.build index 2a3a4973dc5..f7b6c7e686c 100644 --- a/browser/components/downloads/src/moz.build +++ b/browser/components/downloads/src/moz.build @@ -13,5 +13,6 @@ EXTRA_COMPONENTS += [ EXTRA_JS_MODULES += [ 'DownloadsCommon.jsm', 'DownloadsLogger.jsm', + 'DownloadsTaskbar.jsm', ] From ff1042bcb4ef1747e66492d924a22fc2824128ec Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Wed, 18 Sep 2013 11:48:11 -0400 Subject: [PATCH 05/56] Bug 916316 - Speed up FHR date formatting. r=mcomella --- mobile/android/base/android-services-files.mk | 1 + .../base/background/common/DateUtils.java | 43 +++++++++++++++++++ .../background/common/GlobalConstants.java.in | 4 ++ .../healthreport/EnvironmentBuilder.java | 3 +- .../HealthReportConstants.java.in | 11 +++-- .../HealthReportDatabaseStorage.java | 3 +- .../healthreport/HealthReportGenerator.java | 11 +++-- .../healthreport/HealthReportUtils.java | 17 -------- .../upload/AndroidSubmissionClient.java | 3 +- mobile/android/services/java-sources.mn | 1 + 10 files changed, 67 insertions(+), 30 deletions(-) create mode 100644 mobile/android/base/background/common/DateUtils.java diff --git a/mobile/android/base/android-services-files.mk b/mobile/android/base/android-services-files.mk index f883ba29f38..59880c8c512 100644 --- a/mobile/android/base/android-services-files.mk +++ b/mobile/android/base/android-services-files.mk @@ -25,6 +25,7 @@ SYNC_JAVA_FILES := \ background/bagheera/BagheeraRequestDelegate.java \ background/bagheera/BoundedByteArrayEntity.java \ background/bagheera/DeflateHelper.java \ + background/common/DateUtils.java \ background/common/log/Logger.java \ background/common/log/writers/AndroidLevelCachingLogWriter.java \ background/common/log/writers/AndroidLogWriter.java \ diff --git a/mobile/android/base/background/common/DateUtils.java b/mobile/android/base/background/common/DateUtils.java new file mode 100644 index 00000000000..05412224d74 --- /dev/null +++ b/mobile/android/base/background/common/DateUtils.java @@ -0,0 +1,43 @@ +/* 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.background.common; + +import java.util.Calendar; +import java.util.Formatter; +import java.util.TimeZone; + +public class DateUtils { + private static final TimeZone UTC = TimeZone.getTimeZone("UTC"); + + public static final class DateFormatter { + private final Calendar calendar; + private final Formatter formatter; + private final StringBuilder builder; + + public DateFormatter() { + this.calendar = Calendar.getInstance(UTC); + this.builder = new StringBuilder(); // So we can reset it. + this.formatter = new Formatter(this.builder, null); + } + + public String getDateString(long time) { + calendar.setTimeInMillis(time); + builder.setLength(0); + return formatter.format("%04d-%02d-%02d", + calendar.get(Calendar.YEAR), + calendar.get(Calendar.MONTH) + 1, // 0-indexed. + calendar.get(Calendar.DAY_OF_MONTH)) + .toString(); + } + + public String getDateStringForDay(long day) { + return getDateString(GlobalConstants.MILLISECONDS_PER_DAY * day); + } + } + + public static int getDay(final long time) { + return (int) Math.floor(time / GlobalConstants.MILLISECONDS_PER_DAY); + } +} diff --git a/mobile/android/base/background/common/GlobalConstants.java.in b/mobile/android/base/background/common/GlobalConstants.java.in index a58ff6bf97b..206052a636e 100644 --- a/mobile/android/base/background/common/GlobalConstants.java.in +++ b/mobile/android/base/background/common/GlobalConstants.java.in @@ -47,4 +47,8 @@ public class GlobalConstants { public static String GECKO_PREFERENCES_CLASS = "org.mozilla.gecko.GeckoPreferences"; public static String GECKO_BROADCAST_ANNOUNCEMENTS_PREF_METHOD = "broadcastAnnouncementsPref"; public static String GECKO_BROADCAST_HEALTHREPORT_UPLOAD_PREF_METHOD = "broadcastHealthReportUploadPref"; + + // Common time values. + public static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; + public static final long MILLISECONDS_PER_SIX_MONTHS = 180 * MILLISECONDS_PER_DAY; } diff --git a/mobile/android/base/background/healthreport/EnvironmentBuilder.java b/mobile/android/base/background/healthreport/EnvironmentBuilder.java index 9d223d54af2..cb53a7afce0 100644 --- a/mobile/android/base/background/healthreport/EnvironmentBuilder.java +++ b/mobile/android/base/background/healthreport/EnvironmentBuilder.java @@ -9,6 +9,7 @@ import java.util.Iterator; import org.json.JSONObject; import org.mozilla.gecko.AppConstants; import org.mozilla.gecko.SysInfo; +import org.mozilla.gecko.background.common.GlobalConstants; import org.mozilla.gecko.background.common.log.Logger; import android.content.ContentProvider; @@ -77,7 +78,7 @@ public class EnvironmentBuilder { e.sysName = SysInfo.getName(); e.sysVersion = SysInfo.getReleaseVersion(); - e.profileCreation = (int) (info.getProfileCreationTime() / HealthReportConstants.MILLISECONDS_PER_DAY); + e.profileCreation = (int) (info.getProfileCreationTime() / GlobalConstants.MILLISECONDS_PER_DAY); // Corresponds to Gecko pref "extensions.blocklist.enabled". e.isBlocklistEnabled = (info.isBlocklistEnabled() ? 1 : 0); diff --git a/mobile/android/base/background/healthreport/HealthReportConstants.java.in b/mobile/android/base/background/healthreport/HealthReportConstants.java.in index cc6f9c44eb2..d2814278fa5 100644 --- a/mobile/android/base/background/healthreport/HealthReportConstants.java.in +++ b/mobile/android/base/background/healthreport/HealthReportConstants.java.in @@ -5,6 +5,8 @@ package org.mozilla.gecko.background.healthreport; +import org.mozilla.gecko.background.common.GlobalConstants; + public class HealthReportConstants { public static final String HEALTH_AUTHORITY = "@ANDROID_PACKAGE_NAME@.health"; public static final String GLOBAL_LOG_TAG = "GeckoHealth"; @@ -15,9 +17,6 @@ public class HealthReportConstants { */ public static final long EARLIEST_LAST_PING = 1367500000000L; - public static final long MILLISECONDS_PER_DAY = 24 * 60 * 60 * 1000; - public static final long MILLISECONDS_PER_SIX_MONTHS = 180 * MILLISECONDS_PER_DAY; - // Not `final` so we have the option to turn this on at runtime with a magic addon. public static boolean UPLOAD_FEATURE_DISABLED = false; @@ -29,15 +28,15 @@ public class HealthReportConstants { // intent is scheduled to be called by the Android Alarm Manager, not how // frequently we actually submit. public static final String PREF_SUBMISSION_INTENT_INTERVAL_MSEC = "healthreport_submission_intent_interval_msec"; - public static final long DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC = MILLISECONDS_PER_DAY / 24; + public static final long DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC = GlobalConstants.MILLISECONDS_PER_DAY / 24; public static final String ACTION_HEALTHREPORT_UPLOAD_PREF = "@ANDROID_PACKAGE_NAME@.HEALTHREPORT_UPLOAD_PREF"; public static final String PREF_MINIMUM_TIME_BETWEEN_UPLOADS = "healthreport_time_between_uploads"; - public static final long DEFAULT_MINIMUM_TIME_BETWEEN_UPLOADS = MILLISECONDS_PER_DAY; + public static final long DEFAULT_MINIMUM_TIME_BETWEEN_UPLOADS = GlobalConstants.MILLISECONDS_PER_DAY; public static final String PREF_MINIMUM_TIME_BEFORE_FIRST_SUBMISSION = "healthreport_time_before_first_submission"; - public static final long DEFAULT_MINIMUM_TIME_BEFORE_FIRST_SUBMISSION = MILLISECONDS_PER_DAY; + public static final long DEFAULT_MINIMUM_TIME_BEFORE_FIRST_SUBMISSION = GlobalConstants.MILLISECONDS_PER_DAY; public static final String PREF_MINIMUM_TIME_AFTER_FAILURE = "healthreport_time_after_failure"; public static final long DEFAULT_MINIMUM_TIME_AFTER_FAILURE = DEFAULT_SUBMISSION_INTENT_INTERVAL_MSEC; diff --git a/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java b/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java index 9ac4117e2dc..eb6227b8096 100644 --- a/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java +++ b/mobile/android/base/background/healthreport/HealthReportDatabaseStorage.java @@ -12,6 +12,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import org.json.JSONObject; +import org.mozilla.gecko.background.common.DateUtils; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.healthreport.HealthReportStorage.MeasurementFields.FieldSpec; @@ -1029,7 +1030,7 @@ public class HealthReportDatabaseStorage implements HealthReportStorage { @Override public int getDay(long time) { - return HealthReportUtils.getDay(time); + return DateUtils.getDay(time); } @Override diff --git a/mobile/android/base/background/healthreport/HealthReportGenerator.java b/mobile/android/base/background/healthreport/HealthReportGenerator.java index d57e150522f..426a1c336a9 100644 --- a/mobile/android/base/background/healthreport/HealthReportGenerator.java +++ b/mobile/android/base/background/healthreport/HealthReportGenerator.java @@ -10,6 +10,7 @@ import java.util.Set; import org.json.JSONException; import org.json.JSONObject; +import org.mozilla.gecko.background.common.DateUtils.DateFormatter; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.healthreport.HealthReportStorage.Field; @@ -22,9 +23,11 @@ public class HealthReportGenerator { private static final String LOG_TAG = "GeckoHealthGen"; private final HealthReportStorage storage; + private final DateFormatter dateFormatter; public HealthReportGenerator(HealthReportStorage storage) { this.storage = storage; + this.dateFormatter = new DateFormatter(); } @SuppressWarnings("static-method") @@ -76,10 +79,10 @@ public class HealthReportGenerator { JSONObject document = new JSONObject(); if (lastPingTime >= HealthReportConstants.EARLIEST_LAST_PING) { - document.put("lastPingDate", HealthReportUtils.getDateString(lastPingTime)); + document.put("lastPingDate", dateFormatter.getDateString(lastPingTime)); } - document.put("thisPingDate", HealthReportUtils.getDateString(now())); + document.put("thisPingDate", dateFormatter.getDateString(now())); document.put("version", PAYLOAD_VERSION); document.put("environments", getEnvironmentsJSON(currentEnvironment, envs)); @@ -147,7 +150,7 @@ public class HealthReportGenerator { if (dateChanged) { if (dateObject != null) { - days.put(HealthReportUtils.getDateStringForDay(lastDate), dateObject); + days.put(dateFormatter.getDateStringForDay(lastDate), dateObject); } dateObject = new JSONObject(); lastDate = cDate; @@ -179,7 +182,7 @@ public class HealthReportGenerator { cursor.moveToNext(); continue; } - days.put(HealthReportUtils.getDateStringForDay(lastDate), dateObject); + days.put(dateFormatter.getDateStringForDay(lastDate), dateObject); } finally { cursor.close(); } diff --git a/mobile/android/base/background/healthreport/HealthReportUtils.java b/mobile/android/base/background/healthreport/HealthReportUtils.java index e3c849a74f0..4c72b52d73a 100644 --- a/mobile/android/base/background/healthreport/HealthReportUtils.java +++ b/mobile/android/base/background/healthreport/HealthReportUtils.java @@ -4,13 +4,10 @@ package org.mozilla.gecko.background.healthreport; -import java.text.SimpleDateFormat; import java.util.HashSet; import java.util.Iterator; -import java.util.Locale; import java.util.Set; import java.util.SortedSet; -import java.util.TimeZone; import java.util.TreeSet; import java.util.UUID; @@ -25,24 +22,10 @@ import android.net.Uri; public class HealthReportUtils { public static final String LOG_TAG = HealthReportUtils.class.getSimpleName(); - public static int getDay(final long time) { - return (int) Math.floor(time / HealthReportConstants.MILLISECONDS_PER_DAY); - } - public static String getEnvironmentHash(final String input) { return DigestUtils.shaHex(input); } - public static String getDateStringForDay(long day) { - return getDateString(HealthReportConstants.MILLISECONDS_PER_DAY * day); - } - - public static String getDateString(long time) { - final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd", Locale.US); - format.setTimeZone(TimeZone.getTimeZone("UTC")); - return format.format(time); - } - /** * Take an environment URI (one that identifies an environment) and produce an * event URI. diff --git a/mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java b/mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java index efce5e68271..addd248ba39 100644 --- a/mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java +++ b/mobile/android/base/background/healthreport/upload/AndroidSubmissionClient.java @@ -10,6 +10,7 @@ import java.util.Collection; import org.json.JSONObject; import org.mozilla.gecko.background.bagheera.BagheeraClient; import org.mozilla.gecko.background.bagheera.BagheeraRequestDelegate; +import org.mozilla.gecko.background.common.GlobalConstants; import org.mozilla.gecko.background.common.log.Logger; import org.mozilla.gecko.background.healthreport.EnvironmentBuilder; import org.mozilla.gecko.background.healthreport.HealthReportConstants; @@ -103,7 +104,7 @@ public class AndroidSubmissionClient implements SubmissionClient { return; } - long since = localTime - HealthReportConstants.MILLISECONDS_PER_SIX_MONTHS; + long since = localTime - GlobalConstants.MILLISECONDS_PER_SIX_MONTHS; long last = Math.max(getLastUploadLocalTime(), HealthReportConstants.EARLIEST_LAST_PING); if (!storage.hasEventSince(last)) { diff --git a/mobile/android/services/java-sources.mn b/mobile/android/services/java-sources.mn index 21d6c4c1cde..d184a0ed2bd 100644 --- a/mobile/android/services/java-sources.mn +++ b/mobile/android/services/java-sources.mn @@ -12,6 +12,7 @@ background/bagheera/BagheeraClient.java background/bagheera/BagheeraRequestDelegate.java background/bagheera/BoundedByteArrayEntity.java background/bagheera/DeflateHelper.java +background/common/DateUtils.java background/common/log/Logger.java background/common/log/writers/AndroidLevelCachingLogWriter.java background/common/log/writers/AndroidLogWriter.java From 3434ba42e2a72cef7bb6ef8e3e680829946147f3 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Wed, 18 Sep 2013 12:58:25 -0400 Subject: [PATCH 06/56] Bug 905685 - Implement incremental favicon loading in about:home (r=sriram) --- mobile/android/base/Makefile.in | 2 - mobile/android/base/home/BookmarksPage.java | 30 +---- mobile/android/base/home/BrowserSearch.java | 49 ++------ mobile/android/base/home/FaviconsLoader.java | 115 ------------------ .../base/home/HomeCursorLoaderCallbacks.java | 58 --------- mobile/android/base/home/LastTabsPage.java | 38 ++---- mobile/android/base/home/MostRecentPage.java | 39 ++---- mobile/android/base/home/MostVisitedPage.java | 39 ++---- .../android/base/home/PinBookmarkDialog.java | 41 ++----- mobile/android/base/home/ReadingListPage.java | 23 +--- mobile/android/base/home/TwoLinePageRow.java | 80 +++++++++++- 11 files changed, 127 insertions(+), 387 deletions(-) delete mode 100644 mobile/android/base/home/FaviconsLoader.java delete mode 100644 mobile/android/base/home/HomeCursorLoaderCallbacks.java diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 2a93655da6c..8becefd39a9 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -220,14 +220,12 @@ FENNEC_JAVA_FILES = \ home/BookmarkThumbnailView.java \ home/BrowserSearch.java \ home/HistoryPage.java \ - home/HomeCursorLoaderCallbacks.java \ home/HomeFragment.java \ home/HomeListView.java \ home/HomePager.java \ home/HomePagerTabStrip.java \ home/HomeBanner.java \ home/FadedTextView.java \ - home/FaviconsLoader.java \ home/LastTabsPage.java \ home/MostRecentPage.java \ home/MostVisitedPage.java \ diff --git a/mobile/android/base/home/BookmarksPage.java b/mobile/android/base/home/BookmarksPage.java index f3c28e4cb40..238a7e95385 100644 --- a/mobile/android/base/home/BookmarksPage.java +++ b/mobile/android/base/home/BookmarksPage.java @@ -179,7 +179,7 @@ public class BookmarksPage extends HomeFragment { BrowserDB.invalidateCachedState(); // Create callbacks before the initial loader is started. - mLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); + mLoaderCallbacks = new CursorLoaderCallbacks(); mThumbnailsLoaderCallbacks = new ThumbnailsLoaderCallbacks(); loadIfVisible(); } @@ -453,11 +453,7 @@ public class BookmarksPage extends HomeFragment { /** * Loader callbacks for the LoaderManager of this fragment. */ - private class CursorLoaderCallbacks extends HomeCursorLoaderCallbacks { - public CursorLoaderCallbacks(Context context, LoaderManager loaderManager) { - super(context, loaderManager); - } - + private class CursorLoaderCallbacks implements LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { switch(id) { @@ -472,11 +468,9 @@ public class BookmarksPage extends HomeFragment { case LOADER_ID_TOP_BOOKMARKS: { return new TopBookmarksLoader(getActivity()); } - - default: { - return super.onCreateLoader(id, args); - } } + + return null; } @Override @@ -485,7 +479,6 @@ public class BookmarksPage extends HomeFragment { switch(loaderId) { case LOADER_ID_BOOKMARKS_LIST: { mListAdapter.swapCursor(c); - loadFavicons(c); mList.setHeaderDividersEnabled(c != null && c.getCount() > 0); break; } @@ -509,11 +502,6 @@ public class BookmarksPage extends HomeFragment { } break; } - - default: { - super.onLoadFinished(loader, c); - break; - } } } @@ -534,18 +522,8 @@ public class BookmarksPage extends HomeFragment { break; } } - - default: { - super.onLoaderReset(loader); - break; - } } } - - @Override - public void onFaviconsLoaded() { - mListAdapter.notifyDataSetChanged(); - } } /** diff --git a/mobile/android/base/home/BrowserSearch.java b/mobile/android/base/home/BrowserSearch.java index d641ab14578..b7a512f3bdf 100644 --- a/mobile/android/base/home/BrowserSearch.java +++ b/mobile/android/base/home/BrowserSearch.java @@ -104,7 +104,7 @@ public class BrowserSearch extends HomeFragment // Whether search suggestions are enabled or not private boolean mSuggestionsEnabled; - // Callbacks used for the search and favicon cursor loaders + // Callbacks used for the search loader private CursorLoaderCallbacks mCursorLoaderCallbacks; // Callbacks used for the search suggestion loader @@ -286,17 +286,15 @@ public class BrowserSearch extends HomeFragment public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); - // Intialize the search adapter - mAdapter = new SearchAdapter(activity); + mAdapter = new SearchAdapter(getActivity()); mList.setAdapter(mAdapter); // Only create an instance when we need it mSuggestionLoaderCallbacks = null; // Create callbacks before the initial loader is started - mCursorLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); loadIfVisible(); } @@ -772,49 +770,26 @@ public class BrowserSearch extends HomeFragment } } - private class CursorLoaderCallbacks extends HomeCursorLoaderCallbacks { - public CursorLoaderCallbacks(Context context, LoaderManager loaderManager) { - super(context, loaderManager); - } - + private class CursorLoaderCallbacks implements LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { - if (id == LOADER_ID_SEARCH) { - return SearchLoader.createInstance(getActivity(), args); - } else { - return super.onCreateLoader(id, args); - } + return SearchLoader.createInstance(getActivity(), args); } @Override public void onLoadFinished(Loader loader, Cursor c) { - if (loader.getId() == LOADER_ID_SEARCH) { - mAdapter.swapCursor(c); + mAdapter.swapCursor(c); - // We should handle autocompletion based on the search term - // associated with the currently loader that has just provided - // the results. - SearchCursorLoader searchLoader = (SearchCursorLoader) loader; - handleAutocomplete(searchLoader.getSearchTerm(), c); - - loadFavicons(c); - } else { - super.onLoadFinished(loader, c); - } + // We should handle autocompletion based on the search term + // associated with the currently loader that has just provided + // the results. + SearchCursorLoader searchLoader = (SearchCursorLoader) loader; + handleAutocomplete(searchLoader.getSearchTerm(), c); } @Override public void onLoaderReset(Loader loader) { - if (loader.getId() == LOADER_ID_SEARCH) { - mAdapter.swapCursor(null); - } else { - super.onLoaderReset(loader); - } - } - - @Override - public void onFaviconsLoaded() { - mAdapter.notifyDataSetChanged(); + mAdapter.swapCursor(null); } } diff --git a/mobile/android/base/home/FaviconsLoader.java b/mobile/android/base/home/FaviconsLoader.java deleted file mode 100644 index b5da2d7b029..00000000000 --- a/mobile/android/base/home/FaviconsLoader.java +++ /dev/null @@ -1,115 +0,0 @@ -/* -*- 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.home; - -import org.mozilla.gecko.favicons.Favicons; -import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.db.BrowserDB.URLColumns; -import org.mozilla.gecko.gfx.BitmapUtils; - -import android.content.ContentResolver; -import android.content.Context; -import android.database.Cursor; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.support.v4.app.LoaderManager; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.Loader; - -import java.util.ArrayList; - -/** - * Encapsulates the implementation of the favicons cursorloader. - */ -class FaviconsLoader { - // Argument containing list of urls for the favicons loader - private static final String FAVICONS_LOADER_URLS_ARG = "urls"; - - private FaviconsLoader() { - } - - private static ArrayList getUrlsWithoutFavicon(Cursor c) { - ArrayList urls = new ArrayList(); - - if (c == null || !c.moveToFirst()) { - return urls; - } - - do { - final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL)); - - // We only want to load favicons from DB if they are not in the - // memory cache yet. The url is null for bookmark folders. - if (url == null || Favicons.getFaviconFromMemCache(url) != null) { - continue; - } - - urls.add(url); - } while (c.moveToNext()); - - return urls; - } - - private static void storeFaviconsInMemCache(Cursor c) { - if (c == null || !c.moveToFirst()) { - return; - } - - do { - final String url = c.getString(c.getColumnIndexOrThrow(URLColumns.URL)); - final byte[] b = c.getBlob(c.getColumnIndexOrThrow(URLColumns.FAVICON)); - - if (b == null) { - continue; - } - - Bitmap favicon = BitmapUtils.decodeByteArray(b); - if (favicon == null) { - continue; - } - - favicon = Favicons.scaleImage(favicon); - Favicons.putFaviconInMemCache(url, favicon); - } while (c.moveToNext()); - } - - public static void restartFromCursor(LoaderManager manager, int loaderId, - LoaderCallbacks callbacks, Cursor c) { - // If there urls without in-memory favicons, trigger a new loader - // to load the images from disk to memory. - ArrayList urls = getUrlsWithoutFavicon(c); - if (urls.size() > 0) { - Bundle args = new Bundle(); - args.putStringArrayList(FAVICONS_LOADER_URLS_ARG, urls); - - manager.restartLoader(loaderId, args, callbacks); - } - } - - public static Loader createInstance(Context context, Bundle args) { - final ArrayList urls = args.getStringArrayList(FAVICONS_LOADER_URLS_ARG); - return new FaviconsCursorLoader(context, urls); - } - - private static class FaviconsCursorLoader extends SimpleCursorLoader { - private final ArrayList mUrls; - - public FaviconsCursorLoader(Context context, ArrayList urls) { - super(context); - mUrls = urls; - } - - @Override - public Cursor loadCursor() { - final ContentResolver cr = getContext().getContentResolver(); - - Cursor c = BrowserDB.getFaviconsForUrls(cr, mUrls); - storeFaviconsInMemCache(c); - - return c; - } - } -} diff --git a/mobile/android/base/home/HomeCursorLoaderCallbacks.java b/mobile/android/base/home/HomeCursorLoaderCallbacks.java deleted file mode 100644 index 1ff2d732f6c..00000000000 --- a/mobile/android/base/home/HomeCursorLoaderCallbacks.java +++ /dev/null @@ -1,58 +0,0 @@ -/* -*- 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.home; - -import android.content.Context; -import android.database.Cursor; -import android.os.Bundle; -import android.support.v4.app.LoaderManager; -import android.support.v4.app.LoaderManager.LoaderCallbacks; -import android.support.v4.content.Loader; - -/** - * Cursor loader callbacks that takes care loading favicons into memory. - */ -abstract class HomeCursorLoaderCallbacks implements LoaderCallbacks { - - // Cursor loader ID for favicons query - private static final int LOADER_ID_FAVICONS = 100; - - private final Context mContext; - private final LoaderManager mLoaderManager; - - public HomeCursorLoaderCallbacks(Context context, LoaderManager loaderManager) { - mContext = context; - mLoaderManager = loaderManager; - } - - public void loadFavicons(Cursor cursor) { - FaviconsLoader.restartFromCursor(mLoaderManager, LOADER_ID_FAVICONS, this, cursor); - } - - @Override - public Loader onCreateLoader(int id, Bundle args) { - if (id == LOADER_ID_FAVICONS) { - return FaviconsLoader.createInstance(mContext, args); - } - - return null; - } - - @Override - public void onLoadFinished(Loader loader, Cursor c) { - if (loader.getId() == LOADER_ID_FAVICONS) { - onFaviconsLoaded(); - } - } - - @Override - public void onLoaderReset(Loader loader) { - // Do nothing by default. - } - - // Callback for favicons loaded in memory. - public abstract void onFaviconsLoaded(); -} diff --git a/mobile/android/base/home/LastTabsPage.java b/mobile/android/base/home/LastTabsPage.java index 3a08eea2952..87b80b1445d 100644 --- a/mobile/android/base/home/LastTabsPage.java +++ b/mobile/android/base/home/LastTabsPage.java @@ -142,14 +142,12 @@ public class LastTabsPage extends HomeFragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); - // Intialize adapter - mAdapter = new LastTabsAdapter(activity); + mAdapter = new LastTabsAdapter(getActivity()); mList.setAdapter(mAdapter); // Create callbacks before the initial loader is started - mCursorLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); loadIfVisible(); } @@ -262,43 +260,21 @@ public class LastTabsPage extends HomeFragment { } } - private class CursorLoaderCallbacks extends HomeCursorLoaderCallbacks { - public CursorLoaderCallbacks(Context context, LoaderManager loaderManager) { - super(context, loaderManager); - } - + private class CursorLoaderCallbacks implements LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { - if (id == LOADER_ID_LAST_TABS) { - return new LastTabsCursorLoader(getActivity()); - } else { - return super.onCreateLoader(id, args); - } + return new LastTabsCursorLoader(getActivity()); } @Override public void onLoadFinished(Loader loader, Cursor c) { - if (loader.getId() == LOADER_ID_LAST_TABS) { - mAdapter.swapCursor(c); - updateUiFromCursor(c); - loadFavicons(c); - } else { - super.onLoadFinished(loader, c); - } + mAdapter.swapCursor(c); + updateUiFromCursor(c); } @Override public void onLoaderReset(Loader loader) { - if (loader.getId() == LOADER_ID_LAST_TABS) { - mAdapter.swapCursor(null); - } else { - super.onLoaderReset(loader); - } - } - - @Override - public void onFaviconsLoaded() { - mAdapter.notifyDataSetChanged(); + mAdapter.swapCursor(null); } } } diff --git a/mobile/android/base/home/MostRecentPage.java b/mobile/android/base/home/MostRecentPage.java index fe262e9d4e1..0d79ca37106 100644 --- a/mobile/android/base/home/MostRecentPage.java +++ b/mobile/android/base/home/MostRecentPage.java @@ -17,6 +17,7 @@ import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.util.SparseArray; import android.view.LayoutInflater; @@ -127,14 +128,12 @@ public class MostRecentPage extends HomeFragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); - // Intialize adapter - mAdapter = new MostRecentAdapter(activity); + mAdapter = new MostRecentAdapter(getActivity()); mList.setAdapter(mAdapter); // Create callbacks before the initial loader is started - mCursorLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); loadIfVisible(); } @@ -360,43 +359,21 @@ public class MostRecentPage extends HomeFragment { } } - private class CursorLoaderCallbacks extends HomeCursorLoaderCallbacks { - public CursorLoaderCallbacks(Context context, LoaderManager loaderManager) { - super(context, loaderManager); - } - + private class CursorLoaderCallbacks implements LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { - if (id == LOADER_ID_HISTORY) { - return new MostRecentCursorLoader(getActivity()); - } else { - return super.onCreateLoader(id, args); - } + return new MostRecentCursorLoader(getActivity()); } @Override public void onLoadFinished(Loader loader, Cursor c) { - if (loader.getId() == LOADER_ID_HISTORY) { - mAdapter.swapCursor(c); - updateUiFromCursor(c); - loadFavicons(c); - } else { - super.onLoadFinished(loader, c); - } + mAdapter.swapCursor(c); + updateUiFromCursor(c); } @Override public void onLoaderReset(Loader loader) { - if (loader.getId() == LOADER_ID_HISTORY) { - mAdapter.swapCursor(null); - } else { - super.onLoaderReset(loader); - } - } - - @Override - public void onFaviconsLoaded() { - mAdapter.notifyDataSetChanged(); + mAdapter.swapCursor(null); } } } diff --git a/mobile/android/base/home/MostVisitedPage.java b/mobile/android/base/home/MostVisitedPage.java index fff8e9ec0af..9315049aa13 100644 --- a/mobile/android/base/home/MostVisitedPage.java +++ b/mobile/android/base/home/MostVisitedPage.java @@ -16,6 +16,7 @@ import android.content.Context; import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; import android.view.LayoutInflater; @@ -129,14 +130,12 @@ public class MostVisitedPage extends HomeFragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); - // Intialize the search adapter - mAdapter = new VisitedAdapter(activity, null); + mAdapter = new VisitedAdapter(getActivity(), null); mList.setAdapter(mAdapter); // Create callbacks before the initial loader is started - mCursorLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); + mCursorLoaderCallbacks = new CursorLoaderCallbacks(); loadIfVisible(); } @@ -206,43 +205,21 @@ public class MostVisitedPage extends HomeFragment { } } - private class CursorLoaderCallbacks extends HomeCursorLoaderCallbacks { - public CursorLoaderCallbacks(Context context, LoaderManager loaderManager) { - super(context, loaderManager); - } - + private class CursorLoaderCallbacks implements LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { - if (id == LOADER_ID_FRECENCY) { - return new FrecencyCursorLoader(getActivity()); - } else { - return super.onCreateLoader(id, args); - } + return new FrecencyCursorLoader(getActivity()); } @Override public void onLoadFinished(Loader loader, Cursor c) { - if (loader.getId() == LOADER_ID_FRECENCY) { - mAdapter.swapCursor(c); - updateUiFromCursor(c); - loadFavicons(c); - } else { - super.onLoadFinished(loader, c); - } + mAdapter.swapCursor(c); + updateUiFromCursor(c); } @Override public void onLoaderReset(Loader loader) { - if (loader.getId() == LOADER_ID_FRECENCY) { - mAdapter.swapCursor(null); - } else { - super.onLoaderReset(loader); - } - } - - @Override - public void onFaviconsLoaded() { - mAdapter.notifyDataSetChanged(); + mAdapter.swapCursor(null); } } } diff --git a/mobile/android/base/home/PinBookmarkDialog.java b/mobile/android/base/home/PinBookmarkDialog.java index 3e20ea80578..90b0ab9082a 100644 --- a/mobile/android/base/home/PinBookmarkDialog.java +++ b/mobile/android/base/home/PinBookmarkDialog.java @@ -14,6 +14,7 @@ import android.database.Cursor; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.support.v4.app.LoaderManager; +import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.Loader; import android.support.v4.widget.CursorAdapter; import android.text.Editable; @@ -38,9 +39,6 @@ class PinBookmarkDialog extends DialogFragment { // Cursor loader ID for search query private static final int LOADER_ID_SEARCH = 0; - // Cursor loader ID for favicons query - private static final int LOADER_ID_FAVICONS = 1; - // Holds the current search term to use in the query private String mSearchTerm; @@ -53,7 +51,7 @@ class PinBookmarkDialog extends DialogFragment { // Search results private ListView mList; - // Callbacks used for the search and favicon cursor loaders + // Callbacks used for the search loader private CursorLoaderCallbacks mLoaderCallbacks; // Bookmark selected listener @@ -125,15 +123,14 @@ class PinBookmarkDialog extends DialogFragment { public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - final Activity activity = getActivity(); final LoaderManager manager = getLoaderManager(); // Initialize the search adapter - mAdapter = new SearchAdapter(activity); + mAdapter = new SearchAdapter(getActivity()); mList.setAdapter(mAdapter); // Create callbacks before the initial loader is started - mLoaderCallbacks = new CursorLoaderCallbacks(activity, manager); + mLoaderCallbacks = new CursorLoaderCallbacks(); // Reconnect to the loader only if present manager.initLoader(LOADER_ID_SEARCH, null, mLoaderCallbacks); @@ -179,42 +176,20 @@ class PinBookmarkDialog extends DialogFragment { } } - private class CursorLoaderCallbacks extends HomeCursorLoaderCallbacks { - public CursorLoaderCallbacks(Context context, LoaderManager loaderManager) { - super(context, loaderManager); - } - + private class CursorLoaderCallbacks implements LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { - if (id == LOADER_ID_SEARCH) { - return SearchLoader.createInstance(getActivity(), args); - } else { - return super.onCreateLoader(id, args); - } + return SearchLoader.createInstance(getActivity(), args); } @Override public void onLoadFinished(Loader loader, Cursor c) { - if (loader.getId() == LOADER_ID_SEARCH) { - mAdapter.swapCursor(c); - loadFavicons(c); - } else { - super.onLoadFinished(loader, c); - } + mAdapter.swapCursor(c); } @Override public void onLoaderReset(Loader loader) { - if (loader.getId() == LOADER_ID_SEARCH) { - mAdapter.swapCursor(null); - } else { - super.onLoaderReset(loader); - } - } - - @Override - public void onFaviconsLoaded() { - mAdapter.notifyDataSetChanged(); + mAdapter.swapCursor(null); } } } diff --git a/mobile/android/base/home/ReadingListPage.java b/mobile/android/base/home/ReadingListPage.java index 62fc9a558a6..4bf43467d35 100644 --- a/mobile/android/base/home/ReadingListPage.java +++ b/mobile/android/base/home/ReadingListPage.java @@ -219,33 +219,18 @@ public class ReadingListPage extends HomeFragment { private class CursorLoaderCallbacks implements LoaderCallbacks { @Override public Loader onCreateLoader(int id, Bundle args) { - switch(id) { - case LOADER_ID_READING_LIST: - return new ReadingListLoader(getActivity()); - } - return null; + return new ReadingListLoader(getActivity()); } @Override public void onLoadFinished(Loader loader, Cursor c) { - final int loaderId = loader.getId(); - switch(loaderId) { - case LOADER_ID_READING_LIST: - mAdapter.swapCursor(c); - break; - } - - updateUiFromCursor(c); + mAdapter.swapCursor(c); + updateUiFromCursor(c); } @Override public void onLoaderReset(Loader loader) { - final int loaderId = loader.getId(); - switch(loaderId) { - case LOADER_ID_READING_LIST: - mAdapter.swapCursor(null); - break; - } + mAdapter.swapCursor(null); } } } diff --git a/mobile/android/base/home/TwoLinePageRow.java b/mobile/android/base/home/TwoLinePageRow.java index a82b1d797d0..10e85f8683c 100644 --- a/mobile/android/base/home/TwoLinePageRow.java +++ b/mobile/android/base/home/TwoLinePageRow.java @@ -10,14 +10,19 @@ import org.mozilla.gecko.R; import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.db.BrowserContract.Combined; +import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.db.BrowserDB.URLColumns; import org.mozilla.gecko.gfx.BitmapUtils; import org.mozilla.gecko.util.ThreadUtils; +import org.mozilla.gecko.util.UiAsyncTask; import org.mozilla.gecko.widget.FaviconView; +import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.graphics.Bitmap; +import android.os.AsyncTask; +import android.os.Build; import android.text.TextUtils; import android.util.AttributeSet; import android.view.Gravity; @@ -40,6 +45,8 @@ public class TwoLinePageRow extends LinearLayout // The URL for the page corresponding to this view. private String mPageUrl; + private LoadFaviconTask mLoadFaviconTask; + public TwoLinePageRow(Context context) { this(context, null); } @@ -74,6 +81,8 @@ public class TwoLinePageRow extends LinearLayout Tabs.unregisterOnTabsChangedListener(TwoLinePageRow.this); } }); + + cancelLoadFaviconTask(); } @Override @@ -130,6 +139,16 @@ public class TwoLinePageRow extends LinearLayout updateDisplayedUrl(); } + /** + * Cancels any pending favicon loading task associated with this view. + */ + private void cancelLoadFaviconTask() { + if (mLoadFaviconTask != null) { + mLoadFaviconTask.cancel(true); + mLoadFaviconTask = null; + } + } + /** * Replaces the page URL with "Switch to tab" if there is already a tab open with that URL. */ @@ -163,24 +182,44 @@ public class TwoLinePageRow extends LinearLayout // bar view - this is the equivalent of getDisplayTitle() in Tab.java setTitle(TextUtils.isEmpty(title) ? url : title); + // No need to do extra work if the URL associated with this view + // hasn't changed. + if (TextUtils.equals(mPageUrl, url)) { + return; + } + updateDisplayedUrl(url); int faviconIndex = cursor.getColumnIndex(URLColumns.FAVICON); + Bitmap favicon = null; if (faviconIndex != -1) { byte[] b = cursor.getBlob(faviconIndex); - Bitmap favicon = null; if (b != null) { Bitmap bitmap = BitmapUtils.decodeByteArray(b); if (bitmap != null) { favicon = Favicons.scaleImage(bitmap); } } - - setFaviconWithUrl(favicon, url); } else { // If favicons is not on the cursor, try to fetch it from the memory cache - setFaviconWithUrl(Favicons.getFaviconFromMemCache(url), url); + favicon = Favicons.getFaviconFromMemCache(url); + } + + cancelLoadFaviconTask(); + + if (favicon != null) { + setFaviconWithUrl(favicon, url); + } else { + mLoadFaviconTask = new LoadFaviconTask(url); + + // Try to use a thread pool instead of serial execution of tasks + // to add more throughput to the favicon loading routines. + if (Build.VERSION.SDK_INT >= 11) { + mLoadFaviconTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + } else { + mLoadFaviconTask.execute(); + } } // Don't show bookmark/reading list icon, if not needed. @@ -213,4 +252,37 @@ public class TwoLinePageRow extends LinearLayout setBookmarkIcon(NO_ICON); } } + + private class LoadFaviconTask extends AsyncTask { + private final String mUrl; + + public LoadFaviconTask(String url) { + mUrl = url; + } + + @Override + public Bitmap doInBackground(Void... params) { + Bitmap favicon = Favicons.getFaviconFromMemCache(mUrl); + if (favicon == null) { + final ContentResolver cr = getContext().getContentResolver(); + + final Bitmap faviconFromDb = BrowserDB.getFaviconForUrl(cr, mUrl); + if (faviconFromDb != null) { + favicon = Favicons.scaleImage(faviconFromDb); + Favicons.putFaviconInMemCache(mUrl, favicon); + } + } + + return favicon; + } + + @Override + public void onPostExecute(Bitmap favicon) { + if (TextUtils.equals(mPageUrl, mUrl)) { + setFaviconWithUrl(favicon, mUrl); + } + + mLoadFaviconTask = null; + } + } } From 63ca8c553482747b111c13bd978f2f12fa5234f7 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Wed, 18 Sep 2013 12:58:50 -0400 Subject: [PATCH 07/56] Bug 905685 - Don't try to load favicons from cursor in TwoLinePageRow (r=sriram) --- mobile/android/base/home/TwoLinePageRow.java | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/mobile/android/base/home/TwoLinePageRow.java b/mobile/android/base/home/TwoLinePageRow.java index 10e85f8683c..7adc3b1b1eb 100644 --- a/mobile/android/base/home/TwoLinePageRow.java +++ b/mobile/android/base/home/TwoLinePageRow.java @@ -189,25 +189,11 @@ public class TwoLinePageRow extends LinearLayout } updateDisplayedUrl(url); - - int faviconIndex = cursor.getColumnIndex(URLColumns.FAVICON); - Bitmap favicon = null; - if (faviconIndex != -1) { - byte[] b = cursor.getBlob(faviconIndex); - - if (b != null) { - Bitmap bitmap = BitmapUtils.decodeByteArray(b); - if (bitmap != null) { - favicon = Favicons.scaleImage(bitmap); - } - } - } else { - // If favicons is not on the cursor, try to fetch it from the memory cache - favicon = Favicons.getFaviconFromMemCache(url); - } - cancelLoadFaviconTask(); + // First, try to find the favicon in the memory cache. If it's not + // cached yet, try to load it from the database, off main thread. + final Bitmap favicon = Favicons.getFaviconFromMemCache(url); if (favicon != null) { setFaviconWithUrl(favicon, url); } else { From 59c9b9f42ea17153b403df53eee5b9751a89dd54 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Wed, 18 Sep 2013 12:58:58 -0400 Subject: [PATCH 08/56] Bug 905685 - Clear favicon until the new image finishes loading in TwoLinePageRow (r=sriram) --- mobile/android/base/home/TwoLinePageRow.java | 3 +++ mobile/android/base/widget/FaviconView.java | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/mobile/android/base/home/TwoLinePageRow.java b/mobile/android/base/home/TwoLinePageRow.java index 7adc3b1b1eb..c70ae2b693f 100644 --- a/mobile/android/base/home/TwoLinePageRow.java +++ b/mobile/android/base/home/TwoLinePageRow.java @@ -197,6 +197,9 @@ public class TwoLinePageRow extends LinearLayout if (favicon != null) { setFaviconWithUrl(favicon, url); } else { + // Show blank image until the new favicon finishes loading + mFavicon.clearImage(); + mLoadFaviconTask = new LoadFaviconTask(url); // Try to use a thread pool instead of serial execution of tasks diff --git a/mobile/android/base/widget/FaviconView.java b/mobile/android/base/widget/FaviconView.java index 1a8a84ea36b..19f3a424a1a 100644 --- a/mobile/android/base/widget/FaviconView.java +++ b/mobile/android/base/widget/FaviconView.java @@ -154,6 +154,14 @@ public class FaviconView extends ImageView { formatImage(); } + /** + * Clear image and background shown by this view. + */ + public void clearImage() { + setImageResource(0); + hideBackground(); + } + /** * Update the displayed image and apply the scaling logic. * The scaling logic will attempt to resize the image to fit correctly inside the view in a way From eebb5ab023db61e3849564d599bf6d88831b566f Mon Sep 17 00:00:00 2001 From: Richard Newman Date: Wed, 18 Sep 2013 13:12:26 -0400 Subject: [PATCH 09/56] Bug 916316 - Follow-up to fix bustage on a CLOSED TREE. r=trivial --- mobile/android/base/health/BrowserHealthReporter.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/health/BrowserHealthReporter.java b/mobile/android/base/health/BrowserHealthReporter.java index 4b2c754fbb2..0a2309fe7fc 100644 --- a/mobile/android/base/health/BrowserHealthReporter.java +++ b/mobile/android/base/health/BrowserHealthReporter.java @@ -14,6 +14,7 @@ import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.GeckoProfile; import org.mozilla.gecko.background.healthreport.EnvironmentBuilder; +import org.mozilla.gecko.background.common.GlobalConstants; import org.mozilla.gecko.background.healthreport.HealthReportConstants; import org.mozilla.gecko.background.healthreport.HealthReportDatabaseStorage; import org.mozilla.gecko.background.healthreport.HealthReportGenerator; @@ -124,7 +125,7 @@ public class BrowserHealthReporter implements GeckoEventListener { GeckoProfile profile = GeckoAppShell.getGeckoInterface().getProfile(); String profilePath = profile.getDir().getAbsolutePath(); - long since = System.currentTimeMillis() - HealthReportConstants.MILLISECONDS_PER_SIX_MONTHS; + long since = System.currentTimeMillis() - GlobalConstants.MILLISECONDS_PER_SIX_MONTHS; long lastPingTime = Math.max(getLastUploadLocalTime(), HealthReportConstants.EARLIEST_LAST_PING); return generateReport(since, lastPingTime, profilePath); From 93f401fd8744150ce941b9948b9acd2b6879d971 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Mon, 19 Aug 2013 17:22:47 -0700 Subject: [PATCH 10/56] Bug 905262 - Create a JS add-on API for adding content to the promotional banner. r=wesj --- mobile/android/base/GeckoAppShell.java | 2 +- mobile/android/base/home/HomeBanner.java | 86 +++++++++++- .../base/resources/layout/home_banner.xml | 2 +- mobile/android/modules/Home.jsm | 132 ++++++++++++++++++ mobile/android/modules/moz.build | 1 + 5 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 mobile/android/modules/Home.jsm diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index a35ecc06bea..b9639a32b6c 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -2257,7 +2257,7 @@ public class GeckoAppShell sEventDispatcher.registerEventListener(event, listener); } - static EventDispatcher getEventDispatcher() { + public static EventDispatcher getEventDispatcher() { return sEventDispatcher; } diff --git a/mobile/android/base/home/HomeBanner.java b/mobile/android/base/home/HomeBanner.java index cc4fc7cc94b..b4adfb95326 100644 --- a/mobile/android/base/home/HomeBanner.java +++ b/mobile/android/base/home/HomeBanner.java @@ -5,16 +5,31 @@ package org.mozilla.gecko.home; +import org.mozilla.gecko.GeckoAppShell; +import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.R; +import org.mozilla.gecko.gfx.BitmapUtils; +import org.mozilla.gecko.util.GeckoEventListener; +import org.mozilla.gecko.util.ThreadUtils; + +import org.json.JSONException; +import org.json.JSONObject; import android.content.Context; +import android.graphics.drawable.Drawable; +import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.widget.ImageButton; +import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.TextView; -public class HomeBanner extends LinearLayout { +public class HomeBanner extends LinearLayout + implements GeckoEventListener { + private static final String LOGTAG = "GeckoHomeBanner"; public HomeBanner(Context context) { this(context, null); @@ -43,9 +58,78 @@ public class HomeBanner extends LinearLayout { HomeBanner.this.setVisibility(View.GONE); } }); + + setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Send the current message id back to JS. + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Click", (String) getTag())); + } + }); + + GeckoAppShell.getEventDispatcher().registerEventListener("HomeBanner:Data", this); + GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("HomeBanner:Get", null)); } + @Override + public void onDetachedFromWindow() { + super.onDetachedFromWindow(); + + GeckoAppShell.getEventDispatcher().unregisterEventListener("HomeBanner:Data", this); + } + public boolean isDismissed() { return (getVisibility() == View.GONE); } + + @Override + public void handleMessage(String event, JSONObject message) { + try { + // Store the current message id to pass back to JS in the view's OnClickListener. + setTag(message.getString("id")); + + final String text = message.getString("text"); + final TextView textView = (TextView) findViewById(R.id.text); + + // Update the banner message on the UI thread. + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + textView.setText(text); + setVisibility(View.VISIBLE); + } + }); + } catch (JSONException e) { + Log.e(LOGTAG, "Exception handling " + event + " message", e); + return; + } + + final String iconURI = message.optString("iconURI"); + final ImageView iconView = (ImageView) findViewById(R.id.icon); + + if (TextUtils.isEmpty(iconURI)) { + // Hide the image view if we don't have an icon to show. + iconView.setVisibility(View.GONE); + return; + } + + BitmapUtils.getDrawable(getContext(), iconURI, new BitmapUtils.BitmapLoader() { + @Override + public void onBitmapFound(final Drawable d) { + // Bail if getDrawable doesn't find anything. + if (d == null) { + iconView.setVisibility(View.GONE); + return; + } + + // Update the banner icon on the UI thread. + ThreadUtils.postToUiThread(new Runnable() { + @Override + public void run() { + iconView.setImageDrawable(d); + } + }); + } + }); + } } diff --git a/mobile/android/base/resources/layout/home_banner.xml b/mobile/android/base/resources/layout/home_banner.xml index e8a54586d38..cc2e34b1cd9 100644 --- a/mobile/android/base/resources/layout/home_banner.xml +++ b/mobile/android/base/resources/layout/home_banner.xml @@ -9,13 +9,13 @@ android:layout_width="48dip" android:layout_height="48dip" android:layout_marginLeft="10dp" - android:layout_marginRight="10dp" android:scaleType="centerInside"/> Date: Wed, 18 Sep 2013 06:47:08 +0200 Subject: [PATCH 11/56] Bug 917410: Add specializations of Optional for 'any' and 'object' types for callbacks in workers to bindings code. r=bz --- dom/bindings/Codegen.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index f532780afad..d4de0ee48b5 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -3505,7 +3505,12 @@ for (uint32_t i = 0; i < length; ++i) { raise NoSuchDescriptorError("Can't handle member callbacks in " "workers; need to sort out rooting" "issues") - declType = CGGeneric("JS::Rooted") + if isOptional: + # We have a specialization of Optional that will use a + # Rooted for the storage here. + declType = CGGeneric("JS::Handle") + else: + declType = CGGeneric("JS::Rooted") conversion = " ${declName} = &${val}.toObject();\n" declArgs = "cx" else: From c428bc08dc4d306863fdffd9fed835a34ce5364f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 17 Sep 2013 13:13:38 -0700 Subject: [PATCH 12/56] Bug 904321 - Add a test for memory reporters in remote processes. r=billm. --HG-- extra : rebase_source : bc8ab4762c373f7368192e98c9ee8f8b8712940f --- .../aboutmemory/content/aboutMemory.js | 4 +- .../components/aboutmemory/tests/Makefile.in | 2 + .../components/aboutmemory/tests/remote.xul | 12 ++ .../tests/test_memoryReporters.xul | 8 +- .../tests/test_memoryReporters2.xul | 112 ++++++++++++++++++ 5 files changed, 133 insertions(+), 5 deletions(-) create mode 100644 toolkit/components/aboutmemory/tests/remote.xul create mode 100644 toolkit/components/aboutmemory/tests/test_memoryReporters2.xul diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index 53b76fed419..fe5789155ff 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -1737,7 +1737,9 @@ function appendTreeElements(aP, aRoot, aProcess, aPadText) tIsInvalid = true; let unsafePath = aUnsafeNames.join("/"); gUnsafePathsWithInvalidValuesForThisProcess.push(unsafePath); - reportAssertionFailure("Invalid value for " + flipBackslashes(unsafePath)); + reportAssertionFailure("Invalid value (" + aT._amount + " / " + + aRoot._amount + ") for " + + flipBackslashes(unsafePath)); } // For non-leaf nodes, the entire sub-tree is put within a span so it can diff --git a/toolkit/components/aboutmemory/tests/Makefile.in b/toolkit/components/aboutmemory/tests/Makefile.in index 0f2d2dd6cb4..df8f3b55c32 100644 --- a/toolkit/components/aboutmemory/tests/Makefile.in +++ b/toolkit/components/aboutmemory/tests/Makefile.in @@ -17,6 +17,8 @@ MOCHITEST_CHROME_FILES = \ ifndef MOZ_ASAN MOCHITEST_CHROME_FILES += \ + remote.xul \ test_memoryReporters.xul \ + test_memoryReporters2.xul \ $(NULL) endif diff --git a/toolkit/components/aboutmemory/tests/remote.xul b/toolkit/components/aboutmemory/tests/remote.xul new file mode 100644 index 00000000000..7d691013055 --- /dev/null +++ b/toolkit/components/aboutmemory/tests/remote.xul @@ -0,0 +1,12 @@ + + + + + + +

Remote browser

+ + +
diff --git a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul index 10f11b060d0..c83fd09e3fd 100644 --- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -1,9 +1,10 @@ - - + From c23b3a2dac7fae120ee9ffdfd115f5397e97cecc Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 17 Sep 2013 21:55:12 -0700 Subject: [PATCH 13/56] Bug 917646 - Unbreak reading of old memory report files. r=khuey. --- .../aboutmemory/content/aboutMemory.js | 28 +++++++++++++++++-- .../tests/memory-reports-good.json | 7 +++++ .../aboutmemory/tests/test_aboutmemory3.xul | 21 +++++++++++++- .../aboutmemory/tests/test_aboutmemory4.xul | 18 ++++++++++++ 4 files changed, 71 insertions(+), 3 deletions(-) diff --git a/toolkit/components/aboutmemory/content/aboutMemory.js b/toolkit/components/aboutmemory/content/aboutMemory.js index fe5789155ff..809eb4ae953 100644 --- a/toolkit/components/aboutmemory/content/aboutMemory.js +++ b/toolkit/components/aboutmemory/content/aboutMemory.js @@ -483,6 +483,9 @@ function updateAboutMemoryFromReporters() } // Increment this if the JSON format changes. +// +// If/when this changes to 2, the beLenient() function and its use can be +// removed. var gCurrentFileFormatVersion = 1; /** @@ -958,8 +961,29 @@ function getPCollsByProcess(aProcessReports) "non-sentence explicit description"); } else { - assertInput(gSentenceRegExp.test(aDescription), - "non-sentence other description"); + const kLenientPrefixes = + ['rss/', 'pss/', 'size/', 'swap/', 'compartments/', 'ghost-windows/']; + let beLenient = function(aUnsafePath) { + for (let i = 0; i < kLenientPrefixes.length; i++) { + if (aUnsafePath.startsWith(kLenientPrefixes[i])) { + return true; + } + } + return false; + } + + // In general, non-explicit reports should have a description that is a + // complete sentence. However, we want to be able to read old saved + // reports, so we are lenient in a couple of situations where we used to + // allow non-sentence descriptions: + // - smaps reports (which were removed in bug 912165); + // - compartment and ghost-window reports (which had empty descriptions + // prior to bug 911641). + if (!beLenient(aUnsafePath)) { + assertInput(gSentenceRegExp.test(aDescription), + "non-sentence other description: " + aUnsafePath + ", " + + aDescription); + } } assert(aPresence === undefined || diff --git a/toolkit/components/aboutmemory/tests/memory-reports-good.json b/toolkit/components/aboutmemory/tests/memory-reports-good.json index 8c5c2412a54..f3ec6ff4b00 100644 --- a/toolkit/components/aboutmemory/tests/memory-reports-good.json +++ b/toolkit/components/aboutmemory/tests/memory-reports-good.json @@ -7,6 +7,13 @@ {"process": "Main Process (pid NNN)", "path": "other/a", "kind": 2, "units": 0, "amount": 209715, "description": "Other a."}, {"process": "Main Process (pid NNN)", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 52428800, "description": "A b."}, + {"process": "Main Process (pid NNN)", "path": "size/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "rss/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "pss/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "swap/a", "kind": 1, "units": 0, "amount": 1024, "description": "non-sentence"}, + {"process": "Main Process (pid NNN)", "path": "compartments/system/a", "kind": 1, "units": 0, "amount": 1024, "description": ""}, + {"process": "Main Process (pid NNN)", "path": "ghost-windows/a", "kind": 1, "units": 0, "amount": 1024, "description": ""}, + {"process": "Explicit-only process", "path": "explicit/a/b", "kind": 1, "units": 0, "amount": 100000, "description": "A b."}, {"process": "Other-only process", "path": "a/b", "kind": 1, "units": 0, "amount": 100000, "description": "A b."}, diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul index 38c55f7d858..a99a1c175e8 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory3.xul @@ -213,10 +213,28 @@ Explicit Allocations\n\ \n\ Other Measurements\n\ \n\ +1,024 B (100.0%) -- compartments\n\ +└──1,024 B (100.0%) ── system/a\n\ +\n\ +1,024 B (100.0%) -- ghost-windows\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ 314,572 B (100.0%) -- other\n\ ├──209,715 B (66.67%) ── a\n\ └──104,857 B (33.33%) ── b\n\ \n\ +1,024 B (100.0%) -- pss\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +1,024 B (100.0%) -- rss\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +1,024 B (100.0%) -- size\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ +1,024 B (100.0%) -- swap\n\ +└──1,024 B (100.0%) ── a\n\ +\n\ 262,144,000 B ── heap-allocated\n\ \n\ Other-only process\n\ @@ -309,12 +327,13 @@ Other Measurements\n\ // This loads a pre-existing file that is valid. { filename: "memory-reports-good.json", expected: expectedGood, dumpFirst: false }, - // This dumps to a file and then reads it back in. The output is the same as the first test. + // This dumps to a file and then reads it back in. { filename: "memory-reports-dumped.json.gz", expected: expectedGood2, dumpFirst: true }, // This loads a pre-existing file that is invalid. { filename: "memory-reports-bad.json", expected: expectedBad, dumpFirst: false }, + // This loads a pre-existing diff file. { filename: "memory-reports-diff1.json", filename2: "memory-reports-diff2.json", expected: expectedDiff, dumpFirst: false } ]; diff --git a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul index 22ce18777e4..000711a36ae 100644 --- a/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul +++ b/toolkit/components/aboutmemory/tests/test_aboutmemory4.xul @@ -108,10 +108,28 @@ Explicit Allocations\n\ \n\ Other Measurements\n\ \n\ +0.00 MB (100.0%) -- compartments\n\ +└──0.00 MB (100.0%) ── system/a\n\ +\n\ +0.00 MB (100.0%) -- ghost-windows\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ 0.30 MB (100.0%) -- other\n\ ├──0.20 MB (66.67%) ── a\n\ └──0.10 MB (33.33%) ── b\n\ \n\ +0.00 MB (100.0%) -- pss\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +0.00 MB (100.0%) -- rss\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +0.00 MB (100.0%) -- size\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ +0.00 MB (100.0%) -- swap\n\ +└──0.00 MB (100.0%) ── a\n\ +\n\ 250.00 MB ── heap-allocated\n\ \n\ Other-only process\n\ From 19d3aaeb0f34ef27ebf4a85c7e8254325c8be76e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20Qu=C3=A8ze?= Date: Wed, 18 Sep 2013 07:12:38 +0200 Subject: [PATCH 14/56] Bug 916162 - The location of JS errors caused by frameworker message handlers isn't reported, r=markh. --- toolkit/components/social/MessagePortWorker.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/toolkit/components/social/MessagePortWorker.js b/toolkit/components/social/MessagePortWorker.js index 29f889cd5ae..80d495472b5 100644 --- a/toolkit/components/social/MessagePortWorker.js +++ b/toolkit/components/social/MessagePortWorker.js @@ -19,7 +19,9 @@ WorkerPort.prototype = { }, _onerror: function fw_WorkerPort_onerror(err) { - throw new Error("Port " + this + " handler failed: " + err); + // We throw an object that "derives" from the exception, but with + // a more detailed message. + throw {message: "Port " + this + " handler failed: " + err.message, __proto__: err}; } } From 5c3bb55d7c3a974964394dfc60a563111afe03f0 Mon Sep 17 00:00:00 2001 From: Andrew Quartey Date: Wed, 18 Sep 2013 01:29:04 -0400 Subject: [PATCH 15/56] Bug 850364 - Implement setRangeText for HTMLInputElement. r=ehsan --- content/html/content/src/HTMLInputElement.cpp | 113 ++++++++++- content/html/content/src/HTMLInputElement.h | 17 ++ content/html/content/test/forms/Makefile.in | 1 + .../test/forms/test_set_range_text.html | 183 ++++++++++++++++++ dom/webidl/HTMLInputElement.webidl | 14 +- 5 files changed, 325 insertions(+), 3 deletions(-) create mode 100644 content/html/content/test/forms/test_set_range_text.html diff --git a/content/html/content/src/HTMLInputElement.cpp b/content/html/content/src/HTMLInputElement.cpp index 731dceda681..dc5e9368cf5 100644 --- a/content/html/content/src/HTMLInputElement.cpp +++ b/content/html/content/src/HTMLInputElement.cpp @@ -7,7 +7,6 @@ #include "mozilla/DebugOnly.h" #include "mozilla/dom/Date.h" -#include "mozilla/dom/HTMLInputElementBinding.h" #include "nsAsyncDOMEvent.h" #include "nsAttrValueInlines.h" @@ -4504,6 +4503,11 @@ HTMLInputElement::SetSelectionRange(int32_t aSelectionStart, if (!aRv.Failed()) { aRv = textControlFrame->ScrollSelectionIntoView(); } + + nsContentUtils::DispatchTrustedEvent(OwnerDoc(), + static_cast(this), + NS_LITERAL_STRING("select"), true, + false); } } } @@ -4521,6 +4525,113 @@ HTMLInputElement::SetSelectionRange(int32_t aSelectionStart, return rv.ErrorCode(); } +void +HTMLInputElement::SetRangeText(const nsAString& aReplacement, ErrorResult& aRv) +{ + if (!SupportsSetRangeText()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + + int32_t start, end; + aRv = GetSelectionRange(&start, &end); + if (aRv.Failed()) { + nsTextEditorState* state = GetEditorState(); + if (state && state->IsSelectionCached()) { + start = state->GetSelectionProperties().mStart; + end = state->GetSelectionProperties().mEnd; + aRv = NS_OK; + } + } + + SetRangeText(aReplacement, start, end, mozilla::dom::SelectionMode::Preserve, + aRv, start, end); +} + +void +HTMLInputElement::SetRangeText(const nsAString& aReplacement, uint32_t aStart, + uint32_t aEnd, const SelectionMode& aSelectMode, + ErrorResult& aRv, int32_t aSelectionStart, + int32_t aSelectionEnd) +{ + if (!SupportsSetRangeText()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + + if (aStart > aEnd) { + aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); + return; + } + + nsAutoString value; + GetValueInternal(value); + uint32_t inputValueLength = value.Length(); + + if (aStart > inputValueLength) { + aStart = inputValueLength; + } + + if (aEnd > inputValueLength) { + aEnd = inputValueLength; + } + + if (aSelectionStart == -1 && aSelectionEnd == -1) { + aRv = GetSelectionRange(&aSelectionStart, &aSelectionEnd); + if (aRv.Failed()) { + nsTextEditorState* state = GetEditorState(); + if (state && state->IsSelectionCached()) { + aSelectionStart = state->GetSelectionProperties().mStart; + aSelectionEnd = state->GetSelectionProperties().mEnd; + aRv = NS_OK; + } + } + } + + if (aStart < aEnd) { + value.Replace(aStart, aEnd - aStart, aReplacement); + SetValueInternal(value, false, false); + } + + uint32_t newEnd = aStart + aReplacement.Length(); + int32_t delta = aReplacement.Length() - (aEnd - aStart); + + switch (aSelectMode) { + case mozilla::dom::SelectionMode::Select: + { + aSelectionStart = aStart; + aSelectionEnd = newEnd; + } + break; + case mozilla::dom::SelectionMode::Start: + { + aSelectionStart = aSelectionEnd = aStart; + } + break; + case mozilla::dom::SelectionMode::End: + { + aSelectionStart = aSelectionEnd = newEnd; + } + break; + case mozilla::dom::SelectionMode::Preserve: + { + if ((uint32_t)aSelectionStart > aEnd) + aSelectionStart += delta; + else if ((uint32_t)aSelectionStart > aStart) + aSelectionStart = aStart; + + if ((uint32_t)aSelectionEnd > aEnd) + aSelectionEnd += delta; + else if ((uint32_t)aSelectionEnd > aStart) + aSelectionEnd = newEnd; + } + break; + } + + Optional direction; + SetSelectionRange(aSelectionStart, aSelectionEnd, direction, aRv); +} + int32_t HTMLInputElement::GetSelectionStart(ErrorResult& aRv) { diff --git a/content/html/content/src/HTMLInputElement.h b/content/html/content/src/HTMLInputElement.h index c289c146d6d..27446398393 100644 --- a/content/html/content/src/HTMLInputElement.h +++ b/content/html/content/src/HTMLInputElement.h @@ -17,6 +17,7 @@ #include "nsCOMPtr.h" #include "nsIConstraintValidation.h" #include "mozilla/dom/HTMLFormElement.h" // for HasEverTriedInvalidSubmit() +#include "mozilla/dom/HTMLInputElementBinding.h" #include "nsIFilePicker.h" #include "nsIContentPrefService2.h" #include "mozilla/Decimal.h" @@ -641,6 +642,13 @@ public: const Optional< nsAString >& direction, ErrorResult& aRv); + void SetRangeText(const nsAString& aReplacement, ErrorResult& aRv); + + void SetRangeText(const nsAString& aReplacement, uint32_t aStart, + uint32_t aEnd, const SelectionMode& aSelectMode, + ErrorResult& aRv, int32_t aSelectionStart = -1, + int32_t aSelectionEnd = -1); + // XPCOM GetAlign() is OK void SetAlign(const nsAString& aValue, ErrorResult& aRv) { @@ -1239,6 +1247,15 @@ private: return MayFireChangeOnBlur(mType); } + /** + * Returns true if setRangeText can be called on element + */ + bool SupportsSetRangeText() const { + return mType == NS_FORM_INPUT_TEXT || mType == NS_FORM_INPUT_SEARCH || + mType == NS_FORM_INPUT_URL || mType == NS_FORM_INPUT_TEL || + mType == NS_FORM_INPUT_PASSWORD; + } + static bool MayFireChangeOnBlur(uint8_t aType) { return IsSingleLineTextControl(false, aType) || aType == NS_FORM_INPUT_RANGE; diff --git a/content/html/content/test/forms/Makefile.in b/content/html/content/test/forms/Makefile.in index 6119d2385e0..06359c028e1 100644 --- a/content/html/content/test/forms/Makefile.in +++ b/content/html/content/test/forms/Makefile.in @@ -5,6 +5,7 @@ MOCHITEST_FILES = \ save_restore_radio_groups.sjs \ test_save_restore_radio_groups.html \ + test_set_range_text.html \ test_change_event.html \ test_mozistextfield.html \ test_input_attributes_reflection.html \ diff --git a/content/html/content/test/forms/test_set_range_text.html b/content/html/content/test/forms/test_set_range_text.html new file mode 100644 index 00000000000..8ba01587f51 --- /dev/null +++ b/content/html/content/test/forms/test_set_range_text.html @@ -0,0 +1,183 @@ + + + + +Test for Bug 850364 + + + + + +Mozilla Bug 850364 +

+
+ + + + + + + + + + + + + + + + + + + +
+
+
+
+ + diff --git a/dom/webidl/HTMLInputElement.webidl b/dom/webidl/HTMLInputElement.webidl index d8cf6f33103..ba1bb610f2e 100644 --- a/dom/webidl/HTMLInputElement.webidl +++ b/dom/webidl/HTMLInputElement.webidl @@ -12,6 +12,13 @@ * and create derivative works of this document. */ +enum SelectionMode { + "select", + "start", + "end", + "preserve", +}; + interface nsIControllers; interface HTMLInputElement : HTMLElement { @@ -114,8 +121,11 @@ interface HTMLInputElement : HTMLElement { attribute long selectionEnd; [Throws] attribute DOMString selectionDirection; - // Bug 850364 void setRangeText(DOMString replacement); - // Bug 850364 setRangeText(DOMString replacement, unsigned long start, unsigned long end, optional SelectionMode selectionMode); + [Throws] + void setRangeText(DOMString replacement); + [Throws] + void setRangeText(DOMString replacement, unsigned long start, + unsigned long end, optional SelectionMode selectionMode = "preserve"); // also has obsolete members }; From 0571068409b54edcbd429e28d7c3030f465077fa Mon Sep 17 00:00:00 2001 From: Axel Hecht Date: Wed, 18 Sep 2013 09:52:51 +0200 Subject: [PATCH 16/56] bug 915721, package limited set of localized files for b2g, r=wesj --- b2g/app/b2g.js | 5 ++++ b2g/locales/Makefile.in | 1 - b2g/locales/jar.mn | 58 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 5d97332b6ba..bb7eccffc70 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -135,6 +135,11 @@ pref("browser.search.suggest.enabled", true); // tell the search service that we don't really expose the "current engine" pref("browser.search.noCurrentEngine", true); +// Enable sparse localization by setting a few package locale overrides +pref("chrome.override_package.global", "b2g-l10n"); +pref("chrome.override_package.mozapps", "b2g-l10n"); +pref("chrome.override_package.passwordmgr", "b2g-l10n"); + // enable xul error pages pref("browser.xul.error_pages.enabled", true); diff --git a/b2g/locales/Makefile.in b/b2g/locales/Makefile.in index 8366e4340bd..b3b1f85b222 100644 --- a/b2g/locales/Makefile.in +++ b/b2g/locales/Makefile.in @@ -74,7 +74,6 @@ libs-%: # Tailored target to just add the chrome processing for multi-locale builds chrome-%: - @$(MAKE) -C ../../toolkit/locales chrome-$* @$(MAKE) chrome AB_CD=$* @$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales chrome AB_CD=$* diff --git a/b2g/locales/jar.mn b/b2g/locales/jar.mn index d9397c8c5e1..72a443723c5 100644 --- a/b2g/locales/jar.mn +++ b/b2g/locales/jar.mn @@ -13,3 +13,61 @@ * locale/@AB_CD@/b2g-l10n/netError.dtd (%chrome/overrides/netError.dtd) * locale/@AB_CD@/b2g-l10n/aboutCertError.dtd (%chrome/overrides/aboutCertError.dtd) * locale/@AB_CD@/b2g-l10n/appstrings.properties (%chrome/overrides/appstrings.properties) + + +# overrides for toolkit l10n, also for en-US +relativesrcdir toolkit/locales: + locale/@AB_CD@/b2g-l10n/overrides/about.dtd (%chrome/global/about.dtd) + locale/@AB_CD@/b2g-l10n/overrides/aboutAbout.dtd (%chrome/global/aboutAbout.dtd) + locale/@AB_CD@/b2g-l10n/overrides/aboutRights.dtd (%chrome/global/aboutRights.dtd) + locale/@AB_CD@/b2g-l10n/overrides/commonDialogs.properties (%chrome/global/commonDialogs.properties) + locale/@AB_CD@/b2g-l10n/overrides/handling/handling.properties (%chrome/mozapps/handling/handling.properties) + locale/@AB_CD@/b2g-l10n/overrides/intl.properties (%chrome/global/intl.properties) + locale/@AB_CD@/b2g-l10n/overrides/intl.css (%chrome/global/intl.css) + locale/@AB_CD@/b2g-l10n/overrides/passwordmgr.properties (%chrome/passwordmgr/passwordmgr.properties) + locale/@AB_CD@/b2g-l10n/overrides/search/search.properties (%chrome/search/search.properties) + locale/@AB_CD@/b2g-l10n/overrides/update/updates.properties (%chrome/mozapps/update/updates.properties) +# about:support + locale/@AB_CD@/b2g-l10n/overrides/global/aboutSupport.dtd (%chrome/global/aboutSupport.dtd) + locale/@AB_CD@/b2g-l10n/overrides/global/aboutSupport.properties (%chrome/global/aboutSupport.properties) +#about:crashes + locale/@AB_CD@/b2g-l10n/overrides/crashreporter/crashes.dtd (%crashreporter/crashes.dtd) + locale/@AB_CD@/b2g-l10n/overrides/crashreporter/crashes.properties (%crashreporter/crashes.properties) +#about:mozilla + locale/@AB_CD@/b2g-l10n/overrides/global/mozilla.dtd (%chrome/global/mozilla.dtd) +#about:telemetry + locale/@AB_CD@/b2g-l10n/overrides/global/aboutTelemetry.dtd (%chrome/global/aboutTelemetry.dtd) + locale/@AB_CD@/b2g-l10n/overrides/global/aboutTelemetry.properties (%chrome/global/aboutTelemetry.properties) + +% override chrome://global/locale/about.dtd chrome://b2g-l10n/locale/overrides/about.dtd +% override chrome://global/locale/aboutAbout.dtd chrome://b2g-l10n/locale/overrides/aboutAbout.dtd +% override chrome://global/locale/aboutRights.dtd chrome://b2g-l10n/locale/overrides/aboutRights.dtd +% override chrome://global/locale/commonDialogs.properties chrome://b2g-l10n/locale/overrides/commonDialogs.properties +% override chrome://mozapps/locale/handling/handling.properties chrome://b2g-l10n/locale/overrides/handling/handling.properties +% override chrome://global/locale/intl.properties chrome://b2g-l10n/locale/overrides/intl.properties +% override chrome://global/locale/intl.css chrome://b2g-l10n/locale/overrides/intl.css +% override chrome://passwordmgr/locale/passwordmgr.properties chrome://b2g-l10n/locale/overrides/passwordmgr/passwordmgr.properties +% override chrome://global/locale/search/search.properties chrome://b2g-l10n/locale/overrides/search/search.properties +% override chrome://mozapps/locale/update/updates.properties chrome://b2g-l10n/locale/overrides/update/updates.properties +% override chrome://global/locale/aboutSupport.dtd chrome://b2g-l10n/locale/overrides/global/aboutSupport.dtd +% override chrome://global/locale/aboutSupport.properties chrome://b2g-l10n/locale/overrides/global/aboutSupport.properties +% override chrome://global/locale/crashes.dtd chrome://b2g-l10n/locale/overrides/crashreporter/crashes.dtd +% override chrome://global/locale/crashes.properties chrome://b2g-l10n/locale/overrides/crashreporter/crashes.properties +% override chrome://global/locale/mozilla.dtd chrome://b2g-l10n/locale/overrides/global/mozilla.dtd +% override chrome://global/locale/aboutTelemetry.dtd chrome://b2g-l10n/locale/overrides/global/aboutTelemetry.dtd +% override chrome://global/locale/aboutTelemetry.properties chrome://b2g-l10n/locale/overrides/global/aboutTelemetry.properties + +# overrides for dom l10n, also for en-US +relativesrcdir dom/locales: + locale/@AB_CD@/b2g-l10n/overrides/charsetTitles.properties (%chrome/charsetTitles.properties) + locale/@AB_CD@/b2g-l10n/overrides/global.dtd (%chrome/global.dtd) + locale/@AB_CD@/b2g-l10n/overrides/AccessFu.properties (%chrome/accessibility/AccessFu.properties) + locale/@AB_CD@/b2g-l10n/overrides/dom/dom.properties (%chrome/dom/dom.properties) +#about:plugins + locale/@AB_CD@/b2g-l10n/overrides/plugins.properties (%chrome/plugins.properties) + +% override chrome://global/locale/charsetTitles.properties chrome://b2g-l10n/locale/overrides/charsetTitles.properties +% override chrome://global/locale/global.dtd chrome://b2g-l10n/locale/overrides/global.dtd +% override chrome://global/locale/AccessFu.properties chrome://b2g-l10n/locale/overrides/AccessFu.properties +% override chrome://global/locale/dom/dom.properties chrome://b2g-l10n/locale/overrides/dom/dom.properties +% override chrome://global/locale/plugins.properties chrome://b2g-l10n/locale/overrides/plugins.properties From 4e10124da3c5619a6eee856aa021f2f2efaf0d94 Mon Sep 17 00:00:00 2001 From: Eddy Bruel Date: Tue, 17 Sep 2013 15:37:00 +0300 Subject: [PATCH 17/56] Make Debugger.Source load the source text if it is not retained (bug 916845). r=jimb --- js/src/jsapi.cpp | 2 +- js/src/jsfriendapi.h | 2 +- js/src/jsfun.cpp | 2 +- js/src/jsscript.cpp | 9 ++++----- js/src/jsscript.h | 2 +- js/src/vm/Debugger.cpp | 7 ++++++- js/xpconnect/src/XPCJSRuntime.cpp | 4 +--- 7 files changed, 15 insertions(+), 13 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 4e13ea34233..391cc52a903 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -4802,7 +4802,7 @@ JS_DecompileScript(JSContext *cx, JSScript *scriptArg, const char *name, unsigne if (fun) return JS_DecompileFunction(cx, fun, indent); bool haveSource = script->scriptSource()->hasSourceData(); - if (!haveSource && !JSScript::loadSource(cx, script, &haveSource)) + if (!haveSource && !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) return NULL; return haveSource ? script->sourceData(cx) : js_NewStringCopyZ(cx, "[no source]"); } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 0a03fda9857..3cb557ad3ff 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -203,7 +203,7 @@ struct JSFunctionSpecWithHelp { extern JS_FRIEND_API(bool) JS_DefineFunctionsWithHelp(JSContext *cx, JSObject *obj, const JSFunctionSpecWithHelp *fs); -typedef bool (* JS_SourceHook)(JSContext *cx, JS::Handle script, +typedef bool (* JS_SourceHook)(JSContext *cx, const char *filename, jschar **src, uint32_t *length); extern JS_FRIEND_API(void) diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index bdf46e188c5..0e5b5918638 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -649,7 +649,7 @@ js::FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lamb } bool haveSource = fun->isInterpreted() && !fun->isSelfHostedBuiltin(); if (haveSource && !script->scriptSource()->hasSourceData() && - !JSScript::loadSource(cx, script, &haveSource)) + !JSScript::loadSource(cx, script->scriptSource(), &haveSource)) { return NULL; } diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 7cfd12c03bf..8e24d92262b 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -991,19 +991,18 @@ ScriptSource::adjustDataSize(size_t nbytes) } /* static */ bool -JSScript::loadSource(JSContext *cx, HandleScript script, bool *worked) +JSScript::loadSource(JSContext *cx, ScriptSource *ss, bool *worked) { - JS_ASSERT(!script->scriptSource()->hasSourceData()); + JS_ASSERT(!ss->hasSourceData()); *worked = false; - if (!cx->runtime()->sourceHook || !script->scriptSource()->sourceRetrievable()) + if (!cx->runtime()->sourceHook || !ss->sourceRetrievable()) return true; jschar *src = NULL; uint32_t length; - if (!cx->runtime()->sourceHook(cx, script, &src, &length)) + if (!cx->runtime()->sourceHook(cx, ss->filename(), &src, &length)) return false; if (!src) return true; - ScriptSource *ss = script->scriptSource(); ss->setSource(src, length); *worked = true; return true; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index 680b5ae0ad1..b872b4f515e 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -797,7 +797,7 @@ class JSScript : public js::gc::Cell JSFlatString *sourceData(JSContext *cx); - static bool loadSource(JSContext *cx, js::HandleScript scr, bool *worked); + static bool loadSource(JSContext *cx, js::ScriptSource *ss, bool *worked); void setSourceObject(js::ScriptSourceObject *object); js::ScriptSourceObject *sourceObject() const; diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 0e2f1a4ccba..759c57c4e5d 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -3715,7 +3715,12 @@ DebuggerSource_getText(JSContext *cx, unsigned argc, Value *vp) THIS_DEBUGSOURCE_REFERENT(cx, argc, vp, "(get text)", args, obj, sourceObject); ScriptSource *ss = sourceObject->source(); - JSString *str = ss->substring(cx, 0, ss->length()); + bool hasSourceData = ss->hasSourceData(); + if (!ss->hasSourceData() && !JSScript::loadSource(cx, ss, &hasSourceData)) + return false; + + JSString *str = hasSourceData ? ss->substring(cx, 0, ss->length()) + : js_NewStringCopyZ(cx, "[no source]"); if (!str) return false; diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 5f0bcfab4c3..f6d0575577c 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2849,8 +2849,7 @@ ReadSourceFromFilename(JSContext *cx, const char *filename, jschar **src, uint32 function. See the comment in the XPCJSRuntime constructor. */ static bool -SourceHook(JSContext *cx, JS::Handle script, jschar **src, - uint32_t *length) +SourceHook(JSContext *cx, const char *filename, jschar **src, uint32_t *length) { *src = NULL; *length = 0; @@ -2858,7 +2857,6 @@ SourceHook(JSContext *cx, JS::Handle script, jschar **src, if (!nsContentUtils::IsCallerChrome()) return true; - const char *filename = JS_GetScriptFilename(cx, script); if (!filename) return true; From f0635e18eb47fc0fc187fceb7342e05b702a9ae8 Mon Sep 17 00:00:00 2001 From: Max Li Date: Tue, 17 Sep 2013 20:07:45 -0400 Subject: [PATCH 18/56] Bug 899333 - Pivot text traversal should traverse into the subtree. r=tbsaunde --- accessible/src/base/nsAccessiblePivot.cpp | 342 ++++++++++++++---- accessible/src/base/nsAccessiblePivot.h | 6 + .../pivot/doc_virtualcursor_text.html | 19 +- .../pivot/test_virtualcursor_text.html | 164 ++++++++- 4 files changed, 446 insertions(+), 85 deletions(-) diff --git a/accessible/src/base/nsAccessiblePivot.cpp b/accessible/src/base/nsAccessiblePivot.cpp index 43e1b6efb99..d0f712b599a 100644 --- a/accessible/src/base/nsAccessiblePivot.cpp +++ b/accessible/src/base/nsAccessiblePivot.cpp @@ -300,47 +300,114 @@ nsAccessiblePivot::MoveNextByText(TextBoundaryType aBoundary, bool* aResult) *aResult = false; - int32_t oldStart = mStartOffset, oldEnd = mEndOffset; - HyperTextAccessible* text = mPosition->AsHyperText(); - Accessible* oldPosition = mPosition; - while (!text) { - oldPosition = mPosition; - mPosition = mPosition->Parent(); - text = mPosition->AsHyperText(); - } + int32_t tempStart = mStartOffset, tempEnd = mEndOffset; + Accessible* tempPosition = mPosition; + Accessible* root = GetActiveRoot(); + while (true) { + Accessible* curPosition = tempPosition; + HyperTextAccessible* text; + // Find the nearest text node using a preorder traversal starting from + // the current node. + if (!(text = tempPosition->AsHyperText())) { + text = SearchForText(tempPosition, false); + if (!text) + return NS_OK; + if (text != curPosition) + tempStart = tempEnd = -1; + tempPosition = text; + } - if (mEndOffset == -1) - mEndOffset = text != oldPosition ? text->GetChildOffset(oldPosition) : 0; + // If the search led to the parent of the node we started on (e.g. when + // starting on a text leaf), start the text movement from the end of that + // node, otherwise we just default to 0. + if (tempEnd == -1) + tempEnd = text == curPosition->Parent() ? + text->GetChildOffset(curPosition) : 0; - if (mEndOffset == text->CharacterCount()) + // If there's no more text on the current node, try to find the next text + // node; if there isn't one, bail out. + if (tempEnd == text->CharacterCount()) { + if (tempPosition == root) + return NS_OK; + + // If we're currently sitting on a link, try move to either the next + // sibling or the parent, whichever is closer to the current end + // offset. Otherwise, do a forward search for the next node to land on + // (we don't do this in the first case because we don't want to go to the + // subtree). + Accessible* sibling = tempPosition->NextSibling(); + if (tempPosition->IsLink()) { + if (sibling && sibling->IsLink()) { + tempStart = tempEnd = -1; + tempPosition = sibling; + } else { + tempStart = tempPosition->StartOffset(); + tempEnd = tempPosition->EndOffset(); + tempPosition = tempPosition->Parent(); + } + } else { + tempPosition = SearchForText(tempPosition, false); + if (!tempPosition) + return NS_OK; + tempStart = tempEnd = -1; + } + continue; + } + + AccessibleTextBoundary startBoundary, endBoundary; + switch (aBoundary) { + case CHAR_BOUNDARY: + startBoundary = nsIAccessibleText::BOUNDARY_CHAR; + endBoundary = nsIAccessibleText::BOUNDARY_CHAR; + break; + case WORD_BOUNDARY: + startBoundary = nsIAccessibleText::BOUNDARY_WORD_START; + endBoundary = nsIAccessibleText::BOUNDARY_WORD_END; + break; + default: + return NS_ERROR_INVALID_ARG; + } + + nsAutoString unusedText; + int32_t newStart = 0, newEnd = 0, currentEnd = tempEnd; + text->GetTextAtOffset(tempEnd, endBoundary, &newStart, &tempEnd, unusedText); + text->GetTextBeforeOffset(tempEnd, startBoundary, &newStart, &newEnd, + unusedText); + int32_t potentialStart = newEnd == tempEnd ? newStart : newEnd; + tempStart = potentialStart > tempStart ? potentialStart : currentEnd; + + // The offset range we've obtained might have embedded characters in it, + // limit the range to the start of the first occurrence of an embedded + // character. + Accessible* childAtOffset = nullptr; + for (int32_t i = tempStart; i < tempEnd; i++) { + childAtOffset = text->GetChildAtOffset(i); + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) { + tempEnd = i; + break; + } + } + // If there's an embedded character at the very start of the range, we + // instead want to traverse into it. So restart the movement with + // the child as the starting point. + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) && + tempStart == childAtOffset->StartOffset()) { + tempPosition = childAtOffset; + tempStart = tempEnd = -1; + continue; + } + + *aResult = true; + + Accessible* startPosition = mPosition; + int32_t oldStart = mStartOffset, oldEnd = mEndOffset; + mPosition = tempPosition; + mStartOffset = tempStart; + mEndOffset = tempEnd; + NotifyOfPivotChange(startPosition, oldStart, oldEnd, + nsIAccessiblePivot::REASON_TEXT); return NS_OK; - - AccessibleTextBoundary startBoundary, endBoundary; - switch (aBoundary) { - case CHAR_BOUNDARY: - startBoundary = nsIAccessibleText::BOUNDARY_CHAR; - endBoundary = nsIAccessibleText::BOUNDARY_CHAR; - break; - case WORD_BOUNDARY: - startBoundary = nsIAccessibleText::BOUNDARY_WORD_START; - endBoundary = nsIAccessibleText::BOUNDARY_WORD_END; - break; - default: - return NS_ERROR_INVALID_ARG; } - - nsAutoString unusedText; - int32_t newStart = 0, newEnd = 0; - text->GetTextAtOffset(mEndOffset, endBoundary, &newStart, &mEndOffset, unusedText); - text->GetTextBeforeOffset(mEndOffset, startBoundary, &newStart, &newEnd, - unusedText); - mStartOffset = newEnd == mEndOffset ? newStart : newEnd; - - *aResult = true; - - NotifyOfPivotChange(mPosition, oldStart, oldEnd, - nsIAccessiblePivot::REASON_TEXT); - return NS_OK; } NS_IMETHODIMP @@ -350,52 +417,127 @@ nsAccessiblePivot::MovePreviousByText(TextBoundaryType aBoundary, bool* aResult) *aResult = false; - int32_t oldStart = mStartOffset, oldEnd = mEndOffset; - HyperTextAccessible* text = mPosition->AsHyperText(); - Accessible* oldPosition = mPosition; - while (!text) { - oldPosition = mPosition; - mPosition = mPosition->Parent(); - text = mPosition->AsHyperText(); - } + int32_t tempStart = mStartOffset, tempEnd = mEndOffset; + Accessible* tempPosition = mPosition; + Accessible* root = GetActiveRoot(); + while (true) { + Accessible* curPosition = tempPosition; + HyperTextAccessible* text; + // Find the nearest text node using a reverse preorder traversal starting + // from the current node. + if (!(text = tempPosition->AsHyperText())) { + text = SearchForText(tempPosition, true); + if (!text) + return NS_OK; + if (text != curPosition) + tempStart = tempEnd = -1; + tempPosition = text; + } - if (mStartOffset == -1) - mStartOffset = text != oldPosition ? text->GetChildOffset(oldPosition) : 0; + // If the search led to the parent of the node we started on (e.g. when + // starting on a text leaf), start the text movement from the end of that + // node, otherwise we just default to 0. + if (tempStart == -1) { + if (tempPosition != curPosition) + tempStart = text == curPosition->Parent() ? + text->GetChildOffset(curPosition) : text->CharacterCount(); + else + tempStart = 0; + } - if (mStartOffset == 0) + // If there's no more text on the current node, try to find the previous + // text node; if there isn't one, bail out. + if (tempStart == 0) { + if (tempPosition == root) + return NS_OK; + + // If we're currently sitting on a link, try move to either the previous + // sibling or the parent, whichever is closer to the current end + // offset. Otherwise, do a forward search for the next node to land on + // (we don't do this in the first case because we don't want to go to the + // subtree). + Accessible* sibling = tempPosition->PrevSibling(); + if (tempPosition->IsLink()) { + if (sibling && sibling->IsLink()) { + HyperTextAccessible* siblingText = sibling->AsHyperText(); + tempStart = tempEnd = siblingText ? + siblingText->CharacterCount() : -1; + tempPosition = sibling; + } else { + tempStart = tempPosition->StartOffset(); + tempEnd = tempPosition->EndOffset(); + tempPosition = tempPosition->Parent(); + } + } else { + HyperTextAccessible* tempText = SearchForText(tempPosition, true); + if (!tempText) + return NS_OK; + tempPosition = tempText; + tempStart = tempEnd = tempText->CharacterCount(); + } + continue; + } + + AccessibleTextBoundary startBoundary, endBoundary; + switch (aBoundary) { + case CHAR_BOUNDARY: + startBoundary = nsIAccessibleText::BOUNDARY_CHAR; + endBoundary = nsIAccessibleText::BOUNDARY_CHAR; + break; + case WORD_BOUNDARY: + startBoundary = nsIAccessibleText::BOUNDARY_WORD_START; + endBoundary = nsIAccessibleText::BOUNDARY_WORD_END; + break; + default: + return NS_ERROR_INVALID_ARG; + } + + nsAutoString unusedText; + int32_t newStart = 0, newEnd = 0, currentStart = tempStart, potentialEnd = 0; + text->GetTextBeforeOffset(tempStart, startBoundary, &newStart, &newEnd, + unusedText); + if (newStart < tempStart) + tempStart = newEnd >= currentStart ? newStart : newEnd; + else // XXX: In certain odd cases newStart is equal to tempStart + text->GetTextBeforeOffset(tempStart - 1, startBoundary, &newStart, + &tempStart, unusedText); + text->GetTextAtOffset(tempStart, endBoundary, &newStart, &potentialEnd, + unusedText); + tempEnd = potentialEnd < tempEnd ? potentialEnd : currentStart; + + // The offset range we've obtained might have embedded characters in it, + // limit the range to the start of the last occurrence of an embedded + // character. + Accessible* childAtOffset = nullptr; + for (int32_t i = tempEnd - 1; i >= tempStart; i--) { + childAtOffset = text->GetChildAtOffset(i); + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset)) { + tempStart = childAtOffset->EndOffset(); + break; + } + } + // If there's an embedded character at the very end of the range, we + // instead want to traverse into it. So restart the movement with + // the child as the starting point. + if (childAtOffset && nsAccUtils::IsEmbeddedObject(childAtOffset) && + tempEnd == childAtOffset->EndOffset()) { + tempPosition = childAtOffset; + tempStart = tempEnd = childAtOffset->AsHyperText()->CharacterCount(); + continue; + } + + *aResult = true; + + Accessible* startPosition = mPosition; + int32_t oldStart = mStartOffset, oldEnd = mEndOffset; + mPosition = tempPosition; + mStartOffset = tempStart; + mEndOffset = tempEnd; + + NotifyOfPivotChange(startPosition, oldStart, oldEnd, + nsIAccessiblePivot::REASON_TEXT); return NS_OK; - - AccessibleTextBoundary startBoundary, endBoundary; - switch (aBoundary) { - case CHAR_BOUNDARY: - startBoundary = nsIAccessibleText::BOUNDARY_CHAR; - endBoundary = nsIAccessibleText::BOUNDARY_CHAR; - break; - case WORD_BOUNDARY: - startBoundary = nsIAccessibleText::BOUNDARY_WORD_START; - endBoundary = nsIAccessibleText::BOUNDARY_WORD_END; - break; - default: - return NS_ERROR_INVALID_ARG; } - - nsAutoString unusedText; - int32_t newStart = 0, newEnd = 0; - text->GetTextBeforeOffset(mStartOffset, startBoundary, &newStart, &newEnd, - unusedText); - if (newStart < mStartOffset) - mStartOffset = newEnd == mStartOffset ? newStart : newEnd; - else // XXX: In certain odd cases newStart is equal to mStartOffset - text->GetTextBeforeOffset(mStartOffset - 1, startBoundary, &newStart, - &mStartOffset, unusedText); - text->GetTextAtOffset(mStartOffset, endBoundary, &newStart, &mEndOffset, - unusedText); - - *aResult = true; - - NotifyOfPivotChange(mPosition, oldStart, oldEnd, - nsIAccessiblePivot::REASON_TEXT); - return NS_OK; } NS_IMETHODIMP @@ -636,6 +778,48 @@ nsAccessiblePivot::SearchForward(Accessible* aAccessible, return nullptr; } +HyperTextAccessible* +nsAccessiblePivot::SearchForText(Accessible* aAccessible, bool aBackward) +{ + Accessible* root = GetActiveRoot(); + Accessible* accessible = aAccessible; + while (true) { + Accessible* child = nullptr; + + while ((child = (aBackward ? accessible->LastChild() : + accessible->FirstChild()))) { + accessible = child; + if (child->IsHyperText()) + return child->AsHyperText(); + } + + Accessible* sibling = nullptr; + Accessible* temp = accessible; + do { + if (temp == root) + break; + + if (temp != aAccessible && temp->IsHyperText()) + return temp->AsHyperText(); + + sibling = aBackward ? temp->PrevSibling() : temp->NextSibling(); + + if (sibling) + break; + } while ((temp = temp->Parent())); + + if (!sibling) + break; + + accessible = sibling; + if (accessible->IsHyperText()) + return accessible->AsHyperText(); + } + + return nullptr; +} + + bool nsAccessiblePivot::NotifyOfPivotChange(Accessible* aOldPosition, int32_t aOldStart, int32_t aOldEnd, diff --git a/accessible/src/base/nsAccessiblePivot.h b/accessible/src/base/nsAccessiblePivot.h index 0c578c72a42..e6d884df5cf 100644 --- a/accessible/src/base/nsAccessiblePivot.h +++ b/accessible/src/base/nsAccessiblePivot.h @@ -72,6 +72,12 @@ private: bool aSearchCurrent, nsresult* aResult); + /* + * Search in preorder for the first text accessible. + */ + mozilla::a11y::HyperTextAccessible* SearchForText(Accessible* aAccessible, + bool aBackward); + /* * Get the effective root for this pivot, either the true root or modal root. */ diff --git a/accessible/tests/mochitest/pivot/doc_virtualcursor_text.html b/accessible/tests/mochitest/pivot/doc_virtualcursor_text.html index afcc0b92339..aba87bbd8a7 100644 --- a/accessible/tests/mochitest/pivot/doc_virtualcursor_text.html +++ b/accessible/tests/mochitest/pivot/doc_virtualcursor_text.html @@ -5,8 +5,25 @@ +
This is the very beginning.

- This is the test of text. + This is the test of text.

+
A multiword link is here. We will traverse
+
into, out, and between the subtrees.
+

Singularity.

+ + + + + + + + + +
Magicalunicorns
and wizardsreally exist.
+
Endless fun!
+

Objectsadjacentto eachother should be separate.

+
End!
diff --git a/accessible/tests/mochitest/pivot/test_virtualcursor_text.html b/accessible/tests/mochitest/pivot/test_virtualcursor_text.html index 4ef48490c6d..de42d7da71c 100644 --- a/accessible/tests/mochitest/pivot/test_virtualcursor_text.html +++ b/accessible/tests/mochitest/pivot/test_virtualcursor_text.html @@ -38,7 +38,6 @@ gQueue.push(new setVCPosInvoker(docAcc, null, null, getAccessible(doc.getElementById('paragraph-1')))); - gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4], getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText))); gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [4,5], @@ -47,15 +46,170 @@ getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText))); gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [5,7], getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText))); - gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [8,9], - getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,3], + getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText))); gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14], getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText))); - gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [8,9], - getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,3], + getAccessible(doc.getElementById('p1-link-1'), nsIAccessibleText))); gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [5,7], getAccessible(doc.getElementById('paragraph-1'), nsIAccessibleText))); + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('section-1')))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,1], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,9], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [4,6], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [7,12], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,2], + getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [15,19], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [20,28], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,5], + getAccessible(doc.getElementById('section-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [6,10], + getAccessible(doc.getElementById('section-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,5], + getAccessible(doc.getElementById('section-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [20,28], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [15,19], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,2], + getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [7,12], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [4,6], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [10,14], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,9], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,1], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('s1-link-1')))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [1,2], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [0,1], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [1,2], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [0,1], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [1,2], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [2,9], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [10,14], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [3,4], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [13,14], + getAccessible(doc.getElementById('s1-link-1'), nsIAccessibleText))); + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('section-2')))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', CHAR_BOUNDARY, [27,28], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', CHAR_BOUNDARY, [0,1], + getAccessible(doc.getElementById('section-2'), nsIAccessibleText))); + + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('paragraph-2')))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,12], + getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7], + getAccessible(doc.getElementById('cell-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,8], + getAccessible(doc.getElementById('cell-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,3], + getAccessible(doc.getElementById('cell-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [4,11], + getAccessible(doc.getElementById('cell-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,6], + getAccessible(doc.getElementById('cell-4'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [7,13], + getAccessible(doc.getElementById('cell-4'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7], + getAccessible(doc.getElementById('section-3'), nsIAccessibleText))); + + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('section-3')))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7], + getAccessible(doc.getElementById('section-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [7,13], + getAccessible(doc.getElementById('cell-4'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,6], + getAccessible(doc.getElementById('cell-4'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [4,11], + getAccessible(doc.getElementById('cell-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,3], + getAccessible(doc.getElementById('cell-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,8], + getAccessible(doc.getElementById('cell-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,7], + getAccessible(doc.getElementById('cell-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,12], + getAccessible(doc.getElementById('paragraph-2'), nsIAccessibleText))); + + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('paragraph-3')))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,7], + getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,8], + getAccessible(doc.getElementById('p3-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [8,10], + getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4], + getAccessible(doc.getElementById('p3-link-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,5], + getAccessible(doc.getElementById('p3-link-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [14,20], + getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,5], + getAccessible(doc.getElementById('p3-link-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,4], + getAccessible(doc.getElementById('p3-link-2'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [8,10], + getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,8], + getAccessible(doc.getElementById('p3-link-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,7], + getAccessible(doc.getElementById('paragraph-3'), nsIAccessibleText))); + + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('s1-link-2')))); + // Start with the pivot in the middle of the paragraph + gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, " will traverse")); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [15,19], + getAccessible(doc.getElementById('section-1'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, [0,2], + getAccessible(doc.getElementById('s1-link-2'), nsIAccessibleText))); + + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('end-block')))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4], + getAccessible(doc.getElementById('end-block'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, null, false)); + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('start-block')))); + gQueue.push(new setVCTextInvoker(docAcc, 'moveNextByText', WORD_BOUNDARY, [0,4], + getAccessible(doc.getElementById('start-block'), nsIAccessibleText))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false)); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false)); + gQueue.push(new setVCPosInvoker(docAcc, null, null, + getAccessible(doc.getElementById('start-block')))); + gQueue.push(new setVCTextInvoker(docAcc, 'movePreviousByText', WORD_BOUNDARY, null, false)); + gQueue.invoke(); } From d07215fd65a591f6d5941c92e70fabc327152974 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 17 Sep 2013 17:06:21 -0500 Subject: [PATCH 19/56] Bug 916912 - use ImmPtr for j(void*) (r=jandem) --HG-- extra : rebase_source : 4950b437f7d0c38589312415b8e1d7fa82a01e1a --- js/src/jit/arm/Assembler-arm.h | 4 ++-- js/src/jit/arm/MacroAssembler-arm.cpp | 6 ++--- js/src/jit/arm/MacroAssembler-arm.h | 14 +++++------ .../jit/shared/CodeGenerator-x86-shared.cpp | 6 ++--- js/src/jit/x64/Assembler-x64.cpp | 6 ++--- js/src/jit/x64/Assembler-x64.h | 14 +++++------ js/src/jit/x86/Assembler-x86.h | 24 +++++++++---------- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index 08a08f2d900..e9109e234b5 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1640,8 +1640,8 @@ class Assembler static void TraceDataRelocations(JSTracer *trc, IonCode *code, CompactBufferReader &reader); protected: - void addPendingJump(BufferOffset src, void *target, Relocation::Kind kind) { - enoughMemory_ &= jumps_.append(RelativePatch(src, target, kind)); + void addPendingJump(BufferOffset src, ImmPtr target, Relocation::Kind kind) { + enoughMemory_ &= jumps_.append(RelativePatch(src, target.value, kind)); if (kind == Relocation::IONCODE) writeRelocation(src); } diff --git a/js/src/jit/arm/MacroAssembler-arm.cpp b/js/src/jit/arm/MacroAssembler-arm.cpp index be3d9db8a1c..da1ec1c3af3 100644 --- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -1692,7 +1692,7 @@ MacroAssemblerARMCompat::callWithExitFrame(IonCode *target) uint32_t descriptor = MakeFrameDescriptor(framePushed(), IonFrame_OptimizedJS); Push(Imm32(descriptor)); // descriptor - addPendingJump(m_buffer.nextOffset(), target->raw(), Relocation::IONCODE); + addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::IONCODE); RelocStyle rs; if (hasMOVWT()) rs = L_MOVWT; @@ -1710,7 +1710,7 @@ MacroAssemblerARMCompat::callWithExitFrame(IonCode *target, Register dynStack) makeFrameDescriptor(dynStack, IonFrame_OptimizedJS); Push(dynStack); // descriptor - addPendingJump(m_buffer.nextOffset(), target->raw(), Relocation::IONCODE); + addPendingJump(m_buffer.nextOffset(), ImmPtr(target->raw()), Relocation::IONCODE); RelocStyle rs; if (hasMOVWT()) rs = L_MOVWT; @@ -3756,7 +3756,7 @@ MacroAssemblerARMCompat::toggledCall(IonCode *target, bool enabled) { BufferOffset bo = nextOffset(); CodeOffsetLabel offset(bo.getOffset()); - addPendingJump(bo, target->raw(), Relocation::IONCODE); + addPendingJump(bo, ImmPtr(target->raw()), Relocation::IONCODE); ma_movPatchable(ImmPtr(target->raw()), ScratchRegister, Always, hasMOVWT() ? L_MOVWT : L_LDR); if (enabled) ma_blx(ScratchRegister); diff --git a/js/src/jit/arm/MacroAssembler-arm.h b/js/src/jit/arm/MacroAssembler-arm.h index 6e59039608c..f008266fc1f 100644 --- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -536,17 +536,17 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM // for now, assume that it'll be nearby? as_bl(label, Always); } - void call(ImmWord word) { - BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, (void*)word.value, Relocation::HARDCODED); - ma_call((void *) word.value); + void call(ImmWord imm) { + call(ImmPtr((void*)imm.value)); } void call(ImmPtr imm) { - call(ImmWord(uintptr_t(imm.value))); + BufferOffset bo = m_buffer.nextOffset(); + addPendingJump(bo, imm, Relocation::HARDCODED); + ma_call(imm.value); } void call(IonCode *c) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, c->raw(), Relocation::IONCODE); + addPendingJump(bo, ImmPtr(c->raw()), Relocation::IONCODE); RelocStyle rs; if (hasMOVWT()) rs = L_MOVWT; @@ -558,7 +558,7 @@ class MacroAssemblerARMCompat : public MacroAssemblerARM } void branch(IonCode *c) { BufferOffset bo = m_buffer.nextOffset(); - addPendingJump(bo, c->raw(), Relocation::IONCODE); + addPendingJump(bo, ImmPtr(c->raw()), Relocation::IONCODE); RelocStyle rs; if (hasMOVWT()) rs = L_MOVWT; diff --git a/js/src/jit/shared/CodeGenerator-x86-shared.cpp b/js/src/jit/shared/CodeGenerator-x86-shared.cpp index e30407f56de..bbe11a76844 100644 --- a/js/src/jit/shared/CodeGenerator-x86-shared.cpp +++ b/js/src/jit/shared/CodeGenerator-x86-shared.cpp @@ -250,7 +250,7 @@ CodeGeneratorX86Shared::generateOutOfLineCode() masm.push(Imm32(frameSize())); IonCode *handler = gen->ionRuntime()->getGenericBailoutHandler(); - masm.jmp(handler->raw(), Relocation::IONCODE); + masm.jmp(ImmPtr(handler->raw()), Relocation::IONCODE); } return true; @@ -264,7 +264,7 @@ class BailoutJump { { } #ifdef JS_CPU_X86 void operator()(MacroAssembler &masm, uint8_t *code) const { - masm.j(cond_, code, Relocation::HARDCODED); + masm.j(cond_, ImmPtr(code), Relocation::HARDCODED); } #endif void operator()(MacroAssembler &masm, Label *label) const { @@ -280,7 +280,7 @@ class BailoutLabel { { } #ifdef JS_CPU_X86 void operator()(MacroAssembler &masm, uint8_t *code) const { - masm.retarget(label_, code, Relocation::HARDCODED); + masm.retarget(label_, ImmPtr(code), Relocation::HARDCODED); } #endif void operator()(MacroAssembler &masm, Label *label) const { diff --git a/js/src/jit/x64/Assembler-x64.cpp b/js/src/jit/x64/Assembler-x64.cpp index 5b893f55c09..a4e1de620cc 100644 --- a/js/src/jit/x64/Assembler-x64.cpp +++ b/js/src/jit/x64/Assembler-x64.cpp @@ -94,15 +94,15 @@ Assembler::writeRelocation(JmpSrc src, Relocation::Kind reloc) } void -Assembler::addPendingJump(JmpSrc src, void *target, Relocation::Kind reloc) +Assembler::addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind reloc) { - JS_ASSERT(target); + JS_ASSERT(target.value != NULL); // Emit reloc before modifying the jump table, since it computes a 0-based // index. This jump is not patchable at runtime. if (reloc == Relocation::IONCODE) writeRelocation(src, reloc); - enoughMemory_ &= jumps_.append(RelativePatch(src.offset(), target, reloc)); + enoughMemory_ &= jumps_.append(RelativePatch(src.offset(), target.value, reloc)); } size_t diff --git a/js/src/jit/x64/Assembler-x64.h b/js/src/jit/x64/Assembler-x64.h index 2e7d9fa5ca5..58cb7aa4d75 100644 --- a/js/src/jit/x64/Assembler-x64.h +++ b/js/src/jit/x64/Assembler-x64.h @@ -313,7 +313,7 @@ class Assembler : public AssemblerX86Shared private: void writeRelocation(JmpSrc src, Relocation::Kind reloc); - void addPendingJump(JmpSrc src, void *target, Relocation::Kind reloc); + void addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind reloc); protected: size_t addPatchableJump(JmpSrc src, Relocation::Kind reloc); @@ -714,25 +714,25 @@ class Assembler : public AssemblerX86Shared } } - void jmp(void *target, Relocation::Kind reloc = Relocation::HARDCODED) { + void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { JmpSrc src = masm.jmp(); addPendingJump(src, target, reloc); } - void j(Condition cond, void *target, + void j(Condition cond, ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { JmpSrc src = masm.jCC(static_cast(cond)); addPendingJump(src, target, reloc); } void jmp(IonCode *target) { - jmp(target->raw(), Relocation::IONCODE); + jmp(ImmPtr(target->raw()), Relocation::IONCODE); } void j(Condition cond, IonCode *target) { - j(cond, target->raw(), Relocation::IONCODE); + j(cond, ImmPtr(target->raw()), Relocation::IONCODE); } void call(IonCode *target) { JmpSrc src = masm.call(); - addPendingJump(src, target->raw(), Relocation::IONCODE); + addPendingJump(src, ImmPtr(target->raw()), Relocation::IONCODE); } // Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch @@ -740,7 +740,7 @@ class Assembler : public AssemblerX86Shared CodeOffsetLabel toggledCall(IonCode *target, bool enabled) { CodeOffsetLabel offset(size()); JmpSrc src = enabled ? masm.call() : masm.cmp_eax(); - addPendingJump(src, target->raw(), Relocation::IONCODE); + addPendingJump(src, ImmPtr(target->raw()), Relocation::IONCODE); JS_ASSERT(size() - offset.offset() == ToggledCallSize()); return offset; } diff --git a/js/src/jit/x86/Assembler-x86.h b/js/src/jit/x86/Assembler-x86.h index 58a912f3bf5..b3f296232a6 100644 --- a/js/src/jit/x86/Assembler-x86.h +++ b/js/src/jit/x86/Assembler-x86.h @@ -237,8 +237,8 @@ class Assembler : public AssemblerX86Shared void writeRelocation(JmpSrc src) { jumpRelocations_.writeUnsigned(src.offset()); } - void addPendingJump(JmpSrc src, void *target, Relocation::Kind kind) { - enoughMemory_ &= jumps_.append(RelativePatch(src.offset(), target, kind)); + void addPendingJump(JmpSrc src, ImmPtr target, Relocation::Kind kind) { + enoughMemory_ &= jumps_.append(RelativePatch(src.offset(), target.value, kind)); if (kind == Relocation::IONCODE) writeRelocation(src); } @@ -395,32 +395,32 @@ class Assembler : public AssemblerX86Shared return masm.currentOffset(); } - void jmp(void *target, Relocation::Kind reloc = Relocation::HARDCODED) { + void jmp(ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { JmpSrc src = masm.jmp(); addPendingJump(src, target, reloc); } - void j(Condition cond, void *target, + void j(Condition cond, ImmPtr target, Relocation::Kind reloc = Relocation::HARDCODED) { JmpSrc src = masm.jCC(static_cast(cond)); addPendingJump(src, target, reloc); } void jmp(IonCode *target) { - jmp(target->raw(), Relocation::IONCODE); + jmp(ImmPtr(target->raw()), Relocation::IONCODE); } void j(Condition cond, IonCode *target) { - j(cond, target->raw(), Relocation::IONCODE); + j(cond, ImmPtr(target->raw()), Relocation::IONCODE); } void call(IonCode *target) { JmpSrc src = masm.call(); - addPendingJump(src, target->raw(), Relocation::IONCODE); + addPendingJump(src, ImmPtr(target->raw()), Relocation::IONCODE); } void call(ImmWord target) { - JmpSrc src = masm.call(); - addPendingJump(src, (void*)target.value, Relocation::HARDCODED); + call(ImmPtr((void*)target.value)); } void call(ImmPtr target) { - call(ImmWord(uintptr_t(target.value))); + JmpSrc src = masm.call(); + addPendingJump(src, target, Relocation::HARDCODED); } // Emit a CALL or CMP (nop) instruction. ToggleCall can be used to patch @@ -428,7 +428,7 @@ class Assembler : public AssemblerX86Shared CodeOffsetLabel toggledCall(IonCode *target, bool enabled) { CodeOffsetLabel offset(size()); JmpSrc src = enabled ? masm.call() : masm.cmp_eax(); - addPendingJump(src, target->raw(), Relocation::IONCODE); + addPendingJump(src, ImmPtr(target->raw()), Relocation::IONCODE); JS_ASSERT(size() - offset.offset() == ToggledCallSize()); return offset; } @@ -440,7 +440,7 @@ class Assembler : public AssemblerX86Shared // Re-routes pending jumps to an external target, flushing the label in the // process. - void retarget(Label *label, void *target, Relocation::Kind reloc) { + void retarget(Label *label, ImmPtr target, Relocation::Kind reloc) { JSC::MacroAssembler::Label jsclabel; if (label->used()) { bool more; From a32e9216f03fcfaf5059d11db7c6a6ddac0cbe48 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 26 Aug 2013 11:29:15 -0500 Subject: [PATCH 20/56] Bug 900669 - OdinMonkey: split out a static-link step (r=bbouvier) --HG-- extra : rebase_source : e910c2ba371280a13140a3faef97d379e3ce99fb --- .../tests/asm.js/testFloatingPoint.js | 4 + js/src/jit/AsmJS.cpp | 104 +++++++++++------- js/src/jit/AsmJSLink.cpp | 6 +- js/src/jit/AsmJSModule.cpp | 32 +++++- js/src/jit/AsmJSModule.h | 43 ++++++-- js/src/jit/AsmJSSignalHandlers.cpp | 12 +- js/src/jit/arm/Assembler-arm.cpp | 2 +- js/src/jit/arm/Assembler-arm.h | 13 ++- js/src/jit/shared/Assembler-x86-shared.h | 8 ++ 9 files changed, 161 insertions(+), 63 deletions(-) diff --git a/js/src/jit-test/tests/asm.js/testFloatingPoint.js b/js/src/jit-test/tests/asm.js/testFloatingPoint.js index 67a44feaa1a..a552c41292a 100644 --- a/js/src/jit-test/tests/asm.js/testFloatingPoint.js +++ b/js/src/jit-test/tests/asm.js/testFloatingPoint.js @@ -90,6 +90,10 @@ var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return +((( assertEq(f(1,0), 0); assertEq(f(-Math.pow(2,31),-1), 0); +var {f,g} = asmLink(asmCompile(USE_ASM + "function f() { return 3.5 } function g(d) { d=+d; return +(d+3.5) } return {f:f,g:g}")); +assertEq(f(), 3.5); +assertEq(g(1), 1+3.5); + var buf = new ArrayBuffer(4096); var f64 = new Float64Array(buf); var i32 = new Int32Array(buf); diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index dde5ae4a814..06327d93db9 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1674,36 +1674,41 @@ class MOZ_STACK_CLASS ModuleCompiler #endif } - bool staticallyLink(ScopedJSDeletePtr *module, ScopedJSFreePtr *report) { + bool extractModule(ScopedJSDeletePtr *module, AsmJSStaticLinkData *linkData) + { // Record the ScriptSource and [begin, end) range of the module in case // the link-time validation fails in LinkAsmJS and we need to re-parse // the entire module from scratch. uint32_t bodyEnd = parser_.tokenStream.currentToken().pos.end; module_->initSourceDesc(parser_.ss, bodyStart_, bodyEnd); - // Finish the code section. masm_.finish(); if (masm_.oom()) return false; +#if defined(JS_CPU_ARM) + // Now that compilation has finished, we need to update offsets to + // reflect actual offsets (an ARM distinction). + for (unsigned i = 0; i < module_->numHeapAccesses(); i++) { + AsmJSHeapAccess &a = module_->heapAccess(i); + a.setOffset(masm_.actualOffset(a.offset())); + } +#endif + // The returned memory is owned by module_. - uint8_t *code = module_->allocateCodeAndGlobalSegment(cx_, masm_.bytesNeeded()); - if (!code) + if (!module_->allocateAndCopyCode(cx_, masm_)) return false; - // Copy the buffer into executable memory (c.f. IonCode::copyFrom). - masm_.executableCopy(code); - masm_.processCodeLabels(code); + // c.f. IonCode::copyFrom JS_ASSERT(masm_.jumpRelocationTableBytes() == 0); JS_ASSERT(masm_.dataRelocationTableBytes() == 0); JS_ASSERT(masm_.preBarrierTableBytes() == 0); JS_ASSERT(!masm_.hasEnteredExitFrame()); - // Patch everything that needs an absolute address: - #ifdef JS_ION_PERF - // Fix up the code offsets. Note the endCodeOffset should not be filtered through - // 'actualOffset' as it is generated using 'size()' rather than a label. + // Fix up the code offsets. Note the endCodeOffset should not be + // filtered through 'actualOffset' as it is generated using 'size()' + // rather than a label. for (unsigned i = 0; i < module_->numPerfFunctions(); i++) { AsmJSModule::ProfiledFunction &func = module_->perfProfiledFunction(i); func.startCodeOffset = masm_.actualOffset(func.startCodeOffset); @@ -1722,40 +1727,58 @@ class MOZ_STACK_CLASS ModuleCompiler } #endif - // Exit points - for (unsigned i = 0; i < module_->numExits(); i++) { - module_->exitIndexToGlobalDatum(i).exit = module_->interpExitTrampoline(module_->exit(i)); - module_->exitIndexToGlobalDatum(i).fun = NULL; - } - module_->setOperationCallbackExit(code + masm_.actualOffset(operationCallbackLabel_.offset())); + // Some link information does not need to be permanently stored in the + // AsmJSModule since it is not needed after staticallyLink (which + // occurs during compilation and on cache deserialization). This link + // information is collected into AsmJSStaticLinkData which can then be + // serialized/deserialized alongside the AsmJSModule. - // Function-pointer table entries - for (unsigned i = 0; i < funcPtrTables_.length(); i++) { - FuncPtrTable &table = funcPtrTables_[i]; - uint8_t **data = module_->globalDataOffsetToFuncPtrTable(table.globalDataOffset()); - for (unsigned j = 0; j < table.numElems(); j++) - data[j] = code + masm_.actualOffset(table.elem(j).code()->offset()); + linkData->operationCallbackExitOffset = masm_.actualOffset(operationCallbackLabel_.offset()); + + // CodeLabels produced during codegen + for (size_t i = 0; i < masm_.numCodeLabels(); i++) { + CodeLabel src = masm_.codeLabel(i); + int32_t labelOffset = src.dest()->offset(); + int32_t targetOffset = masm_.actualOffset(src.src()->offset()); + // The patched uses of a label embed a linked list where the + // to-be-patched immediate is the offset of the next to-be-patched + // instruction. + while (labelOffset != LabelBase::INVALID_OFFSET) { + size_t patchAtOffset = masm_.labelOffsetToPatchOffset(labelOffset); + AsmJSStaticLinkData::RelativeLink link; + link.patchAtOffset = patchAtOffset; + link.targetOffset = targetOffset; + if (!linkData->relativeLinks.append(link)) + return false; + labelOffset = *(uintptr_t *)(module_->codeBase() + patchAtOffset); + } } - // Fix up heap/global accesses now that compilation has finished -#ifdef JS_CPU_ARM - // The AsmJSHeapAccess offsets need to be updated to reflect the - // "actualOffset" (an ARM distinction). - for (unsigned i = 0; i < module_->numHeapAccesses(); i++) { - AsmJSHeapAccess &a = module_->heapAccess(i); - a.setOffset(masm_.actualOffset(a.offset())); + // Function-pointer-table entries + for (unsigned tableIndex = 0; tableIndex < funcPtrTables_.length(); tableIndex++) { + FuncPtrTable &table = funcPtrTables_[tableIndex]; + unsigned tableBaseOffset = module_->offsetOfGlobalData() + table.globalDataOffset(); + for (unsigned elemIndex = 0; elemIndex < table.numElems(); elemIndex++) { + AsmJSStaticLinkData::RelativeLink link; + link.patchAtOffset = tableBaseOffset + elemIndex * sizeof(uint8_t*); + link.targetOffset = masm_.actualOffset(table.elem(elemIndex).code()->offset()); + if (!linkData->relativeLinks.append(link)) + return false; + } } - JS_ASSERT(globalAccesses_.length() == 0); -#else + + // Global-data-section accesses +#if defined(JS_CPU_X86) || defined(JS_CPU_X64) + uint8_t *code = module_->codeBase(); for (unsigned i = 0; i < globalAccesses_.length(); i++) { AsmJSGlobalAccess a = globalAccesses_[i]; masm_.patchAsmJSGlobalAccess(a.offset, code, module_->globalData(), a.globalDataOffset); } +#else + JS_ASSERT(globalAccesses_.empty()); #endif *module = module_.forget(); - - buildCompilationTimeReport(report); return true; } }; @@ -6371,7 +6394,7 @@ GenerateStubs(ModuleCompiler &m) static bool FinishModule(ModuleCompiler &m, ScopedJSDeletePtr *module, - ScopedJSFreePtr *compilationTimeReport) + AsmJSStaticLinkData *linkData) { LifoAlloc lifo(LIFO_ALLOC_PRIMARY_CHUNK_SIZE); TempAllocator alloc(&lifo); @@ -6380,7 +6403,7 @@ FinishModule(ModuleCompiler &m, if (!GenerateStubs(m)) return false; - return m.staticallyLink(module, compilationTimeReport); + return m.extractModule(module, linkData); } static bool @@ -6435,7 +6458,14 @@ CheckModule(ExclusiveContext *cx, AsmJSParser &parser, ParseNode *stmtList, if (tk != TOK_EOF && tk != TOK_RC) return m.fail(NULL, "top-level export (return) must be the last statement"); - return FinishModule(m, module, compilationTimeReport); + AsmJSStaticLinkData linkData(cx); + if (!FinishModule(m, module, &linkData)) + return false; + + (*module)->staticallyLink(linkData); + + m.buildCompilationTimeReport(compilationTimeReport); + return true; } static bool diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp index c895bca508c..55df8b8c33e 100644 --- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -464,7 +464,7 @@ HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, Hand static bool SendFunctionsToVTune(JSContext *cx, AsmJSModule &module) { - uint8_t *base = module.functionCode(); + uint8_t *base = module.codeBase(); for (unsigned i = 0; i < module.numProfiledFunctions(); i++) { const AsmJSModule::ProfiledFunction &func = module.profiledFunction(i); @@ -507,7 +507,7 @@ SendFunctionsToPerf(JSContext *cx, AsmJSModule &module) if (!PerfFuncEnabled()) return true; - uintptr_t base = (uintptr_t) module.functionCode(); + uintptr_t base = (uintptr_t) module.codeBase(); const char *filename = module.sourceDesc().scriptSource()->filename(); for (unsigned i = 0; i < module.numPerfFunctions(); i++) { @@ -534,7 +534,7 @@ SendBlocksToPerf(JSContext *cx, AsmJSModule &module) if (!PerfBlockEnabled()) return true; - unsigned long funcBaseAddress = (unsigned long) module.functionCode(); + unsigned long funcBaseAddress = (unsigned long) module.codeBase(); const char *filename = module.sourceDesc().scriptSource()->filename(); for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) { diff --git a/js/src/jit/AsmJSModule.cpp b/js/src/jit/AsmJSModule.cpp index 4df4c06f3e6..97a2324fe70 100644 --- a/js/src/jit/AsmJSModule.cpp +++ b/js/src/jit/AsmJSModule.cpp @@ -20,6 +20,7 @@ #include "jsobjinlines.h" using namespace js; +using namespace jit; void AsmJSModule::initHeap(Handle heap, JSContext *cx) @@ -85,15 +86,15 @@ DeallocateExecutableMemory(uint8_t *code, size_t totalBytes) #endif } -uint8_t * -AsmJSModule::allocateCodeAndGlobalSegment(ExclusiveContext *cx, size_t bytesNeeded) +bool +AsmJSModule::allocateAndCopyCode(ExclusiveContext *cx, MacroAssembler &masm) { JS_ASSERT(!code_); // The global data section sits immediately after the executable (and // other) data allocated by the MacroAssembler, so ensure it is // double-aligned. - pod.codeBytes_ = AlignBytes(bytesNeeded, sizeof(double)); + pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), sizeof(double)); // The entire region is allocated via mmap/VirtualAlloc which requires // units of pages. @@ -101,10 +102,31 @@ AsmJSModule::allocateCodeAndGlobalSegment(ExclusiveContext *cx, size_t bytesNeed code_ = AllocateExecutableMemory(cx, pod.totalBytes_); if (!code_) - return NULL; + return false; JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0); - return code_; + masm.executableCopy(code_); + return true; +} + +void +AsmJSModule::staticallyLink(const AsmJSStaticLinkData &linkData) +{ + // Process AsmJSStaticLinkData: + + operationCallbackExit_ = code_ + linkData.operationCallbackExitOffset; + + for (size_t i = 0; i < linkData.relativeLinks.length(); i++) { + AsmJSStaticLinkData::RelativeLink link = linkData.relativeLinks[i]; + *(void **)(code_ + link.patchAtOffset) = code_ + link.targetOffset; + } + + // Initialize global data segment + + for (size_t i = 0; i < exits_.length(); i++) { + exitIndexToGlobalDatum(i).exit = interpExitTrampoline(exits_[i]); + exitIndexToGlobalDatum(i).fun = NULL; + } } AsmJSModule::~AsmJSModule() diff --git a/js/src/jit/AsmJSModule.h b/js/src/jit/AsmJSModule.h index 544084174cc..ffc8d9af661 100644 --- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -38,6 +38,30 @@ enum AsmJSMathBuiltin AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul }; +// Static-link data is used to patch a module either after it has been +// compiled or deserialized with various absolute addresses (of code or +// data in the process) or relative addresses (of code or data in the same +// AsmJSModule). Since AsmJSStaticLinkData can be serialized alongside the +// AsmJSModule and isn't needed after compilation/deserialization, it +// doesn't need to be stored in the AsmJSModule. +struct AsmJSStaticLinkData +{ + struct RelativeLink + { + uint32_t patchAtOffset; + uint32_t targetOffset; + }; + + typedef Vector RelativeLinkVector; + + size_t operationCallbackExitOffset; + RelativeLinkVector relativeLinks; + + AsmJSStaticLinkData(ExclusiveContext *cx) + : relativeLinks(cx) + {} +}; + // An asm.js module represents the collection of functions nested inside a // single outer "use asm" function. For example, this asm.js module: // function() { "use asm"; function f() {} function g() {} return f } @@ -568,11 +592,13 @@ class AsmJSModule // 2. interleaved function-pointer tables and exits. These are allocated // while type checking function bodies (as exits and uses of // function-pointer tables are encountered). - uint8_t *globalData() const { + size_t offsetOfGlobalData() const { JS_ASSERT(code_); - return code_ + pod.codeBytes_; + return pod.codeBytes_; + } + uint8_t *globalData() const { + return code_ + offsetOfGlobalData(); } - size_t globalDataBytes() const { return sizeof(void*) + pod.numGlobalVars_ * sizeof(uint64_t) + @@ -614,8 +640,7 @@ class AsmJSModule return pod.functionBytes_; } bool containsPC(void *pc) const { - uint8_t *code = functionCode(); - return pc >= code && pc < (code + functionBytes()); + return pc >= code_ && pc < (code_ + functionBytes()); } bool addHeapAccesses(const jit::AsmJSHeapAccessVector &accesses) { @@ -640,17 +665,15 @@ class AsmJSModule return minHeapLength_; } - uint8_t *allocateCodeAndGlobalSegment(ExclusiveContext *cx, size_t bytesNeeded); + bool allocateAndCopyCode(ExclusiveContext *cx, jit::MacroAssembler &masm); + void staticallyLink(const AsmJSStaticLinkData &linkData); - uint8_t *functionCode() const { + uint8_t *codeBase() const { JS_ASSERT(code_); JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0); return code_; } - void setOperationCallbackExit(uint8_t *ptr) { - operationCallbackExit_ = ptr; - } uint8_t *operationCallbackExit() const { return operationCallbackExit_; } diff --git a/js/src/jit/AsmJSSignalHandlers.cpp b/js/src/jit/AsmJSSignalHandlers.cpp index fd3e50eb6f6..9ba748b6fce 100644 --- a/js/src/jit/AsmJSSignalHandlers.cpp +++ b/js/src/jit/AsmJSSignalHandlers.cpp @@ -268,7 +268,7 @@ static const AsmJSHeapAccess * LookupHeapAccess(const AsmJSModule &module, uint8_t *pc) { JS_ASSERT(module.containsPC(pc)); - size_t targetOffset = pc - module.functionCode(); + size_t targetOffset = pc - module.codeBase(); if (module.numHeapAccesses() == 0) return NULL; @@ -489,7 +489,7 @@ HandleException(PEXCEPTION_POINTERS exception) activation->setResumePC(pc); *ppc = module.operationCallbackExit(); DWORD oldProtect; - if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_EXECUTE, &oldProtect)) + if (!VirtualProtect(module.codeBase(), module.functionBytes(), PAGE_EXECUTE, &oldProtect)) MOZ_CRASH(); return true; } @@ -685,7 +685,7 @@ HandleMachException(JSRuntime *rt, const ExceptionRequest &request) if (module.containsPC(faultingAddress)) { activation->setResumePC(pc); *ppc = module.operationCallbackExit(); - mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC); + mprotect(module.codeBase(), module.functionBytes(), PROT_EXEC); // Update the thread state with the new pc. kret = thread_set_state(rtThread, x86_THREAD_STATE, (thread_state_t)&state, x86_THREAD_STATE_COUNT); @@ -919,7 +919,7 @@ HandleSignal(int signum, siginfo_t *info, void *ctx) if (module.containsPC(faultingAddress)) { activation->setResumePC(pc); *ppc = module.operationCallbackExit(); - mprotect(module.functionCode(), module.functionBytes(), PROT_EXEC); + mprotect(module.codeBase(), module.functionBytes(), PROT_EXEC); return true; } @@ -1040,10 +1040,10 @@ js::TriggerOperationCallbackForAsmJSCode(JSRuntime *rt) #if defined(XP_WIN) DWORD oldProtect; - if (!VirtualProtect(module.functionCode(), module.functionBytes(), PAGE_NOACCESS, &oldProtect)) + if (!VirtualProtect(module.codeBase(), module.functionBytes(), PAGE_NOACCESS, &oldProtect)) MOZ_CRASH(); #else // assume Unix - if (mprotect(module.functionCode(), module.functionBytes(), PROT_NONE)) + if (mprotect(module.codeBase(), module.functionBytes(), PROT_NONE)) MOZ_CRASH(); #endif } diff --git a/js/src/jit/arm/Assembler-arm.cpp b/js/src/jit/arm/Assembler-arm.cpp index c2c2beeeed1..a47f3c04abb 100644 --- a/js/src/jit/arm/Assembler-arm.cpp +++ b/js/src/jit/arm/Assembler-arm.cpp @@ -900,7 +900,7 @@ Assembler::processCodeLabels(uint8_t *rawCode) void Assembler::writeCodePointer(AbsoluteLabel *absoluteLabel) { JS_ASSERT(!absoluteLabel->bound()); - BufferOffset off = writeInst(-1); + BufferOffset off = writeInst(LabelBase::INVALID_OFFSET); // x86/x64 makes general use of AbsoluteLabel and weaves a linked list of // uses of an AbsoluteLabel through the assembly. ARM only uses labels diff --git a/js/src/jit/arm/Assembler-arm.h b/js/src/jit/arm/Assembler-arm.h index e9109e234b5..3ea43cb8d1e 100644 --- a/js/src/jit/arm/Assembler-arm.h +++ b/js/src/jit/arm/Assembler-arm.h @@ -1375,6 +1375,12 @@ class Assembler void copyPreBarrierTable(uint8_t *dest); bool addCodeLabel(CodeLabel label); + size_t numCodeLabels() const { + return codeLabels_.length(); + } + CodeLabel codeLabel(size_t i) { + return codeLabels_[i]; + } // Size of the instruction stream, in bytes. size_t size() const; @@ -1627,9 +1633,14 @@ class Assembler void retarget(Label *label, Label *target); // I'm going to pretend this doesn't exist for now. void retarget(Label *label, void *target, Relocation::Kind reloc); - // void Bind(IonCode *code, AbsoluteLabel *label, const void *address); + void Bind(uint8_t *rawCode, AbsoluteLabel *label, const void *address); + // See Bind + size_t labelOffsetToPatchOffset(size_t offset) { + return actualOffset(offset); + } + void call(Label *label); void call(void *target); diff --git a/js/src/jit/shared/Assembler-x86-shared.h b/js/src/jit/shared/Assembler-x86-shared.h index 819a75d89f1..52bd38313e9 100644 --- a/js/src/jit/shared/Assembler-x86-shared.h +++ b/js/src/jit/shared/Assembler-x86-shared.h @@ -189,6 +189,9 @@ class AssemblerX86Shared size_t numCodeLabels() const { return codeLabels_.length(); } + CodeLabel codeLabel(size_t i) { + return codeLabels_[i]; + } // Size of the instruction stream, in bytes. size_t size() const { @@ -666,6 +669,11 @@ class AssemblerX86Shared label->bind(); } + // See Bind and JSC::X86Assembler::setPointer. + size_t labelOffsetToPatchOffset(size_t offset) { + return offset - sizeof(void*); + } + void ret() { masm.ret(); } From 1da2541e2826ada43688e354c4bb5aedd3182964 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Tue, 17 Sep 2013 17:06:37 -0500 Subject: [PATCH 21/56] Bug 900669 - OdinMonkey: simplify how ScriptSource and source location are saved (r=bbouvier) --HG-- extra : rebase_source : a3cabb43976f39dfc77543dc64688357e3801478 --- js/src/jit/AsmJS.cpp | 15 +++++++------- js/src/jit/AsmJSLink.cpp | 10 +++++----- js/src/jit/AsmJSModule.cpp | 21 ++++++++++++++++++-- js/src/jit/AsmJSModule.h | 40 +++++++++++++++++++------------------- js/src/jsfriendapi.cpp | 16 --------------- js/src/jsfriendapi.h | 25 ------------------------ 6 files changed, 51 insertions(+), 76 deletions(-) diff --git a/js/src/jit/AsmJS.cpp b/js/src/jit/AsmJS.cpp index 06327d93db9..2f84e602f37 100644 --- a/js/src/jit/AsmJS.cpp +++ b/js/src/jit/AsmJS.cpp @@ -1261,7 +1261,6 @@ class MOZ_STACK_CLASS ModuleCompiler char * errorString_; uint32_t errorOffset_; - uint32_t bodyStart_; int64_t usecBefore_; SlowFunctionVector slowFunctions_; @@ -1291,7 +1290,6 @@ class MOZ_STACK_CLASS ModuleCompiler globalAccesses_(cx), errorString_(NULL), errorOffset_(UINT32_MAX), - bodyStart_(parser.tokenStream.currentToken().pos.end), usecBefore_(PRMJ_Now()), slowFunctions_(cx), finishedFunctionBodies_(false) @@ -1339,7 +1337,12 @@ class MOZ_STACK_CLASS ModuleCompiler return false; } - module_ = cx_->new_(); + // The record offset in the char buffer officially begins the char + // after the "use asm" processing directive statement (including any + // semicolon). + uint32_t charsBegin = parser_.tokenStream.currentToken().pos.end; + + module_ = cx_->new_(parser_.ss, charsBegin); if (!module_) return false; @@ -1676,11 +1679,7 @@ class MOZ_STACK_CLASS ModuleCompiler bool extractModule(ScopedJSDeletePtr *module, AsmJSStaticLinkData *linkData) { - // Record the ScriptSource and [begin, end) range of the module in case - // the link-time validation fails in LinkAsmJS and we need to re-parse - // the entire module from scratch. - uint32_t bodyEnd = parser_.tokenStream.currentToken().pos.end; - module_->initSourceDesc(parser_.ss, bodyStart_, bodyEnd); + module_->initCharsEnd(parser_.tokenStream.currentToken().pos.end); masm_.finish(); if (masm_.oom()) diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp index 55df8b8c33e..94c931ae905 100644 --- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -409,9 +409,9 @@ HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, Hand if (cx->isExceptionPending()) return false; - const AsmJSModuleSourceDesc &desc= module.sourceDesc(); - uint32_t length = desc.bufEnd() - desc.bufStart(); - Rooted src(cx, desc.scriptSource()->substring(cx, desc.bufStart(), desc.bufEnd())); + uint32_t begin = module.charsBegin(); + uint32_t end = module.charsEnd(); + Rooted src(cx, module.scriptSource()->substring(cx, begin, end)); if (!src) return false; @@ -432,11 +432,11 @@ HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, Hand CompileOptions options(cx); options.setPrincipals(cx->compartment()->principals) - .setOriginPrincipals(desc.scriptSource()->originPrincipals()) + .setOriginPrincipals(module.scriptSource()->originPrincipals()) .setCompileAndGo(false) .setNoScriptRval(false); - if (!frontend::CompileFunctionBody(cx, &fun, options, formals, src->chars().get(), length)) + if (!frontend::CompileFunctionBody(cx, &fun, options, formals, src->chars().get(), end - begin)) return false; // Call the function we just recompiled. diff --git a/js/src/jit/AsmJSModule.cpp b/js/src/jit/AsmJSModule.cpp index 97a2324fe70..e66fcde1743 100644 --- a/js/src/jit/AsmJSModule.cpp +++ b/js/src/jit/AsmJSModule.cpp @@ -80,9 +80,9 @@ static void DeallocateExecutableMemory(uint8_t *code, size_t totalBytes) { #ifdef XP_WIN - JS_ALWAYS_TRUE(VirtualFree(code, 0, MEM_RELEASE)); + JS_ALWAYS_TRUE(VirtualFree(code, 0, MEM_RELEASE)); #else - JS_ALWAYS_TRUE(munmap(code, totalBytes) == 0); + JS_ALWAYS_TRUE(munmap(code, totalBytes) == 0); #endif } @@ -129,8 +129,25 @@ AsmJSModule::staticallyLink(const AsmJSStaticLinkData &linkData) } } +AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t charsBegin) + : globalArgumentName_(NULL), + importArgumentName_(NULL), + bufferArgumentName_(NULL), + minHeapLength_(AsmJSAllocationGranularity), + code_(NULL), + operationCallbackExit_(NULL), + linked_(false), + charsBegin_(charsBegin), + scriptSource_(scriptSource) +{ + mozilla::PodZero(&pod); + scriptSource_->incref(); +} + AsmJSModule::~AsmJSModule() { + scriptSource_->decref(); + if (code_) { for (unsigned i = 0; i < numExits(); i++) { AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i); diff --git a/js/src/jit/AsmJSModule.h b/js/src/jit/AsmJSModule.h index ffc8d9af661..35ed0969315 100644 --- a/js/src/jit/AsmJSModule.h +++ b/js/src/jit/AsmJSModule.h @@ -351,6 +351,7 @@ class AsmJSModule #endif struct Pod { + uint32_t charsLength_; uint32_t numGlobalVars_; uint32_t numFFIs_; size_t funcPtrTableAndExitBytes_; @@ -366,22 +367,13 @@ class AsmJSModule bool linked_; HeapPtr maybeHeap_; - AsmJSModuleSourceDesc sourceDesc_; + uint32_t charsBegin_; + ScriptSource * scriptSource_; + FunctionCountsVector functionCounts_; public: - explicit AsmJSModule() - : globalArgumentName_(NULL), - importArgumentName_(NULL), - bufferArgumentName_(NULL), - minHeapLength_(AsmJSAllocationGranularity), - code_(NULL), - operationCallbackExit_(NULL), - linked_(false) - { - mozilla::PodZero(&pod); - } - + explicit AsmJSModule(ScriptSource *scriptSource, uint32_t charsBegin); ~AsmJSModule(); void trace(JSTracer *trc) { @@ -414,6 +406,21 @@ class AsmJSModule MarkStringUnbarriered(trc, &bufferArgumentName_, "asm.js buffer argument name"); } + ScriptSource *scriptSource() const { + JS_ASSERT(scriptSource_ != NULL); + return scriptSource_; + } + uint32_t charsBegin() const { + return charsBegin_; + } + void initCharsEnd(uint32_t charsEnd) { + JS_ASSERT(charsEnd >= charsBegin_); + pod.charsLength_ = charsEnd - charsBegin_; + } + uint32_t charsEnd() const { + return charsBegin_ + pod.charsLength_; + } + bool addGlobalVarInitConstant(const Value &v, uint32_t *globalIndex) { JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); if (pod.numGlobalVars_ == UINT32_MAX) @@ -717,13 +724,6 @@ class AsmJSModule return bufferArgumentName_; } - void initSourceDesc(ScriptSource *scriptSource, uint32_t bufStart, uint32_t bufEnd) { - sourceDesc_.init(scriptSource, bufStart, bufEnd); - } - const AsmJSModuleSourceDesc &sourceDesc() const { - return sourceDesc_; - } - void detachIonCompilation(size_t exitIndex) const { exitIndexToGlobalDatum(exitIndex).exit = interpExitTrampoline(exit(exitIndex)); } diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 46957ad9fc8..88a050a1cb8 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -1143,22 +1143,6 @@ js::IsInRequest(JSContext *cx) } #endif -void -AsmJSModuleSourceDesc::init(ScriptSource *scriptSource, uint32_t bufStart, uint32_t bufEnd) -{ - JS_ASSERT(scriptSource_ == NULL); - scriptSource_ = scriptSource; - bufStart_ = bufStart; - bufEnd_ = bufEnd; - scriptSource_->incref(); -} - -AsmJSModuleSourceDesc::~AsmJSModuleSourceDesc() -{ - if (scriptSource_) - scriptSource_->decref(); -} - #ifdef JSGC_GENERATIONAL JS_FRIEND_API(void) JS_StoreObjectPostBarrierCallback(JSContext* cx, diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 3cb557ad3ff..d2f86285d71 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1574,31 +1574,6 @@ extern JS_FRIEND_API(bool) CheckDefineProperty(JSContext *cx, JS::HandleObject obj, JS::HandleId id, JS::HandleValue value, JSPropertyOp getter, JSStrictPropertyOp setter, unsigned attrs); -class ScriptSource; - -// An AsmJSModuleSourceDesc object holds a reference to the ScriptSource -// containing an asm.js module as well as the [begin, end) range of the -// module's chars within the ScriptSource. -class AsmJSModuleSourceDesc -{ - ScriptSource *scriptSource_; - uint32_t bufStart_; - uint32_t bufEnd_; - - public: - AsmJSModuleSourceDesc() : scriptSource_(NULL), bufStart_(UINT32_MAX), bufEnd_(UINT32_MAX) {} - void init(ScriptSource *scriptSource, uint32_t bufStart, uint32_t bufEnd); - ~AsmJSModuleSourceDesc(); - - ScriptSource *scriptSource() const { JS_ASSERT(scriptSource_ != NULL); return scriptSource_; } - uint32_t bufStart() const { JS_ASSERT(bufStart_ != UINT32_MAX); return bufStart_; } - uint32_t bufEnd() const { JS_ASSERT(bufStart_ != UINT32_MAX); return bufEnd_; } - - private: - AsmJSModuleSourceDesc(const AsmJSModuleSourceDesc &) MOZ_DELETE; - void operator=(const AsmJSModuleSourceDesc &) MOZ_DELETE; -}; - } /* namespace js */ extern JS_FRIEND_API(bool) From 8ace21131e040dec456ea9f4cb4add13f44ccb34 Mon Sep 17 00:00:00 2001 From: Joel Maher Date: Wed, 18 Sep 2013 09:37:09 -0400 Subject: [PATCH 22/56] Bug 914799 - create mobile_talos.json. r=kmoir --- testing/talos/talos.json | 42 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/testing/talos/talos.json b/testing/talos/talos.json index e5c3bcce39c..0b7488b249b 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -1,11 +1,11 @@ { "talos.zip": { - "url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.ca2229a32cb6.zip", + "url": "http://talos-bundles.pvt.build.mozilla.org/zips/talos.e11c30b59cef.zip", "path": "" }, "global": { "talos_repo": "http://hg.mozilla.org/build/talos", - "talos_revision": "655c5140970c" + "talos_revision": "e11c30b59cef" }, "suites": { "chromez": { @@ -52,5 +52,43 @@ "C:/slave/talos-data/talos/xperf.config" ] } + }, + "mobile-suites": { + "remote-ts": { + "tests": ["ts_paint"] + }, + "remote-tsvgx": { + "tests": ["tsvgx"], + "talos_options": [ + "--noChrome" + ] + }, + "remote-tcanvasmark": { + "tests": ["tcanvasmark"], + "talos_options": [ + "--noChrome" + ] + }, + "remote-trobopan": { + "tests": ["trobopan"], + "talos_options": [ + "--fennecIDs", "../fennec_ids.txt" + ] + }, + "remote-troboprovider": { + "tests": ["tprovider"], + "talos_options": [ + "--fennecIDs", "../fennec_ids.txt" + ] + }, + "remote-trobocheck2": { + "tests": ["tcheck2"], + "talos_options": [ + "--fennecIDs", "../fennec_ids.txt" + ] + }, + "remote-tp4m_nochrome": { + "tests": ["tp4m"] + } } } From fce6596a8835d2c04407c0aa3fa201a95126713b Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Wed, 18 Sep 2013 07:55:29 -0600 Subject: [PATCH 23/56] Bug 917590 - Remove most roots and handles in IonBuilder, r=jandem. --- js/src/jit/IonBuilder.cpp | 253 +++++++++++++++++------------------ js/src/jit/IonBuilder.h | 56 ++++---- js/src/jit/MCallOptimize.cpp | 26 ++-- js/src/jit/MIR.h | 50 +++---- 4 files changed, 189 insertions(+), 196 deletions(-) diff --git a/js/src/jit/IonBuilder.cpp b/js/src/jit/IonBuilder.cpp index cc8afb64a63..6eaaa32ce6c 100644 --- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -229,7 +229,7 @@ IonBuilder::canEnterInlinedFunction(JSFunction *target) if (target->isHeavyweight()) return false; - RootedScript targetScript(cx, target->nonLazyScript()); + JSScript *targetScript = target->nonLazyScript(); if (!targetScript->ensureRanAnalysis(cx)) return false; @@ -273,7 +273,7 @@ IonBuilder::canInlineTarget(JSFunction *target, bool constructing) return false; } - RootedScript inlineScript(cx, target->nonLazyScript()); + JSScript *inlineScript = target->nonLazyScript(); ExecutionMode executionMode = info().executionMode(); if (!CanIonCompile(inlineScript, executionMode)) { IonSpew(IonSpew_Inlining, "%s:%d Cannot inline due to disable Ion compilation", @@ -1438,21 +1438,15 @@ IonBuilder::inspectOpcode(JSOp op) return true; case JSOP_NEWINIT: - { if (GET_UINT8(pc) == JSProto_Array) return jsop_newarray(0); - RootedObject baseObj(cx, NULL); - return jsop_newobject(baseObj); - } + return jsop_newobject(NULL); case JSOP_NEWARRAY: return jsop_newarray(GET_UINT24(pc)); case JSOP_NEWOBJECT: - { - RootedObject baseObj(cx, info().getObject(pc)); - return jsop_newobject(baseObj); - } + return jsop_newobject(info().getObject(pc)); case JSOP_INITELEM: return jsop_initelem(); @@ -1462,7 +1456,7 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_INITPROP: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); return jsop_initprop(name); } @@ -1501,8 +1495,8 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_GETGNAME: case JSOP_CALLGNAME: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); - RootedObject obj(cx, &script()->global()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); + JSObject *obj = &script()->global(); bool succeeded; if (!getStaticName(obj, name, &succeeded)) return false; @@ -1514,22 +1508,22 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_SETGNAME: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); - RootedObject obj(cx, &script()->global()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); + JSObject *obj = &script()->global(); return setStaticName(obj, name); } case JSOP_NAME: case JSOP_CALLNAME: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); return jsop_getname(name); } case JSOP_GETINTRINSIC: case JSOP_CALLINTRINSIC: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); return jsop_intrinsic(name); } @@ -1601,20 +1595,20 @@ IonBuilder::inspectOpcode(JSOp op) case JSOP_GETPROP: case JSOP_CALLPROP: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); return jsop_getprop(name); } case JSOP_SETPROP: case JSOP_SETNAME: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); return jsop_setprop(name); } case JSOP_DELPROP: { - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); return jsop_delprop(name); } @@ -3732,8 +3726,7 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) // Create new |this| on the caller-side for inlined constructors. if (callInfo.constructing()) { - RootedFunction targetRoot(cx, target); - MDefinition *thisDefn = createThis(targetRoot, callInfo.fun()); + MDefinition *thisDefn = createThis(target, callInfo.fun()); if (!thisDefn) return false; callInfo.setThis(thisDefn); @@ -3751,8 +3744,8 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) callInfo.popFormals(current); current->push(callInfo.fun()); - RootedScript calleeScript(cx, target->nonLazyScript()); - BaselineInspector inspector(cx, target->nonLazyScript()); + JSScript *calleeScript = target->nonLazyScript(); + BaselineInspector inspector(cx, calleeScript); // Improve type information of |this| when not set. if (callInfo.constructing() && !callInfo.thisArg()->resultTypeSet()) { @@ -3766,7 +3759,7 @@ IonBuilder::inlineScriptedCall(CallInfo &callInfo, JSFunction *target) // Start inlining. LifoAlloc *alloc = GetIonContext()->temp->lifoAlloc(); - CompileInfo *info = alloc->new_(calleeScript.get(), target, + CompileInfo *info = alloc->new_(calleeScript, target, (jsbytecode *)NULL, callInfo.constructing(), this->info().executionMode()); if (!info) @@ -4160,8 +4153,7 @@ IonBuilder::inlineGenericFallback(JSFunction *target, CallInfo &callInfo, MBasic // Generate an MCall, which uses stateful |current|. setCurrentAndSpecializePhis(fallbackBlock); - RootedFunction targetRooted(cx, target); - if (!makeCall(targetRooted, fallbackInfo, clonedAtCallsite)) + if (!makeCall(target, fallbackInfo, clonedAtCallsite)) return false; // Pass return block to caller as |current|. @@ -4502,7 +4494,6 @@ IonBuilder::createDeclEnvObject(MDefinition *callee, MDefinition *scope) // compilation, it is used by the background compilation thread and thus // cannot use the Nursery. - RootedScript script(cx, script_); RootedFunction fun(cx, info().fun()); RootedObject templateObj(cx, DeclEnvObject::createTemplateObject(cx, fun, gc::TenuredHeap)); if (!templateObj) @@ -4634,10 +4625,10 @@ IonBuilder::getSingletonPrototype(JSFunction *target) } MDefinition * -IonBuilder::createThisScriptedSingleton(HandleFunction target, MDefinition *callee) +IonBuilder::createThisScriptedSingleton(JSFunction *target, MDefinition *callee) { // Get the singleton prototype (if exists) - RootedObject proto(cx, getSingletonPrototype(target)); + JSObject *proto = getSingletonPrototype(target); if (!proto) return NULL; @@ -4646,13 +4637,14 @@ IonBuilder::createThisScriptedSingleton(HandleFunction target, MDefinition *call // Generate an inline path to create a new |this| object with // the given singleton prototype. - types::TypeObject *type = cx->getNewType(&JSObject::class_, proto.get(), target); + types::TypeObject *type = cx->getNewType(&JSObject::class_, proto, target); if (!type) return NULL; if (!types::TypeScript::ThisTypes(target->nonLazyScript())->hasType(types::Type::ObjectType(type))) return NULL; - RootedObject templateObject(cx, CreateThisForFunctionWithProto(cx, target, proto, TenuredObject)); + RootedObject targetRoot(cx, target); + JSObject *templateObject = CreateThisForFunctionWithProto(cx, targetRoot, proto, TenuredObject); if (!templateObject) return NULL; @@ -4667,7 +4659,7 @@ IonBuilder::createThisScriptedSingleton(HandleFunction target, MDefinition *call } MDefinition * -IonBuilder::createThis(HandleFunction target, MDefinition *callee) +IonBuilder::createThis(JSFunction *target, MDefinition *callee) { // Create this for unknown target if (!target) { @@ -4709,7 +4701,7 @@ IonBuilder::jsop_funcall(uint32_t argc) // If |Function.prototype.call| may be overridden, don't optimize callsite. types::TemporaryTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet(); - RootedFunction native(cx, getSingleCallTarget(calleeTypes)); + JSFunction *native = getSingleCallTarget(calleeTypes); if (!native || !native->isNative() || native->native() != &js_fun_call) { CallInfo callInfo(cx, false); if (!callInfo.init(current, argc)) @@ -4720,7 +4712,7 @@ IonBuilder::jsop_funcall(uint32_t argc) // Extract call target. types::TemporaryTypeSet *funTypes = current->peek(funcDepth)->resultTypeSet(); - RootedFunction target(cx, getSingleCallTarget(funTypes)); + JSFunction *target = getSingleCallTarget(funTypes); // Unwrap the (JSFunction *) parameter. MPassArg *passFunc = current->peek(funcDepth)->toPassArg(); @@ -4764,7 +4756,7 @@ IonBuilder::jsop_funapply(uint32_t argc) int calleeDepth = -((int)argc + 2); types::TemporaryTypeSet *calleeTypes = current->peek(calleeDepth)->resultTypeSet(); - RootedFunction native(cx, getSingleCallTarget(calleeTypes)); + JSFunction *native = getSingleCallTarget(calleeTypes); if (argc != 2) { CallInfo callInfo(cx, false); if (!callInfo.init(current, argc)) @@ -4816,7 +4808,7 @@ IonBuilder::jsop_funapplyarguments(uint32_t argc) // Extract call target. types::TemporaryTypeSet *funTypes = current->peek(funcDepth)->resultTypeSet(); - RootedFunction target(cx, getSingleCallTarget(funTypes)); + JSFunction *target = getSingleCallTarget(funTypes); // When this script isn't inlined, use MApplyArgs, // to copy the arguments from the stack and call the function @@ -4963,7 +4955,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing) return false; // No inline, just make the call. - RootedFunction target(cx, NULL); + JSFunction *target = NULL; if (targets.length() == 1) target = &targets[0]->as(); @@ -4971,7 +4963,7 @@ IonBuilder::jsop_call(uint32_t argc, bool constructing) } MDefinition * -IonBuilder::makeCallsiteClone(HandleFunction target, MDefinition *fun) +IonBuilder::makeCallsiteClone(JSFunction *target, MDefinition *fun) { // Bake in the clone eagerly if we have a known target. We have arrived here // because TI told us that the known target is a should-clone-at-callsite @@ -4993,7 +4985,7 @@ IonBuilder::makeCallsiteClone(HandleFunction target, MDefinition *fun) } static bool -TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func, +TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, JSFunction *func, JSJitInfo::OpType opType) { if (!func->isNative() || !func->jitInfo()) @@ -5022,9 +5014,8 @@ TestShouldDOMCall(JSContext *cx, types::TypeSet *inTypes, HandleFunction func, return false; } - JSObject *typeProto = curType->proto; - RootedObject proto(cx, typeProto); - if (!instanceChecker(proto, jinfo->protoID, jinfo->depth)) + RootedObject protoRoot(cx, curType->proto); + if (!instanceChecker(protoRoot, jinfo->protoID, jinfo->depth)) return false; } @@ -5087,7 +5078,7 @@ ArgumentTypesMatch(MDefinition *def, types::TemporaryTypeSet *calleeTypes) } bool -IonBuilder::testNeedsArgumentCheck(JSContext *cx, HandleFunction target, CallInfo &callInfo) +IonBuilder::testNeedsArgumentCheck(JSContext *cx, JSFunction *target, CallInfo &callInfo) { // If we have a known target, check if the caller arg types are a subset of callee. // Since typeset accumulates and can't decrease that means we don't need to check @@ -5095,7 +5086,7 @@ IonBuilder::testNeedsArgumentCheck(JSContext *cx, HandleFunction target, CallInf if (!target->isInterpreted()) return true; - RootedScript targetScript(cx, target->nonLazyScript()); + JSScript *targetScript = target->nonLazyScript(); if (!targetScript->hasAnalysis()) return true; @@ -5115,7 +5106,7 @@ IonBuilder::testNeedsArgumentCheck(JSContext *cx, HandleFunction target, CallInf } MCall * -IonBuilder::makeCallHelper(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite) +IonBuilder::makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite) { JS_ASSERT(callInfo.isWrapped()); @@ -5236,7 +5227,7 @@ DOMCallNeedsBarrier(const JSJitInfo* jitinfo, types::TemporaryTypeSet *types) } bool -IonBuilder::makeCall(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite) +IonBuilder::makeCall(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite) { // Constructor calls to non-constructors should throw. We don't want to use // CallKnown in this case. @@ -5274,7 +5265,7 @@ IonBuilder::jsop_eval(uint32_t argc) if (calleeTypes && calleeTypes->empty()) return jsop_call(argc, /* constructing = */ false); - RootedFunction singleton(cx, getSingleCallTarget(calleeTypes)); + JSFunction *singleton = getSingleCallTarget(calleeTypes); if (!singleton) return abort("No singleton callee for eval()"); @@ -5335,7 +5326,7 @@ IonBuilder::jsop_eval(uint32_t argc) if (!evalCallInfo.init(current, /* argc = */ 0)) return false; - return makeCall(NullPtr(), evalCallInfo, false); + return makeCall(NULL, evalCallInfo, false); } } @@ -5373,19 +5364,18 @@ IonBuilder::jsop_compare(JSOp op) JSObject * IonBuilder::getNewArrayTemplateObject(uint32_t count) { - RootedScript scriptRoot(cx, script()); - NewObjectKind newKind = types::UseNewTypeForInitializer(cx, scriptRoot, pc, JSProto_Array); + NewObjectKind newKind = types::UseNewTypeForInitializer(cx, script(), pc, JSProto_Array); // Do not allocate template objects in the nursery. if (newKind == GenericObject) newKind = TenuredObject; - RootedObject templateObject(cx, NewDenseUnallocatedArray(cx, count, NULL, newKind)); + JSObject *templateObject = NewDenseUnallocatedArray(cx, count, NULL, newKind); if (!templateObject) return NULL; if (newKind != SingletonObject) { - types::TypeObject *type = types::TypeScript::InitObject(cx, scriptRoot, pc, JSProto_Array); + types::TypeObject *type = types::TypeScript::InitObject(cx, script(), pc, JSProto_Array); if (!type) return NULL; templateObject->setType(type); @@ -5423,22 +5413,21 @@ IonBuilder::jsop_newarray(uint32_t count) } bool -IonBuilder::jsop_newobject(HandleObject baseObj) +IonBuilder::jsop_newobject(JSObject *baseObj) { // Don't bake in the TypeObject for non-CNG scripts. JS_ASSERT(script()->compileAndGo); - RootedObject templateObject(cx); - - RootedScript scriptRoot(cx, script()); - NewObjectKind newKind = types::UseNewTypeForInitializer(cx, scriptRoot, pc, JSProto_Object); + NewObjectKind newKind = types::UseNewTypeForInitializer(cx, script(), pc, JSProto_Object); // Do not allocate template objects in the nursery. if (newKind == GenericObject) newKind = TenuredObject; + JSObject *templateObject; if (baseObj) { - templateObject = CopyInitializerObject(cx, baseObj, newKind); + RootedObject baseObjRoot(cx, baseObj); + templateObject = CopyInitializerObject(cx, baseObjRoot, newKind); } else { gc::AllocKind allocKind = GuessObjectGCKind(0); templateObject = NewBuiltinClassInstance(cx, &JSObject::class_, allocKind, newKind); @@ -5448,7 +5437,7 @@ IonBuilder::jsop_newobject(HandleObject baseObj) return false; if (newKind != SingletonObject) { - types::TypeObject *type = types::TypeScript::InitObject(cx, scriptRoot, pc, JSProto_Object); + types::TypeObject *type = types::TypeScript::InitObject(cx, script(), pc, JSProto_Object); if (!type) return false; templateObject->setType(type); @@ -5555,19 +5544,19 @@ CanEffectlesslyCallLookupGenericOnObject(JSContext *cx, JSObject *obj, jsid id) } bool -IonBuilder::jsop_initprop(HandlePropertyName name) +IonBuilder::jsop_initprop(PropertyName *name) { MDefinition *value = current->pop(); MDefinition *obj = current->peek(-1); RootedObject templateObject(cx, obj->toNewObject()->templateObject()); - RootedObject holder(cx); - RootedShape shape(cx); RootedId id(cx, NameToId(name)); if (!CanEffectlesslyCallLookupGenericOnObject(cx, templateObject, id)) return abort("INITPROP template object is special"); + RootedObject holder(cx); + RootedShape shape(cx); bool res = LookupPropertyWithFlags(cx, templateObject, id, 0, &holder, &shape); if (!res) @@ -5975,8 +5964,8 @@ IonBuilder::maybeInsertResume() } static inline bool -TestSingletonProperty(JSContext *cx, HandleObject obj, JSObject *singleton, - HandleId id, bool *isKnownConstant) +TestSingletonProperty(JSContext *cx, JSObject *obj, JSObject *singleton, + jsid id, bool *isKnownConstant) { // We would like to completely no-op property/global accesses which can // produce only a particular JSObject. When indicating the access result is @@ -5999,9 +5988,12 @@ TestSingletonProperty(JSContext *cx, HandleObject obj, JSObject *singleton, if (!CanEffectlesslyCallLookupGenericOnObject(cx, obj, id)) return true; + RootedObject objRoot(cx, obj); + RootedId idRoot(cx, id); + RootedObject holder(cx); RootedShape shape(cx); - if (!JSObject::lookupGeneric(cx, obj, id, &holder, &shape)) + if (!JSObject::lookupGeneric(cx, objRoot, idRoot, &holder, &shape)) return false; if (!shape) return true; @@ -6032,7 +6024,7 @@ TestSingletonProperty(JSContext *cx, HandleObject obj, JSObject *singleton, static inline bool TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, - HandleObject globalObj, HandleId id, + JSObject *globalObj, jsid id, bool *isKnownConstant, bool *testObject, bool *testString) { @@ -6055,7 +6047,7 @@ TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, if (id != types::IdToTypeId(id)) return true; - RootedObject objectSingleton(cx, types ? types->getSingleton() : NULL); + JSObject *objectSingleton = types ? types->getSingleton() : NULL; if (objectSingleton) return TestSingletonProperty(cx, objectSingleton, singleton, id, isKnownConstant); @@ -6116,9 +6108,8 @@ TestSingletonPropertyTypes(JSContext *cx, MDefinition *obj, JSObject *singleton, if (object->proto) { // Test this type. - RootedObject proto(cx, object->proto); bool thoughtConstant = false; - if (!TestSingletonProperty(cx, proto, singleton, id, &thoughtConstant)) + if (!TestSingletonProperty(cx, object->proto, singleton, id, &thoughtConstant)) return false; if (!thoughtConstant) return true; @@ -6219,8 +6210,10 @@ IonBuilder::pushTypeBarrier(MInstruction *ins, types::TemporaryTypeSet *observed } bool -IonBuilder::getStaticName(HandleObject staticObject, HandlePropertyName name, bool *psucceeded) +IonBuilder::getStaticName(JSObject *staticObject, PropertyName *name, bool *psucceeded) { + jsid id = NameToId(name); + JS_ASSERT(staticObject->is() || staticObject->is()); *psucceeded = true; @@ -6235,11 +6228,9 @@ IonBuilder::getStaticName(HandleObject staticObject, HandlePropertyName name, bo return pushConstant(cx->runtime()->positiveInfinityValue); } - RootedId id(cx, NameToId(name)); - // For the fastest path, the property must be found, and it must be found // as a normal data property on exactly the global object. - RootedShape shape(cx, staticObject->nativeLookup(cx, id)); + Shape *shape = staticObject->nativeLookup(cx, id); if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot()) { *psucceeded = false; return true; @@ -6339,9 +6330,9 @@ jit::NeedsPostBarrier(CompileInfo &info, MDefinition *value) } bool -IonBuilder::setStaticName(HandleObject staticObject, HandlePropertyName name) +IonBuilder::setStaticName(JSObject *staticObject, PropertyName *name) { - RootedId id(cx, NameToId(name)); + jsid id = NameToId(name); JS_ASSERT(staticObject->is() || staticObject->is()); @@ -6352,7 +6343,7 @@ IonBuilder::setStaticName(HandleObject staticObject, HandlePropertyName name) // For the fastest path, the property must be found, and it must be found // as a normal data property on exactly the global object. - RootedShape shape(cx, staticObject->nativeLookup(cx, id)); + Shape *shape = staticObject->nativeLookup(cx, id); if (!shape || !shape->hasDefaultSetter() || !shape->writable() || !shape->hasSlot()) return jsop_setprop(name); @@ -6413,7 +6404,7 @@ IonBuilder::setStaticName(HandleObject staticObject, HandlePropertyName name) } bool -IonBuilder::jsop_getname(HandlePropertyName name) +IonBuilder::jsop_getname(PropertyName *name) { MDefinition *object; if (js_CodeSpec[*pc].format & JOF_GNAME) { @@ -6442,7 +6433,7 @@ IonBuilder::jsop_getname(HandlePropertyName name) } bool -IonBuilder::jsop_intrinsic(HandlePropertyName name) +IonBuilder::jsop_intrinsic(PropertyName *name) { types::TemporaryTypeSet *types = bytecodeTypes(pc); JSValueType type = types->getKnownTypeTag(); @@ -6462,8 +6453,9 @@ IonBuilder::jsop_intrinsic(HandlePropertyName name) } // Bake in the intrinsic. Make sure that TI agrees with us on the type. + RootedPropertyName nameRoot(cx, name); RootedValue vp(cx, UndefinedValue()); - if (!cx->global()->getIntrinsicValue(cx, name, &vp)) + if (!cx->global()->getIntrinsicValue(cx, nameRoot, &vp)) return false; JS_ASSERT(types->hasType(types::GetValueType(vp))); @@ -7485,7 +7477,7 @@ IonBuilder::jsop_length() if (jsop_length_fastPath()) return true; - RootedPropertyName name(cx, info().getAtom(pc)->asPropertyName()); + PropertyName *name = info().getAtom(pc)->asPropertyName(); return jsop_getprop(name); } @@ -7693,7 +7685,7 @@ TestClassHasAccessorHook(const Class *clasp, bool isGetter) } inline bool -TestTypeHasOwnProperty(JSContext *cx, types::TypeObject *typeObj, HandleId id, bool &cont) +TestTypeHasOwnProperty(JSContext *cx, types::TypeObject *typeObj, jsid id, bool &cont) { cont = true; types::HeapTypeSet *propSet = typeObj->getProperty(cx, types::IdToTypeId(id), false); @@ -7705,11 +7697,11 @@ TestTypeHasOwnProperty(JSContext *cx, types::TypeObject *typeObj, HandleId id, b } inline bool -TestCommonAccessorProtoChain(JSContext *cx, HandleId id, bool isGetter, JSObject *foundProto, +TestCommonAccessorProtoChain(JSContext *cx, jsid id, bool isGetter, JSObject *foundProto, JSObject *obj, bool &cont) { cont = false; - RootedObject curObj(cx, obj); + JSObject *curObj = obj; JSObject *stopAt = foundProto->getProto(); while (curObj != stopAt) { // Don't optimize if we have a hook that would have to be called. @@ -7753,7 +7745,7 @@ TestCommonAccessorProtoChain(JSContext *cx, HandleId id, bool isGetter, JSObject } inline bool -SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, HandleId id, bool isGetter, +SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, bool isGetter, JSObject *&found, JSObject *&foundProto, bool &cont) { cont = false; @@ -7798,9 +7790,10 @@ SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, HandleId id, if (!CanEffectlesslyCallLookupGenericOnObject(cx, curObj, id)) return true; + RootedId idRoot(cx, id); RootedObject proto(cx); RootedShape shape(cx); - if (!JSObject::lookupGeneric(cx, curObj, id, &proto, &shape)) + if (!JSObject::lookupGeneric(cx, curObj, idRoot, &proto, &shape)) return false; if (!shape) @@ -7847,7 +7840,7 @@ SearchCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, HandleId id, } inline bool -FreezePropTypeSets(JSContext *cx, types::TemporaryTypeSet *types, JSObject *foundProto, HandleId id) +FreezePropTypeSets(JSContext *cx, types::TemporaryTypeSet *types, JSObject *foundProto, jsid id) { types::TypeObject *curType; for (unsigned i = 0; i < types->getObjectCount(); i++) { @@ -7892,7 +7885,7 @@ FreezePropTypeSets(JSContext *cx, types::TemporaryTypeSet *types, JSObject *foun } inline bool -IonBuilder::TestCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, HandleId id, +IonBuilder::testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, jsid id, JSFunction **funcp, bool isGetter, bool *isDOM, MDefinition **guardOut) { @@ -7949,7 +7942,7 @@ bool IonBuilder::annotateGetPropertyCache(JSContext *cx, MDefinition *obj, MGetPropertyCache *getPropCache, types::TemporaryTypeSet *objTypes, types::TemporaryTypeSet *pushedTypes) { - RootedId id(cx, NameToId(getPropCache->name())); + jsid id = NameToId(getPropCache->name()); if (id != types::IdToTypeId(id)) return true; @@ -8116,9 +8109,9 @@ IonBuilder::storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool n } bool -IonBuilder::jsop_getprop(HandlePropertyName name) +IonBuilder::jsop_getprop(PropertyName *name) { - RootedId id(cx, NameToId(name)); + jsid id = NameToId(name); bool emitted = false; @@ -8191,14 +8184,14 @@ IonBuilder::getPropTryArgumentsLength(bool *emitted) } bool -IonBuilder::getPropTryConstant(bool *emitted, HandleId id, types::TemporaryTypeSet *types) +IonBuilder::getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); JSObject *singleton = types ? types->getSingleton() : NULL; if (!singleton) return true; - RootedObject global(cx, &script()->global()); + JSObject *global = &script()->global(); bool isConstant, testObject, testString; if (!TestSingletonPropertyTypes(cx, current->peek(-1), singleton, global, id, @@ -8229,7 +8222,7 @@ IonBuilder::getPropTryConstant(bool *emitted, HandleId id, types::TemporaryTypeS } bool -IonBuilder::getPropTryDefiniteSlot(bool *emitted, HandlePropertyName name, +IonBuilder::getPropTryDefiniteSlot(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); @@ -8260,7 +8253,7 @@ IonBuilder::getPropTryDefiniteSlot(bool *emitted, HandlePropertyName name, } bool -IonBuilder::getPropTryCommonGetter(bool *emitted, HandleId id, +IonBuilder::getPropTryCommonGetter(bool *emitted, jsid id, bool barrier, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); @@ -8270,16 +8263,15 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, HandleId id, types::TemporaryTypeSet *objTypes = current->peek(-1)->resultTypeSet(); - if (!TestCommonPropFunc(cx, objTypes, id, &commonGetter, true, &isDOM, &guard)) + if (!testCommonPropFunc(cx, objTypes, id, &commonGetter, true, &isDOM, &guard)) return false; if (!commonGetter) return true; MDefinition *obj = current->pop(); - RootedFunction getter(cx, commonGetter); - if (isDOM && TestShouldDOMCall(cx, objTypes, getter, JSJitInfo::Getter)) { - const JSJitInfo *jitinfo = getter->jitInfo(); + if (isDOM && TestShouldDOMCall(cx, objTypes, commonGetter, JSJitInfo::Getter)) { + const JSJitInfo *jitinfo = commonGetter->jitInfo(); MGetDOMProperty *get = MGetDOMProperty::New(jitinfo, obj, guard); current->add(get); current->push(get); @@ -8314,11 +8306,11 @@ IonBuilder::getPropTryCommonGetter(bool *emitted, HandleId id, return false; // Inline if we can, otherwise, forget it and just generate a call. - if (makeInliningDecision(getter, callInfo) && getter->isInterpreted()) { - if (!inlineScriptedCall(callInfo, getter)) + if (makeInliningDecision(commonGetter, callInfo) && commonGetter->isInterpreted()) { + if (!inlineScriptedCall(callInfo, commonGetter)) return false; } else { - if (!makeCall(getter, callInfo, false)) + if (!makeCall(commonGetter, callInfo, false)) return false; } @@ -8342,7 +8334,7 @@ CanInlinePropertyOpShapes(const Vector &shapes) } bool -IonBuilder::getPropTryInlineAccess(bool *emitted, HandlePropertyName name, HandleId id, +IonBuilder::getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, bool barrier, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); @@ -8403,7 +8395,7 @@ IonBuilder::getPropTryInlineAccess(bool *emitted, HandlePropertyName name, Handl } bool -IonBuilder::getPropTryCache(bool *emitted, HandlePropertyName name, HandleId id, +IonBuilder::getPropTryCache(bool *emitted, PropertyName *name, jsid id, bool barrier, types::TemporaryTypeSet *types) { JS_ASSERT(*emitted == false); @@ -8488,12 +8480,12 @@ IonBuilder::needsToMonitorMissingProperties(types::TemporaryTypeSet *types) } bool -IonBuilder::jsop_setprop(HandlePropertyName name) +IonBuilder::jsop_setprop(PropertyName *name) { MDefinition *value = current->pop(); MDefinition *obj = current->pop(); - RootedId id(cx, NameToId(name)); + jsid id = NameToId(name); bool emitted = false; // Always use a call if we are doing the definite properties analysis and @@ -8542,7 +8534,7 @@ IonBuilder::jsop_setprop(HandlePropertyName name) bool IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, - HandlePropertyName name, HandleId id, + PropertyName *name, jsid id, MDefinition *value) { JS_ASSERT(*emitted == false); @@ -8551,7 +8543,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, bool isDOM; types::TemporaryTypeSet *objTypes = obj->resultTypeSet(); - if (!TestCommonPropFunc(cx, objTypes, id, &commonSetter, false, &isDOM, NULL)) + if (!testCommonPropFunc(cx, objTypes, id, &commonSetter, false, &isDOM, NULL)) return false; if (!commonSetter) @@ -8562,10 +8554,9 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, // Setters can be called even if the property write needs a type // barrier, as calling the setter does not actually write any data // properties. - RootedFunction setter(cx, commonSetter); // Try emitting dom call. - if (!setPropTryCommonDOMSetter(emitted, obj, value, setter, isDOM)) + if (!setPropTryCommonDOMSetter(emitted, obj, value, commonSetter, isDOM)) return false; if (*emitted) @@ -8586,7 +8577,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, return false; } - pushConstant(ObjectValue(*setter)); + pushConstant(ObjectValue(*commonSetter)); MPassArg *wrapper = MPassArg::New(obj); current->push(wrapper); @@ -8606,15 +8597,15 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, callInfo.markAsSetter(); // Inline the setter if we can. - if (makeInliningDecision(setter, callInfo) && setter->isInterpreted()) { - if (!inlineScriptedCall(callInfo, setter)) + if (makeInliningDecision(commonSetter, callInfo) && commonSetter->isInterpreted()) { + if (!inlineScriptedCall(callInfo, commonSetter)) return false; *emitted = true; return true; } - MCall *call = makeCallHelper(setter, callInfo, false); + MCall *call = makeCallHelper(commonSetter, callInfo, false); if (!call) return false; @@ -8628,7 +8619,7 @@ IonBuilder::setPropTryCommonSetter(bool *emitted, MDefinition *obj, bool IonBuilder::setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj, - MDefinition *value, HandleFunction setter, + MDefinition *value, JSFunction *setter, bool isDOM) { JS_ASSERT(*emitted == false); @@ -8656,7 +8647,7 @@ IonBuilder::setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj, bool IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, - HandlePropertyName name, MDefinition *value, + PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes) { JS_ASSERT(*emitted == false); @@ -8684,7 +8675,7 @@ IonBuilder::setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, bool IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, - HandlePropertyName name, HandleId id, + PropertyName *name, jsid id, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes) { @@ -8748,7 +8739,7 @@ IonBuilder::setPropTryInlineAccess(bool *emitted, MDefinition *obj, bool IonBuilder::setPropTryCache(bool *emitted, MDefinition *obj, - HandlePropertyName name, MDefinition *value, + PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes) { JS_ASSERT(*emitted == false); @@ -8756,7 +8747,7 @@ IonBuilder::setPropTryCache(bool *emitted, MDefinition *obj, // Emit SetPropertyCache. MSetPropertyCache *ins = MSetPropertyCache::New(obj, value, name, script()->strict, barrier); - RootedId id(cx, NameToId(name)); + jsid id = NameToId(name); if (!objTypes || objTypes->propertyNeedsBarrier(cx, id)) ins->setNeedsBarrier(); @@ -8771,7 +8762,7 @@ IonBuilder::setPropTryCache(bool *emitted, MDefinition *obj, } bool -IonBuilder::jsop_delprop(HandlePropertyName name) +IonBuilder::jsop_delprop(PropertyName *name) { MDefinition *obj = current->pop(); @@ -8858,7 +8849,7 @@ IonBuilder::jsop_defvar(uint32_t index) { JS_ASSERT(JSOp(*pc) == JSOP_DEFVAR || JSOp(*pc) == JSOP_DEFCONST); - RootedPropertyName name(cx, script()->getName(index)); + PropertyName *name = script()->getName(index); // Bake in attrs. unsigned attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT; @@ -8878,7 +8869,7 @@ IonBuilder::jsop_defvar(uint32_t index) bool IonBuilder::jsop_deffun(uint32_t index) { - RootedFunction fun(cx, script()->getFunction(index)); + JSFunction *fun = script()->getFunction(index); if (fun->isNative() && IsAsmJSModuleNative(fun->native())) return abort("asm.js module function"); @@ -9047,7 +9038,7 @@ IonBuilder::walkScopeChain(unsigned hops) } bool -IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, MutableHandleObject pcall) +IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall) { JSScript *outerScript = ScopeCoordinateFunctionScript(cx, script(), pc); if (!outerScript || !outerScript->treatAsRunOnce) @@ -9078,7 +9069,7 @@ IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, MutableHandleObject pcall) environment->as().callee().nonLazyScript() == outerScript) { JS_ASSERT(environment->hasSingletonType()); - pcall.set(environment); + *pcall = environment; return true; } environment = environment->enclosingScope(); @@ -9095,7 +9086,7 @@ IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, MutableHandleObject pcall) scope->as().callee().nonLazyScript() == outerScript) { JS_ASSERT(scope->hasSingletonType()); - pcall.set(scope); + *pcall = scope; return true; } } @@ -9106,9 +9097,9 @@ IonBuilder::hasStaticScopeObject(ScopeCoordinate sc, MutableHandleObject pcall) bool IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc) { - RootedObject call(cx); + JSObject *call = NULL; if (hasStaticScopeObject(sc, &call) && call) { - RootedPropertyName name(cx, ScopeCoordinateName(cx, script(), pc)); + PropertyName *name = ScopeCoordinateName(cx, script(), pc); bool succeeded; if (!getStaticName(call, name, &succeeded)) return false; @@ -9118,7 +9109,7 @@ IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc) MDefinition *obj = walkScopeChain(sc.hops); - RootedShape shape(cx, ScopeCoordinateToStaticScopeShape(cx, script(), pc)); + Shape *shape = ScopeCoordinateToStaticScopeShape(cx, script(), pc); MInstruction *load; if (shape->numFixedSlots() <= sc.slot) { @@ -9140,7 +9131,7 @@ IonBuilder::jsop_getaliasedvar(ScopeCoordinate sc) bool IonBuilder::jsop_setaliasedvar(ScopeCoordinate sc) { - RootedObject call(cx); + JSObject *call = NULL; if (hasStaticScopeObject(sc, &call)) { uint32_t depth = current->stackDepth() + 1; if (depth > current->nslots()) { @@ -9148,7 +9139,7 @@ IonBuilder::jsop_setaliasedvar(ScopeCoordinate sc) return false; } MDefinition *value = current->pop(); - RootedPropertyName name(cx, ScopeCoordinateName(cx, script(), pc)); + PropertyName *name = ScopeCoordinateName(cx, script(), pc); if (call) { // Push the object on the stack to match the bound object expected in @@ -9171,7 +9162,7 @@ IonBuilder::jsop_setaliasedvar(ScopeCoordinate sc) MDefinition *rval = current->peek(-1); MDefinition *obj = walkScopeChain(sc.hops); - RootedShape shape(cx, ScopeCoordinateToStaticScopeShape(cx, script(), pc)); + Shape *shape = ScopeCoordinateToStaticScopeShape(cx, script(), pc); if (NeedsPostBarrier(info(), rval)) current->add(MPostWriteBarrier::New(obj, rval)); diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 6483a1d6a6a..0eabbe9220b 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -330,8 +330,8 @@ class IonBuilder : public MIRGenerator JSObject *getSingletonPrototype(JSFunction *target); MDefinition *createThisScripted(MDefinition *callee); - MDefinition *createThisScriptedSingleton(HandleFunction target, MDefinition *callee); - MDefinition *createThis(HandleFunction target, MDefinition *callee); + MDefinition *createThisScriptedSingleton(JSFunction *target, MDefinition *callee); + MDefinition *createThis(JSFunction *target, MDefinition *callee); MInstruction *createDeclEnvObject(MDefinition *callee, MDefinition *scopeObj); MInstruction *createCallObject(MDefinition *callee, MDefinition *scopeObj); @@ -347,7 +347,7 @@ class IonBuilder : public MIRGenerator bool invalidatedIdempotentCache(); - bool hasStaticScopeObject(ScopeCoordinate sc, MutableHandleObject pcall); + bool hasStaticScopeObject(ScopeCoordinate sc, JSObject **pcall); bool loadSlot(MDefinition *obj, Shape *shape, MIRType rvalType, bool barrier, types::TemporaryTypeSet *types); bool storeSlot(MDefinition *obj, Shape *shape, MDefinition *value, bool needsBarrier, @@ -355,33 +355,33 @@ class IonBuilder : public MIRGenerator // jsop_getprop() helpers. bool getPropTryArgumentsLength(bool *emitted); - bool getPropTryConstant(bool *emitted, HandleId id, types::TemporaryTypeSet *types); - bool getPropTryDefiniteSlot(bool *emitted, HandlePropertyName name, + bool getPropTryConstant(bool *emitted, jsid id, types::TemporaryTypeSet *types); + bool getPropTryDefiniteSlot(bool *emitted, PropertyName *name, bool barrier, types::TemporaryTypeSet *types); - bool getPropTryCommonGetter(bool *emitted, HandleId id, + bool getPropTryCommonGetter(bool *emitted, jsid id, bool barrier, types::TemporaryTypeSet *types); - bool getPropTryInlineAccess(bool *emitted, HandlePropertyName name, HandleId id, + bool getPropTryInlineAccess(bool *emitted, PropertyName *name, jsid id, bool barrier, types::TemporaryTypeSet *types); - bool getPropTryCache(bool *emitted, HandlePropertyName name, HandleId id, + bool getPropTryCache(bool *emitted, PropertyName *name, jsid id, bool barrier, types::TemporaryTypeSet *types); bool needsToMonitorMissingProperties(types::TemporaryTypeSet *types); // jsop_setprop() helpers. bool setPropTryCommonSetter(bool *emitted, MDefinition *obj, - HandlePropertyName name, HandleId id, + PropertyName *name, jsid id, MDefinition *value); bool setPropTryCommonDOMSetter(bool *emitted, MDefinition *obj, - MDefinition *value, HandleFunction setter, + MDefinition *value, JSFunction *setter, bool isDOM); bool setPropTryDefiniteSlot(bool *emitted, MDefinition *obj, - HandlePropertyName name, MDefinition *value, + PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); bool setPropTryInlineAccess(bool *emitted, MDefinition *obj, - HandlePropertyName name, HandleId id, + PropertyName *name, jsid id, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); bool setPropTryCache(bool *emitted, MDefinition *obj, - HandlePropertyName name, MDefinition *value, + PropertyName *name, MDefinition *value, bool barrier, types::TemporaryTypeSet *objTypes); // jsop_setelem() helpers. @@ -432,10 +432,10 @@ class IonBuilder : public MIRGenerator bool jsop_dup2(); bool jsop_loophead(jsbytecode *pc); bool jsop_compare(JSOp op); - bool getStaticName(HandleObject staticObject, HandlePropertyName name, bool *psucceeded); - bool setStaticName(HandleObject staticObject, HandlePropertyName name); - bool jsop_getname(HandlePropertyName name); - bool jsop_intrinsic(HandlePropertyName name); + bool getStaticName(JSObject *staticObject, PropertyName *name, bool *psucceeded); + bool setStaticName(JSObject *staticObject, PropertyName *name); + bool jsop_getname(PropertyName *name); + bool jsop_intrinsic(PropertyName *name); bool jsop_bindname(PropertyName *name); bool jsop_getelem(); bool jsop_getelem_dense(MDefinition *obj, MDefinition *index); @@ -455,16 +455,16 @@ class IonBuilder : public MIRGenerator bool jsop_runonce(); bool jsop_rest(); bool jsop_not(); - bool jsop_getprop(HandlePropertyName name); - bool jsop_setprop(HandlePropertyName name); - bool jsop_delprop(HandlePropertyName name); + bool jsop_getprop(PropertyName *name); + bool jsop_setprop(PropertyName *name); + bool jsop_delprop(PropertyName *name); bool jsop_delelem(); bool jsop_newarray(uint32_t count); - bool jsop_newobject(HandleObject baseObj); + bool jsop_newobject(JSObject *baseObj); bool jsop_initelem(); bool jsop_initelem_array(); bool jsop_initelem_getter_setter(); - bool jsop_initprop(HandlePropertyName name); + bool jsop_initprop(PropertyName *name); bool jsop_initprop_getter_setter(PropertyName *name); bool jsop_regexp(RegExpObject *reobj); bool jsop_object(JSObject *obj); @@ -581,17 +581,17 @@ class IonBuilder : public MIRGenerator MTypeObjectDispatch *dispatch, MGetPropertyCache *cache, MBasicBlock **fallbackTarget); - bool testNeedsArgumentCheck(JSContext *cx, HandleFunction target, CallInfo &callInfo); + bool testNeedsArgumentCheck(JSContext *cx, JSFunction *target, CallInfo &callInfo); - MDefinition *makeCallsiteClone(HandleFunction target, MDefinition *fun); - MCall *makeCallHelper(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite); - bool makeCall(HandleFunction target, CallInfo &callInfo, bool cloneAtCallsite); + MDefinition *makeCallsiteClone(JSFunction *target, MDefinition *fun); + MCall *makeCallHelper(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite); + bool makeCall(JSFunction *target, CallInfo &callInfo, bool cloneAtCallsite); MDefinition *patchInlinedReturn(CallInfo &callInfo, MBasicBlock *exit, MBasicBlock *bottom); MDefinition *patchInlinedReturns(CallInfo &callInfo, MIRGraphExits &exits, MBasicBlock *bottom); - inline bool TestCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, - HandleId id, JSFunction **funcp, + inline bool testCommonPropFunc(JSContext *cx, types::TemporaryTypeSet *types, + jsid id, JSFunction **funcp, bool isGetter, bool *isDOM, MDefinition **guardOut); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 957377d1765..9461c54e07e 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -324,8 +324,9 @@ IonBuilder::inlineArrayPopShift(CallInfo &callInfo, MArrayPopShift::Mode mode) return InliningStatus_NotInlined; if (thisTypes->hasObjectFlags(cx, unhandledFlags)) return InliningStatus_NotInlined; - RootedScript script(cx, script_); - if (types::ArrayPrototypeHasIndexedProperty(cx, script)) + + RootedScript scriptRoot(cx, script()); + if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot)) return InliningStatus_NotInlined; callInfo.unwrapArgs(); @@ -387,8 +388,9 @@ IonBuilder::inlineArrayPush(CallInfo &callInfo) { return InliningStatus_NotInlined; } - RootedScript script(cx, script_); - if (types::ArrayPrototypeHasIndexedProperty(cx, script)) + + RootedScript scriptRoot(cx, script()); + if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot)) return InliningStatus_NotInlined; types::TemporaryTypeSet::DoubleConversion conversion = thisTypes->convertDoubleElements(cx); @@ -455,8 +457,8 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) } // Watch out for indexed properties on the prototype. - RootedScript script(cx, script_); - if (types::ArrayPrototypeHasIndexedProperty(cx, script)) + RootedScript scriptRoot(cx, script()); + if (types::ArrayPrototypeHasIndexedProperty(cx, scriptRoot)) return InliningStatus_NotInlined; // Require the 'this' types to have a specific type matching the current @@ -467,7 +469,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) types::TypeObject *thisType = thisTypes->getTypeObject(0); if (!thisType || thisType->unknownProperties() || - &thisType->proto->global() != &script->global()) + &thisType->proto->global() != &script()->global()) { return InliningStatus_NotInlined; } @@ -511,7 +513,7 @@ IonBuilder::inlineArrayConcat(CallInfo &callInfo) } // Inline the call. - RootedObject templateObj(cx, NewDenseEmptyArray(cx, thisType->proto, TenuredObject)); + JSObject *templateObj = NewDenseEmptyArray(cx, thisType->proto, TenuredObject); if (!templateObj) return InliningStatus_Error; templateObj->setType(thisType); @@ -701,7 +703,7 @@ IonBuilder::inlineMathPow(CallInfo &callInfo) // Optimize some constant powers. if (callInfo.getArg(1)->isConstant()) { double pow; - RootedValue v(GetIonContext()->cx, callInfo.getArg(1)->toConstant()->value()); + Value v = callInfo.getArg(1)->toConstant()->value(); if (!ToNumber(GetIonContext()->cx, v, &pow)) return InliningStatus_Error; @@ -895,7 +897,7 @@ IonBuilder::inlineStringObject(CallInfo &callInfo) callInfo.unwrapArgs(); RootedString emptyString(cx, cx->runtime()->emptyString); - RootedObject templateObj(cx, StringObject::create(cx, emptyString, TenuredObject)); + JSObject *templateObj = StringObject::create(cx, emptyString, TenuredObject); if (!templateObj) return InliningStatus_Error; @@ -1309,7 +1311,7 @@ IonBuilder::inlineParallelArrayTail(CallInfo &callInfo, // Create the MIR to allocate the new parallel array. Take the type // object is taken from the prediction set. - RootedObject templateObject(cx, ParallelArrayObject::newInstance(cx, TenuredObject)); + JSObject *templateObject = ParallelArrayObject::newInstance(cx, TenuredObject); if (!templateObject) return InliningStatus_Error; templateObject->setType(typeObject); @@ -1373,7 +1375,7 @@ IonBuilder::inlineNewDenseArrayForParallelExecution(CallInfo &callInfo) return InliningStatus_NotInlined; types::TypeObject *typeObject = returnTypes->getTypeObject(0); - RootedObject templateObject(cx, NewDenseAllocatedArray(cx, 0, NULL, TenuredObject)); + JSObject *templateObject = NewDenseAllocatedArray(cx, 0, NULL, TenuredObject); if (!templateObject) return InliningStatus_Error; templateObject->setType(typeObject); diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index 7bcacc8b6b7..ac41db67fb4 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -1502,7 +1502,7 @@ class MInitProp CompilerRootPropertyName name_; protected: - MInitProp(MDefinition *obj, HandlePropertyName name, MDefinition *value) + MInitProp(MDefinition *obj, PropertyName *name, MDefinition *value) : name_(name) { setOperand(0, obj); @@ -1513,7 +1513,7 @@ class MInitProp public: INSTRUCTION_HEADER(InitProp) - static MInitProp *New(MDefinition *obj, HandlePropertyName name, MDefinition *value) { + static MInitProp *New(MDefinition *obj, PropertyName *name, MDefinition *value) { return new MInitProp(obj, name, value); } @@ -4327,7 +4327,7 @@ class MDefFun : public MUnaryInstruction CompilerRootFunction fun_; private: - MDefFun(HandleFunction fun, MDefinition *scopeChain) + MDefFun(JSFunction *fun, MDefinition *scopeChain) : MUnaryInstruction(scopeChain), fun_(fun) {} @@ -4335,7 +4335,7 @@ class MDefFun : public MUnaryInstruction public: INSTRUCTION_HEADER(DefFun) - static MDefFun *New(HandleFunction fun, MDefinition *scopeChain) { + static MDefFun *New(JSFunction *fun, MDefinition *scopeChain) { return new MDefFun(fun, scopeChain); } @@ -5327,7 +5327,7 @@ class MArrayConcat { CompilerRootObject templateObj_; - MArrayConcat(MDefinition *lhs, MDefinition *rhs, HandleObject templateObj) + MArrayConcat(MDefinition *lhs, MDefinition *rhs, JSObject *templateObj) : MBinaryInstruction(lhs, rhs), templateObj_(templateObj) { @@ -5338,7 +5338,7 @@ class MArrayConcat public: INSTRUCTION_HEADER(ArrayConcat) - static MArrayConcat *New(MDefinition *lhs, MDefinition *rhs, HandleObject templateObj) { + static MArrayConcat *New(MDefinition *lhs, MDefinition *rhs, JSObject *templateObj) { return new MArrayConcat(lhs, rhs, templateObj); } @@ -5924,7 +5924,7 @@ class MGetPropertyCache InlinePropertyTable *inlinePropertyTable_; - MGetPropertyCache(MDefinition *obj, HandlePropertyName name) + MGetPropertyCache(MDefinition *obj, PropertyName *name) : MUnaryInstruction(obj), name_(name), idempotent_(false), @@ -5943,7 +5943,7 @@ class MGetPropertyCache public: INSTRUCTION_HEADER(GetPropertyCache) - static MGetPropertyCache *New(MDefinition *obj, HandlePropertyName name) { + static MGetPropertyCache *New(MDefinition *obj, PropertyName *name) { return new MGetPropertyCache(obj, name); } @@ -6025,7 +6025,7 @@ class MGetPropertyPolymorphic Vector shapes_; CompilerRootPropertyName name_; - MGetPropertyPolymorphic(MDefinition *obj, HandlePropertyName name) + MGetPropertyPolymorphic(MDefinition *obj, PropertyName *name) : MUnaryInstruction(obj), name_(name) { @@ -6040,7 +6040,7 @@ class MGetPropertyPolymorphic public: INSTRUCTION_HEADER(GetPropertyPolymorphic) - static MGetPropertyPolymorphic *New(MDefinition *obj, HandlePropertyName name) { + static MGetPropertyPolymorphic *New(MDefinition *obj, PropertyName *name) { return new MGetPropertyPolymorphic(obj, name); } @@ -6683,7 +6683,7 @@ class MGetNameCache CompilerRootPropertyName name_; AccessKind kind_; - MGetNameCache(MDefinition *obj, HandlePropertyName name, AccessKind kind) + MGetNameCache(MDefinition *obj, PropertyName *name, AccessKind kind) : MUnaryInstruction(obj), name_(name), kind_(kind) @@ -6694,7 +6694,7 @@ class MGetNameCache public: INSTRUCTION_HEADER(GetNameCache) - static MGetNameCache *New(MDefinition *obj, HandlePropertyName name, AccessKind kind) { + static MGetNameCache *New(MDefinition *obj, PropertyName *name, AccessKind kind) { return new MGetNameCache(obj, name, kind); } TypePolicy *typePolicy() { @@ -6715,7 +6715,7 @@ class MCallGetIntrinsicValue : public MNullaryInstruction { CompilerRootPropertyName name_; - MCallGetIntrinsicValue(HandlePropertyName name) + MCallGetIntrinsicValue(PropertyName *name) : name_(name) { setResultType(MIRType_Value); @@ -6724,7 +6724,7 @@ class MCallGetIntrinsicValue : public MNullaryInstruction public: INSTRUCTION_HEADER(CallGetIntrinsicValue) - static MCallGetIntrinsicValue *New(HandlePropertyName name) { + static MCallGetIntrinsicValue *New(PropertyName *name) { return new MCallGetIntrinsicValue(name); } PropertyName *name() const { @@ -6780,7 +6780,7 @@ class MSetPropertyInstruction : public MBinaryInstruction bool needsBarrier_; protected: - MSetPropertyInstruction(MDefinition *obj, MDefinition *value, HandlePropertyName name, + MSetPropertyInstruction(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict) : MBinaryInstruction(obj, value), name_(name), strict_(strict), needsBarrier_(true) @@ -6835,7 +6835,7 @@ class MDeleteProperty CompilerRootPropertyName name_; protected: - MDeleteProperty(MDefinition *val, HandlePropertyName name) + MDeleteProperty(MDefinition *val, PropertyName *name) : MUnaryInstruction(val), name_(name) { @@ -6845,7 +6845,7 @@ class MDeleteProperty public: INSTRUCTION_HEADER(DeleteProperty) - static MDeleteProperty *New(MDefinition *obj, HandlePropertyName name) { + static MDeleteProperty *New(MDefinition *obj, PropertyName *name) { return new MDeleteProperty(obj, name); } MDefinition *value() const { @@ -6892,7 +6892,7 @@ class MCallSetProperty : public MSetPropertyInstruction, public CallSetElementPolicy { - MCallSetProperty(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict) + MCallSetProperty(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict) : MSetPropertyInstruction(obj, value, name, strict) { } @@ -6900,7 +6900,7 @@ class MCallSetProperty public: INSTRUCTION_HEADER(CallSetProperty) - static MCallSetProperty *New(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict) { + static MCallSetProperty *New(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict) { return new MCallSetProperty(obj, value, name, strict); } @@ -6918,7 +6918,7 @@ class MSetPropertyCache { bool needsTypeBarrier_; - MSetPropertyCache(MDefinition *obj, MDefinition *value, HandlePropertyName name, bool strict, + MSetPropertyCache(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict, bool typeBarrier) : MSetPropertyInstruction(obj, value, name, strict), needsTypeBarrier_(typeBarrier) @@ -6928,7 +6928,7 @@ class MSetPropertyCache public: INSTRUCTION_HEADER(SetPropertyCache) - static MSetPropertyCache *New(MDefinition *obj, MDefinition *value, HandlePropertyName name, + static MSetPropertyCache *New(MDefinition *obj, MDefinition *value, PropertyName *name, bool strict, bool typeBarrier) { return new MSetPropertyCache(obj, value, name, strict, typeBarrier); } @@ -6980,7 +6980,7 @@ class MCallGetProperty CompilerRootPropertyName name_; bool idempotent_; - MCallGetProperty(MDefinition *value, HandlePropertyName name) + MCallGetProperty(MDefinition *value, PropertyName *name) : MUnaryInstruction(value), name_(name), idempotent_(false) { @@ -6990,7 +6990,7 @@ class MCallGetProperty public: INSTRUCTION_HEADER(CallGetProperty) - static MCallGetProperty *New(MDefinition *value, HandlePropertyName name) { + static MCallGetProperty *New(MDefinition *value, PropertyName *name) { return new MCallGetProperty(value, name); } MDefinition *value() const { @@ -8022,7 +8022,7 @@ class MNewStringObject : { CompilerRootObject templateObj_; - MNewStringObject(MDefinition *input, HandleObject templateObj) + MNewStringObject(MDefinition *input, JSObject *templateObj) : MUnaryInstruction(input), templateObj_(templateObj) { @@ -8032,7 +8032,7 @@ class MNewStringObject : public: INSTRUCTION_HEADER(NewStringObject) - static MNewStringObject *New(MDefinition *input, HandleObject templateObj) { + static MNewStringObject *New(MDefinition *input, JSObject *templateObj) { return new MNewStringObject(input, templateObj); } From c09b8d61a2d0c6bc8d3a3ce4a12d09c177db901e Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Wed, 18 Sep 2013 10:28:53 -0400 Subject: [PATCH 24/56] Bug 672717 - Broken caret when moving into/out of embedded objects with right arrow, r=tbsaunde --- .../src/generic/HyperTextAccessible.cpp | 10 +-- accessible/tests/mochitest/events.js | 22 ++++- .../mochitest/textcaret/test_general.html | 83 +++++++++++++++++-- 3 files changed, 98 insertions(+), 17 deletions(-) diff --git a/accessible/src/generic/HyperTextAccessible.cpp b/accessible/src/generic/HyperTextAccessible.cpp index eea26770480..a6d20d7abaa 100644 --- a/accessible/src/generic/HyperTextAccessible.cpp +++ b/accessible/src/generic/HyperTextAccessible.cpp @@ -593,14 +593,8 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode, // addTextOffset, to put us after the embedded object char. We'll only treat the offset as // before the embedded object char if we end at the very beginning of the child. addTextOffset = addTextOffset > 0; - } - else { - // Start offset, inclusive - // Make sure the offset lands on the embedded object character in order to indicate - // the true inner offset is inside the subtree for that link - addTextOffset = - (nsAccUtils::TextLength(descendantAcc) == addTextOffset) ? 1 : 0; - } + } else + addTextOffset = 0; descendantAcc = parentAcc; } diff --git a/accessible/tests/mochitest/events.js b/accessible/tests/mochitest/events.js index 112b1c482fb..264f168c19b 100644 --- a/accessible/tests/mochitest/events.js +++ b/accessible/tests/mochitest/events.js @@ -1541,10 +1541,12 @@ function moveToTextStart(aID) /** * Move the caret in text accessible. */ -function moveCaretToDOMPoint(aID, aNode, aOffset, aExpectedOffset, - aFocusTargetID) +function moveCaretToDOMPoint(aID, aDOMPointNodeID, aDOMPointOffset, + aExpectedOffset, aFocusTargetID, + aCheckFunc) { this.target = getAccessible(aID, [nsIAccessibleText]); + this.DOMPointNode = getNode(aDOMPointNodeID); this.focus = aFocusTargetID ? getAccessible(aFocusTargetID) : null; this.focusNode = this.focus ? this.focus.DOMNode : null; @@ -1553,13 +1555,25 @@ function moveCaretToDOMPoint(aID, aNode, aOffset, aExpectedOffset, if (this.focusNode) this.focusNode.focus(); - window.getSelection().getRangeAt(0).setStart(aNode, aOffset); + var selection = this.DOMPointNode.ownerDocument.defaultView.getSelection(); + var selRange = selection.getRangeAt(0); + selRange.setStart(this.DOMPointNode, aDOMPointOffset); + selRange.collapse(true); + + selection.removeRange(selRange); + selection.addRange(selRange); } this.getID = function moveCaretToDOMPoint_getID() { return "Set caret on " + prettyName(aID) + " at point: " + - prettyName(aNode) + " node with offset " + aOffset; + prettyName(aDOMPointNodeID) + " node with offset " + aDOMPointOffset; + } + + this.finalCheck = function moveCaretToDOMPoint_finalCheck() + { + if (aCheckFunc) + aCheckFunc.call(); } this.eventSeq = [ diff --git a/accessible/tests/mochitest/textcaret/test_general.html b/accessible/tests/mochitest/textcaret/test_general.html index c2b538d32af..69f83959f18 100644 --- a/accessible/tests/mochitest/textcaret/test_general.html +++ b/accessible/tests/mochitest/textcaret/test_general.html @@ -35,6 +35,29 @@ "Wrong caret offset for " + aID); } + function testCaretOffsets(aList) + { + for (var i = 0; i < aList.length; i++) + testCaretOffset(aList[0][0], aList[0][1]); + } + + function queueTraversalList(aList, aFocusNode) + { + for (var i = 0 ; i < aList.length; i++) { + var node = aList[i].DOMPoint[0]; + var nodeOffset = aList[i].DOMPoint[1]; + + var textAcc = aList[i].point[0]; + var textOffset = aList[i].point[1]; + var textList = aList[i].pointList; + var invoker = + new moveCaretToDOMPoint(textAcc, node, nodeOffset, textOffset, + ((i == 0) ? aFocusNode : null), + testCaretOffsets.bind(null, textList)) + gQueue.push(invoker); + } + } + /** * Do tests. */ @@ -48,7 +71,7 @@ turnCaretBrowsing(true); // test caret offsets - testCaretOffset(document, 14); + testCaretOffset(document, 16); testCaretOffset("textbox", -1); testCaretOffset("textarea", -1); testCaretOffset("p", -1); @@ -59,6 +82,49 @@ gQueue.push(new setCaretOffset("textbox", 1, "textbox")); gQueue.push(new setCaretOffset("link", 1, "link")); gQueue.push(new setCaretOffset("heading", 1, document)); + + // a*b*c + var p2Doc = getNode("p2_container").contentDocument; + var traversalList = [ + { // before 'a' + DOMPoint: [ getNode("p2", p2Doc).firstChild, 0 ], + point: [ getNode("p2", p2Doc), 0 ], + pointList: [ [ p2Doc, 0 ] ] + }, + { // after 'a' (before anchor) + DOMPoint: [ getNode("p2", p2Doc).firstChild, 1 ], + point: [ getNode("p2", p2Doc), 1 ], + pointList: [ [ p2Doc, 0 ] ] + }, + { // before 'b' (inside anchor) + DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 0 ], + point: [ getNode("p2_a", p2Doc), 0 ], + pointList: [ + [ getNode("p2", p2Doc), 1 ], + [ p2Doc, 0 ] + ] + }, + { // after 'b' (inside anchor) + DOMPoint: [ getNode("p2_a", p2Doc).firstChild, 1 ], + point: [ getNode("p2_a", p2Doc), 1 ], + pointList: [ + [ getNode("p2", p2Doc), 1 ] , + [ p2Doc, 0 ] + ] + }, + { // before 'c' (after anchor) + DOMPoint: [ getNode("p2", p2Doc).lastChild, 0 ], + point: [ getNode("p2", p2Doc), 2 ], + pointList: [ [ p2Doc, 0 ] ] + }, + { // after 'c' + DOMPoint: [ getNode("p2", p2Doc).lastChild, 1 ], + point: [ getNode("p2", p2Doc), 3 ], + pointList: [ [ p2Doc, 0 ] ] + } + ]; + queueTraversalList(traversalList, getNode("p2", p2Doc)); + gQueue.onFinish = function() { turnCaretBrowsing(false); @@ -77,22 +143,27 @@ - Mozilla Bug 448744 + Bug 448744 - Mozilla Bug 524115 + Bug 524115 - Mozilla Bug 546068 + Bug 546068 + + + Bug 672717 - Mozilla Bug 725581 + Bug 725581

@@ -104,6 +175,8 @@

text
text

about mozilla
heading
+
From 12d708eeaca816d095d4a55dac36c279a695b226 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Wed, 18 Sep 2013 09:56:34 -0500 Subject: [PATCH 25/56] Bug 900669 - Fix compile error with JS_ION_PERF (r=me) --HG-- extra : rebase_source : faba81a347300a0f40e302930bd6d394e08dae66 --- js/src/jit/AsmJSLink.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/src/jit/AsmJSLink.cpp b/js/src/jit/AsmJSLink.cpp index 94c931ae905..1a1e37b5420 100644 --- a/js/src/jit/AsmJSLink.cpp +++ b/js/src/jit/AsmJSLink.cpp @@ -508,7 +508,7 @@ SendFunctionsToPerf(JSContext *cx, AsmJSModule &module) return true; uintptr_t base = (uintptr_t) module.codeBase(); - const char *filename = module.sourceDesc().scriptSource()->filename(); + const char *filename = module.scriptSource()->filename(); for (unsigned i = 0; i < module.numPerfFunctions(); i++) { const AsmJSModule::ProfiledFunction &func = module.perfProfiledFunction(i); @@ -535,7 +535,7 @@ SendBlocksToPerf(JSContext *cx, AsmJSModule &module) return true; unsigned long funcBaseAddress = (unsigned long) module.codeBase(); - const char *filename = module.sourceDesc().scriptSource()->filename(); + const char *filename = module.scriptSource()->filename(); for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) { const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i); From 61cbfaf31fa957cd7a296e42ec397cd70923549b Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Wed, 18 Sep 2013 08:34:12 -0700 Subject: [PATCH 26/56] Bug 914939 - Don't assert against OOM in XPCWrappedJS QI hooks. r=gabor --- js/xpconnect/src/XPCWrappedJSClass.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/js/xpconnect/src/XPCWrappedJSClass.cpp b/js/xpconnect/src/XPCWrappedJSClass.cpp index 1e4c9c69f6f..ac123f4f25e 100644 --- a/js/xpconnect/src/XPCWrappedJSClass.cpp +++ b/js/xpconnect/src/XPCWrappedJSClass.cpp @@ -252,10 +252,7 @@ nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx, JS_SetOptions(cx, oldOpts); - if (!success) { - MOZ_ASSERT(JS_IsExceptionPending(cx), - "JS failed without setting an exception!"); - + if (!success && JS_IsExceptionPending(cx)) { RootedValue jsexception(cx, NullValue()); if (JS_GetPendingException(cx, jsexception.address())) { @@ -290,6 +287,8 @@ nsXPCWrappedJSClass::CallQueryInterfaceOnJSObject(JSContext* cx, // Don't report if reporting was disabled by someone else. if (!(oldOpts & JSOPTION_DONT_REPORT_UNCAUGHT)) JS_ReportPendingException(cx); + } else if (!success) { + NS_WARNING("QI hook ran OOMed - this is probably a bug!"); } } From 3fc43261c73c3d841a7a8d11cd66a58f293c2159 Mon Sep 17 00:00:00 2001 From: Corey Ford Date: Fri, 13 Sep 2013 16:53:48 -0700 Subject: [PATCH 27/56] Bug 904197 - Use the union of continuations' rects in sticky positioning calculations. r=dholbert Reftest inline-3.html fails because handling the sticky element's margin correctly will take more work; similarly, border/padding on containing-block continuations won't be handled quite right. Reftest column-contain-1a fails because some of the anonymous blocks inside an nsColumnSetFrame have 0 height. --- layout/base/RestyleManager.cpp | 23 ++++++--- layout/generic/StickyScrollContainer.cpp | 48 +++++++++++++++---- layout/generic/StickyScrollContainer.h | 5 ++ layout/generic/nsFrame.cpp | 1 + layout/generic/nsHTMLReflowState.cpp | 9 +++- .../position-sticky/column-contain-1-ref.html | 42 ++++++++++++++++ .../position-sticky/column-contain-1a.html | 45 +++++++++++++++++ .../position-sticky/column-contain-1b.html | 47 ++++++++++++++++++ .../position-sticky/inline-1-ref.html | 29 +++++++++++ layout/reftests/position-sticky/inline-1.html | 28 +++++++++++ .../position-sticky/inline-2-ref.html | 29 +++++++++++ layout/reftests/position-sticky/inline-2.html | 36 ++++++++++++++ .../position-sticky/inline-3-ref.html | 35 ++++++++++++++ layout/reftests/position-sticky/inline-3.html | 41 ++++++++++++++++ layout/reftests/position-sticky/reftest.list | 5 ++ 15 files changed, 405 insertions(+), 18 deletions(-) create mode 100644 layout/reftests/position-sticky/column-contain-1-ref.html create mode 100644 layout/reftests/position-sticky/column-contain-1a.html create mode 100644 layout/reftests/position-sticky/column-contain-1b.html create mode 100644 layout/reftests/position-sticky/inline-1-ref.html create mode 100644 layout/reftests/position-sticky/inline-1.html create mode 100644 layout/reftests/position-sticky/inline-2-ref.html create mode 100644 layout/reftests/position-sticky/inline-2.html create mode 100644 layout/reftests/position-sticky/inline-3-ref.html create mode 100644 layout/reftests/position-sticky/inline-3.html diff --git a/layout/base/RestyleManager.cpp b/layout/base/RestyleManager.cpp index ce76ae12258..ef9daf9e2a8 100644 --- a/layout/base/RestyleManager.cpp +++ b/layout/base/RestyleManager.cpp @@ -350,7 +350,16 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame) // Move the frame if (display->mPosition == NS_STYLE_POSITION_STICKY) { - StickyScrollContainer::ComputeStickyOffsets(aFrame); + // Update sticky positioning for an entire element at once when + // RecomputePosition is called with the first continuation in a chain. + if (!aFrame->GetPrevContinuation()) { + StickyScrollContainer::ComputeStickyOffsets(aFrame); + StickyScrollContainer* ssc = + StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); + if (ssc) { + ssc->PositionContinuations(aFrame); + } + } } else { MOZ_ASSERT(NS_STYLE_POSITION_RELATIVE == display->mPosition, "Unexpected type of positioning"); @@ -362,14 +371,14 @@ RestyleManager::RecomputePosition(nsIFrame* aFrame) NS_ASSERTION(newOffsets.left == -newOffsets.right && newOffsets.top == -newOffsets.bottom, "ComputeRelativeOffsets should return valid results"); + + // nsHTMLReflowState::ApplyRelativePositioning would work here, but + // since we've already checked mPosition and aren't changing the frame's + // normal position, go ahead and add the offsets directly. + aFrame->SetPosition(aFrame->GetNormalPosition() + + nsPoint(newOffsets.left, newOffsets.top)); } - nsPoint position = aFrame->GetNormalPosition(); - - // This handles both relative and sticky positioning. - nsHTMLReflowState::ApplyRelativePositioning(aFrame, newOffsets, &position); - aFrame->SetPosition(position); - return true; } diff --git a/layout/generic/StickyScrollContainer.cpp b/layout/generic/StickyScrollContainer.cpp index 8210ba893bd..6b2ca7acf5e 100644 --- a/layout/generic/StickyScrollContainer.cpp +++ b/layout/generic/StickyScrollContainer.cpp @@ -129,6 +129,9 @@ void StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick, nsRect* aContain) const { + NS_ASSERTION(!aFrame->GetPrevContinuation(), + "Can't sticky position individual continuations"); + aStick->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX); aContain->SetRect(nscoord_MIN/2, nscoord_MIN/2, nscoord_MAX, nscoord_MAX); @@ -146,17 +149,16 @@ StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick, nsLayoutUtils::IsProperAncestorFrame(scrolledFrame, cbFrame), "Scroll frame should be an ancestor of the containing block"); - nsRect rect = aFrame->GetRect(); - nsMargin margin = aFrame->GetUsedMargin(); + nsRect rect = + nsLayoutUtils::GetAllInFlowRectsUnion(aFrame, aFrame->GetParent()); // Containing block limits if (cbFrame != scrolledFrame) { - nsMargin cbBorderPadding = cbFrame->GetUsedBorderAndPadding(); - aContain->SetRect(nsPoint(cbBorderPadding.left, cbBorderPadding.top) - - aFrame->GetParent()->GetOffsetTo(cbFrame), - cbFrame->GetContentRectRelativeToSelf().Size() - - rect.Size()); - aContain->Deflate(margin); + *aContain = nsLayoutUtils::GetAllInFlowRectsUnion(cbFrame, cbFrame); + aContain->MoveBy(-aFrame->GetParent()->GetOffsetTo(cbFrame)); + aContain->Deflate(cbFrame->GetUsedBorderAndPadding()); + aContain->Deflate(aFrame->GetUsedMargin()); + aContain->Deflate(nsMargin(0, rect.width, rect.height, 0)); } nsMargin sfPadding = scrolledFrame->GetUsedPadding(); @@ -197,6 +199,10 @@ StickyScrollContainer::ComputeStickyLimits(nsIFrame* aFrame, nsRect* aStick, aStick->SetRightEdge(mScrollPosition.x + sfPadding.left + sfSize.width - computedOffsets->right - rect.width - sfOffset.x); } + + // These limits are for the bounding box of aFrame's continuations. Convert + // to limits for aFrame itself. + aStick->MoveBy(aFrame->GetPosition() - rect.TopLeft()); } nsPoint @@ -255,6 +261,22 @@ StickyScrollContainer::GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter, } } +void +StickyScrollContainer::PositionContinuations(nsIFrame* aFrame) +{ + NS_ASSERTION(!aFrame->GetPrevContinuation(), + "Should be starting from the first continuation"); + nsPoint newPosition = ComputePosition(aFrame); + nsPoint translation = newPosition - aFrame->GetPosition(); + aFrame->SetPosition(newPosition); + + // Move all continuation frames by the same amount. + for (nsIFrame* cont = aFrame->GetNextContinuation(); cont; + cont = cont->GetNextContinuation()) { + cont->SetPosition(cont->GetPosition() + translation); + } +} + void StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition, nsIFrame* aSubtreeRoot) @@ -272,12 +294,18 @@ StickyScrollContainer::UpdatePositions(nsPoint aScrollPosition, oct.SetSubtreeRoot(aSubtreeRoot); for (nsTArray::size_type i = 0; i < mFrames.Length(); i++) { nsIFrame* f = mFrames[i]; + if (aSubtreeRoot) { // Reflowing the scroll frame, so recompute offsets. ComputeStickyOffsets(f); } - f->SetPosition(ComputePosition(f)); - oct.AddFrame(f); + // mFrames will only contain first continuations, because we filter in + // nsIFrame::Init. + PositionContinuations(f); + + for (nsIFrame* cont = f; cont; cont = cont->GetNextContinuation()) { + oct.AddFrame(cont); + } } oct.Flush(); } diff --git a/layout/generic/StickyScrollContainer.h b/layout/generic/StickyScrollContainer.h index 3b29a8dca8e..e919ed8e92a 100644 --- a/layout/generic/StickyScrollContainer.h +++ b/layout/generic/StickyScrollContainer.h @@ -63,6 +63,11 @@ public: */ void GetScrollRanges(nsIFrame* aFrame, nsRect* aOuter, nsRect* aInner) const; + /** + * Compute and set the position of a frame and its following continuations. + */ + void PositionContinuations(nsIFrame* aFrame); + /** * Compute and set the position of all sticky frames, given the current * scroll position of the scroll frame. If not in reflow, aSubtreeRoot should diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 67a331942d2..4ba20a8dcf8 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -512,6 +512,7 @@ nsFrame::Init(nsIContent* aContent, mState |= NS_FRAME_MAY_BE_TRANSFORMED; } if (disp->mPosition == NS_STYLE_POSITION_STICKY && + !aPrevInFlow && !(mState & NS_FRAME_IS_NONDISPLAY)) { StickyScrollContainer* ssc = StickyScrollContainer::GetStickyScrollContainerForFrame(this); diff --git a/layout/generic/nsHTMLReflowState.cpp b/layout/generic/nsHTMLReflowState.cpp index 94020f5fc4a..fd1ff62c324 100644 --- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -871,7 +871,14 @@ nsHTMLReflowState::ApplyRelativePositioning(nsIFrame* aFrame, const nsStyleDisplay* display = aFrame->StyleDisplay(); if (NS_STYLE_POSITION_RELATIVE == display->mPosition) { *aPosition += nsPoint(aComputedOffsets.left, aComputedOffsets.top); - } else if (NS_STYLE_POSITION_STICKY == display->mPosition) { + } else if (NS_STYLE_POSITION_STICKY == display->mPosition && + !aFrame->GetNextContinuation() && !aFrame->GetPrevContinuation()) { + // Sticky positioning for elements with multiple frames needs to be + // computed all at once. We can't safely do that here because we might be + // partway through (re)positioning the frames, so leave it until the scroll + // container reflows and calls StickyScrollContainer::UpdatePositions. + // For single-frame sticky positioned elements, though, go ahead and apply + // it now to avoid unnecessary overflow updates later. StickyScrollContainer* ssc = StickyScrollContainer::GetStickyScrollContainerForFrame(aFrame); if (ssc) { diff --git a/layout/reftests/position-sticky/column-contain-1-ref.html b/layout/reftests/position-sticky/column-contain-1-ref.html new file mode 100644 index 00000000000..39ed2b1cb66 --- /dev/null +++ b/layout/reftests/position-sticky/column-contain-1-ref.html @@ -0,0 +1,42 @@ + + + + + + + + +
+
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/column-contain-1a.html b/layout/reftests/position-sticky/column-contain-1a.html new file mode 100644 index 00000000000..136851dc9d8 --- /dev/null +++ b/layout/reftests/position-sticky/column-contain-1a.html @@ -0,0 +1,45 @@ + + + + + CSS Test: Sticky Positioning - multiframe containing-block element + + + + + + +
+
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/column-contain-1b.html b/layout/reftests/position-sticky/column-contain-1b.html new file mode 100644 index 00000000000..f9e16681e25 --- /dev/null +++ b/layout/reftests/position-sticky/column-contain-1b.html @@ -0,0 +1,47 @@ + + + + + CSS Test: Sticky Positioning - multiframe containing-block element + + + + + + +
+
+
+
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/inline-1-ref.html b/layout/reftests/position-sticky/inline-1-ref.html new file mode 100644 index 00000000000..02cf1ee3348 --- /dev/null +++ b/layout/reftests/position-sticky/inline-1-ref.html @@ -0,0 +1,29 @@ + + + + + + + + +
+ hello
hello there
everyone
+
+ + diff --git a/layout/reftests/position-sticky/inline-1.html b/layout/reftests/position-sticky/inline-1.html new file mode 100644 index 00000000000..675dd074436 --- /dev/null +++ b/layout/reftests/position-sticky/inline-1.html @@ -0,0 +1,28 @@ + + + + + CSS Test: Sticky Positioning - inline + + + + + + +
+ hello there
everyone
+
+ + diff --git a/layout/reftests/position-sticky/inline-2-ref.html b/layout/reftests/position-sticky/inline-2-ref.html new file mode 100644 index 00000000000..2d881b860b7 --- /dev/null +++ b/layout/reftests/position-sticky/inline-2-ref.html @@ -0,0 +1,29 @@ + + + + + + + + +
+ hello
hello there
everyone
+
+ + diff --git a/layout/reftests/position-sticky/inline-2.html b/layout/reftests/position-sticky/inline-2.html new file mode 100644 index 00000000000..d41a42be92e --- /dev/null +++ b/layout/reftests/position-sticky/inline-2.html @@ -0,0 +1,36 @@ + + + + + CSS Test: Sticky Positioning - inline, dynamic changes + + + + + + + +
+ hello there
everyone
+
+ + + diff --git a/layout/reftests/position-sticky/inline-3-ref.html b/layout/reftests/position-sticky/inline-3-ref.html new file mode 100644 index 00000000000..59728a6ecc9 --- /dev/null +++ b/layout/reftests/position-sticky/inline-3-ref.html @@ -0,0 +1,35 @@ + + + + + + + + +
+
+
+ + diff --git a/layout/reftests/position-sticky/inline-3.html b/layout/reftests/position-sticky/inline-3.html new file mode 100644 index 00000000000..329bd8599b0 --- /dev/null +++ b/layout/reftests/position-sticky/inline-3.html @@ -0,0 +1,41 @@ + + + + + CSS Test: Sticky Positioning - inline with margins + + + + + + +
+
+
+
+
+ + diff --git a/layout/reftests/position-sticky/reftest.list b/layout/reftests/position-sticky/reftest.list index 0d7fed3963b..9abd7ae355d 100644 --- a/layout/reftests/position-sticky/reftest.list +++ b/layout/reftests/position-sticky/reftest.list @@ -41,3 +41,8 @@ fuzzy-if(Android,4,1) == containing-block-1.html containing-block-1-ref.html == overconstrained-1.html overconstrained-1-ref.html == overconstrained-2.html overconstrained-2-ref.html == overconstrained-3.html overconstrained-3-ref.html +== inline-1.html inline-1-ref.html +== inline-2.html inline-2-ref.html +fails == inline-3.html inline-3-ref.html # bug 916302 +fails == column-contain-1a.html column-contain-1-ref.html +== column-contain-1b.html column-contain-1-ref.html From df2805989ecfe32b552681e30847febf824fe5b6 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Wed, 18 Sep 2013 08:36:27 -0700 Subject: [PATCH 28/56] Bug 904197 followup: make reftests use Ahem font, to avoid pixel-alignment difference between block vs. inline elements. (test-only) --HG-- rename : layout/reftests/flexbox/ahem.css => layout/reftests/position-sticky/ahem.css --- layout/reftests/position-sticky/ahem.css | 4 ++++ layout/reftests/position-sticky/inline-1-ref.html | 3 ++- layout/reftests/position-sticky/inline-1.html | 3 ++- layout/reftests/position-sticky/inline-2-ref.html | 3 ++- layout/reftests/position-sticky/inline-2.html | 3 ++- layout/reftests/position-sticky/inline-3-ref.html | 3 ++- layout/reftests/position-sticky/inline-3.html | 3 ++- 7 files changed, 16 insertions(+), 6 deletions(-) create mode 100644 layout/reftests/position-sticky/ahem.css diff --git a/layout/reftests/position-sticky/ahem.css b/layout/reftests/position-sticky/ahem.css new file mode 100644 index 00000000000..884a41198b8 --- /dev/null +++ b/layout/reftests/position-sticky/ahem.css @@ -0,0 +1,4 @@ +@font-face { + font-family: "Ahem"; + src: url(../fonts/Ahem.ttf); +} diff --git a/layout/reftests/position-sticky/inline-1-ref.html b/layout/reftests/position-sticky/inline-1-ref.html index 02cf1ee3348..2c3bc2470f0 100644 --- a/layout/reftests/position-sticky/inline-1-ref.html +++ b/layout/reftests/position-sticky/inline-1-ref.html @@ -4,12 +4,13 @@ +