author | Rob Campbell <rcampbell@mozilla.com> |
Tue, 16 Aug 2011 12:21:00 -0300 | |
changeset 75386 | e765c8f565c63da5550aa59bf5d6358a4392fee0 |
parent 75385 | b44e9053d17d40417911a7b0ac1f209117b1814e (current diff) |
parent 75367 | 55c3a125c9bb94345ee529fc87cbbe20b22a7ddf (diff) |
child 75387 | 9967f28c64e183bd85ae5f761c3ab2d8cb5adbc3 |
child 75468 | a2b79dba671030d52bf6ee42227d0e1c6d3c7207 |
push id | 21011 |
push user | rcampbell@mozilla.com |
push date | Tue, 16 Aug 2011 15:35:10 +0000 |
treeherder | mozilla-central@e765c8f565c6 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 8.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
new file mode 100644 --- /dev/null +++ b/accessible/src/base/Statistics.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Trevor Saunders <trev.saunders@gmail.com> (original author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef A11Y_STATISTICS_H_ +#define A11Y_STATISTICS_H_ + +#include "mozilla/Telemetry.h" + +namespace mozilla { +namespace a11y { +namespace statistics { + + inline void A11yInitialized() + { Telemetry::Accumulate(Telemetry::A11Y_INSTANTIATED, true); } + +} // namespace statistics +} // namespace a11y +} // namespace mozilla + +#endif +
--- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -51,16 +51,17 @@ #include "nsHTMLLinkAccessible.h" #include "nsHTMLSelectAccessible.h" #include "nsHTMLTableAccessibleWrap.h" #include "nsHTMLTextAccessible.h" #include "nsHyperTextAccessibleWrap.h" #include "nsIAccessibilityService.h" #include "nsIAccessibleProvider.h" #include "States.h" +#include "Statistics.h" #include "nsIDOMDocument.h" #include "nsIDOMHTMLAreaElement.h" #include "nsIDOMHTMLLegendElement.h" #include "nsIDOMHTMLObjectElement.h" #include "nsIDOMHTMLOptGroupElement.h" #include "nsIDOMHTMLOptionElement.h" #include "nsIDOMXULElement.h" @@ -1767,16 +1768,18 @@ NS_GetAccessibilityService(nsIAccessibil nsRefPtr<nsAccessibilityService> service = new nsAccessibilityService(); NS_ENSURE_TRUE(service, NS_ERROR_OUT_OF_MEMORY); if (!service->Init()) { service->Shutdown(); return NS_ERROR_FAILURE; } + statistics::A11yInitialized(); + nsAccessibilityService::gAccessibilityService = service; NS_ADDREF(*aResult = service); return NS_OK; } //////////////////////////////////////////////////////////////////////////////// // nsAccessibilityService private (DON'T put methods here)
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -778,22 +778,22 @@ pref("browser.sessionstore.privacy_level // how many tabs can be reopened (per window) pref("browser.sessionstore.max_tabs_undo", 10); // how many windows can be reopened (per session) - on non-OS X platforms this // pref may be ignored when dealing with pop-up windows to ensure proper startup pref("browser.sessionstore.max_windows_undo", 3); // number of crashes that can occur before the about:sessionrestore page is displayed // (this pref has no effect if more than 6 hours have passed since the last crash) pref("browser.sessionstore.max_resumed_crashes", 1); -// The number of tabs that can restore concurrently: -// < 0 = All tabs can restore at the same time -// 0 = Only the selected tab in each window will be restored -// Other tabs won't be restored until they are selected -// N = The number of tabs to restore at the same time -pref("browser.sessionstore.max_concurrent_tabs", 3); +// restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant) +// and restore_hidden_tabs. When true, tabs will not be restored until they are +// focused (also applies to tabs that aren't visible). When false, the values +// for MAX_CONCURRENT_TAB_RESTORES and restore_hidden_tabs are respected. +// Selected tabs are always restored regardless of this pref. +pref("browser.sessionstore.restore_on_demand", false); // Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not pref("browser.sessionstore.restore_hidden_tabs", false); // allow META refresh by default pref("accessibility.blockautorefresh", false); // Whether history is enabled or not. pref("places.history.enabled", true);
--- a/browser/base/content/test/tabview/browser_tabview_bug595601.js +++ b/browser/base/content/test/tabview/browser_tabview_bug595601.js @@ -30,24 +30,21 @@ function test() { Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", false); TabsProgressListener.init(); registerCleanupFunction(function () { TabsProgressListener.uninit(); - Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs"); Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs"); ss.setBrowserState(stateBackup); }); - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 3); - TabView._initFrame(function () { executeSoon(testRestoreWithHiddenTabs); }); } function testRestoreWithHiddenTabs() { let checked = false; let ssReady = false;
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp +++ b/browser/components/migration/src/nsIEProfileMigrator.cpp @@ -2094,17 +2094,16 @@ nsIEProfileMigrator::CopySecurityPrefs(n "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"); if (regKey && NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER, key, nsIWindowsRegKey::ACCESS_READ))) { PRUint32 value; if (NS_SUCCEEDED(regKey->ReadIntValue(NS_LITERAL_STRING("SecureProtocols"), &value))) { - aPrefs->SetBoolPref("security.enable_ssl2", (value >> 3) & PR_TRUE); aPrefs->SetBoolPref("security.enable_ssl3", (value >> 5) & PR_TRUE); aPrefs->SetBoolPref("security.enable_tls", (value >> 7) & PR_TRUE); } } return NS_OK; }
--- a/browser/components/migration/src/nsOperaProfileMigrator.cpp +++ b/browser/components/migration/src/nsOperaProfileMigrator.cpp @@ -315,17 +315,16 @@ nsOperaProfileMigrator::PrefTransform gT { nsnull, "Allow script to change status", _OPM(BOOL), "dom.disable_window_status_change", _OPM(SetBool), PR_FALSE, { -1 } }, { nsnull, "Ignore Unrequested Popups", _OPM(BOOL), "dom.disable_open_during_load", _OPM(SetBool), PR_FALSE, { -1 } }, { nsnull, "Load Figures", _OPM(BOOL), "permissions.default.image", _OPM(SetImageBehavior), PR_FALSE, { -1 } }, { "Visited link", nsnull, _OPM(COLOR), "browser.visited_color", _OPM(SetString), PR_FALSE, { -1 } }, { "Link", nsnull, _OPM(COLOR), "browser.anchor_color", _OPM(SetString), PR_FALSE, { -1 } }, { nsnull, "Underline", _OPM(BOOL), "browser.underline_anchors", _OPM(SetBool), PR_FALSE, { -1 } }, - { "Security Prefs", "Enable SSL v2", _OPM(BOOL), "security.enable_ssl2", _OPM(SetBool), PR_FALSE, { -1 } }, { nsnull, "Enable SSL v3", _OPM(BOOL), "security.enable_ssl3", _OPM(SetBool), PR_FALSE, { -1 } }, { nsnull, "Enable TLS v1.0", _OPM(BOOL), "security.enable_tls", _OPM(SetBool), PR_FALSE, { -1 } }, { "Extensions", "Scripting", _OPM(BOOL), "javascript.enabled", _OPM(SetBool), PR_FALSE, { -1 } } }; nsresult nsOperaProfileMigrator::SetFile(void* aTransform, nsIPrefBranch* aBranch)
--- a/browser/components/migration/src/nsSeamonkeyProfileMigrator.cpp +++ b/browser/components/migration/src/nsSeamonkeyProfileMigrator.cpp @@ -342,17 +342,16 @@ nsSeamonkeyProfileMigrator::PrefTransfor MAKESAMETYPEPREFTRANSFORM("browser.tabs.autoHide", Bool), MAKESAMETYPEPREFTRANSFORM("browser.tabs.loadInBackground", Bool), MAKESAMETYPEPREFTRANSFORM("browser.enable_automatic_image_resizing", Bool), MAKESAMETYPEPREFTRANSFORM("network.cookie.warnAboutCookies", Bool), MAKESAMETYPEPREFTRANSFORM("network.cookie.lifetime.enabled", Bool), MAKESAMETYPEPREFTRANSFORM("network.cookie.lifetime.behavior", Int), MAKESAMETYPEPREFTRANSFORM("dom.disable_open_during_load", Bool), MAKESAMETYPEPREFTRANSFORM("signon.rememberSignons", Bool), - MAKESAMETYPEPREFTRANSFORM("security.enable_ssl2", Bool), MAKESAMETYPEPREFTRANSFORM("security.enable_ssl3", Bool), MAKESAMETYPEPREFTRANSFORM("security.enable_tls", Bool), MAKESAMETYPEPREFTRANSFORM("security.warn_entering_secure", Bool), MAKESAMETYPEPREFTRANSFORM("security.warn_entering_weak", Bool), MAKESAMETYPEPREFTRANSFORM("security.warn_leaving_secure", Bool), MAKESAMETYPEPREFTRANSFORM("security.warn_submit_insecure", Bool), MAKESAMETYPEPREFTRANSFORM("security.warn_viewing_mixed", Bool), MAKESAMETYPEPREFTRANSFORM("security.default_personal_cert", String),
--- a/browser/components/preferences/main.js +++ b/browser/components/preferences/main.js @@ -48,16 +48,17 @@ var gMainPane = { { this._pane = document.getElementById("paneMain"); // set up the "use current page" label-changing listener this._updateUseCurrentButton(); window.addEventListener("focus", this._updateUseCurrentButton, false); this.updateBrowserStartupLastSession(); + this.startupPagePrefChanged(); // Notify observers that the UI is now ready Components.classes["@mozilla.org/observer-service;1"] .getService(Components.interfaces.nsIObserverService) .notifyObservers(window, "main-pane-loaded", null); }, // HOME PAGE @@ -76,16 +77,26 @@ var gMainPane = { * 2: the last page the user visited (DEPRECATED) * 3: windows and tabs from the last session (a.k.a. session restore) * * The deprecated option is not exposed in UI; however, if the user has it * selected and doesn't change the UI for this preference, the deprecated * option is preserved. */ + /** + * Enables/Disables the restore on demand checkbox. + */ + startupPagePrefChanged: function () + { + let startupPref = document.getElementById("browser.startup.page"); + let restoreOnDemandPref = document.getElementById("browser.sessionstore.restore_on_demand"); + restoreOnDemandPref.disabled = startupPref.value != 3; + }, + syncFromHomePref: function () { let homePref = document.getElementById("browser.startup.homepage"); // If the pref is set to about:home, set the value to "" to show the // placeholder text (about:home title). if (homePref.value.toLowerCase() == "about:home") return "";
--- a/browser/components/preferences/main.xul +++ b/browser/components/preferences/main.xul @@ -59,17 +59,21 @@ <script type="application/javascript" src="chrome://browser/content/preferences/main.js"/> <preferences id="mainPreferences"> <!-- XXX Button preferences --> <!-- Startup --> <preference id="browser.startup.page" name="browser.startup.page" - type="int"/> + type="int" + onchange="gMainPane.startupPagePrefChanged();"/> + <preference id="browser.sessionstore.restore_on_demand" + name="browser.sessionstore.restore_on_demand" + type="bool"/> <preference id="browser.startup.homepage" name="browser.startup.homepage" type="wstring"/> <preference id="pref.browser.homepage.disable_button.current_page" name="pref.browser.homepage.disable_button.current_page" type="bool"/> <preference id="pref.browser.homepage.disable_button.bookmark_page" @@ -115,16 +119,23 @@ <menulist id="browserStartupPage" preference="browser.startup.page"> <menupopup> <menuitem label="&startupHomePage.label;" value="1" id="browserStartupHomePage"/> <menuitem label="&startupBlankPage.label;" value="0" id="browserStartupBlank"/> <menuitem label="&startupLastSession.label;" value="3" id="browserStartupLastSession"/> </menupopup> </menulist> </hbox> + <hbox align="center"> + <checkbox id="restoreOnDemand" + label="&restoreOnDemand.label;" + accesskey="&restoreOnDemand.accesskey;" + class="indent" + preference="browser.sessionstore.restore_on_demand"/> + </hbox> <separator class="thin"/> <hbox align="center"> <label value="&homepage.label;" accesskey="&homepage.accesskey;" control="browserHomePage"/> <textbox id="browserHomePage" class="padded uri-element" flex="1" type="autocomplete" autocompletesearch="history" onsyncfrompreference="return gMainPane.syncFromHomePref();" onsynctopreference="return gMainPane.syncToHomePref(this.value);" placeholder="&abouthome.pageTitle;"
--- a/browser/components/sessionstore/src/nsSessionStore.js +++ b/browser/components/sessionstore/src/nsSessionStore.js @@ -70,16 +70,20 @@ const TAB_STATE_RESTORING = 2; const PRIVACY_NONE = 0; const PRIVACY_ENCRYPTED = 1; const PRIVACY_FULL = 2; const NOTIFY_WINDOWS_RESTORED = "sessionstore-windows-restored"; const NOTIFY_BROWSER_STATE_RESTORED = "sessionstore-browser-state-restored"; +// Maximum number of tabs to restore simultaneously. Previously controlled by +// the browser.sessionstore.max_concurrent_tabs pref. +const MAX_CONCURRENT_TAB_RESTORES = 3; + // global notifications observed const OBSERVING = [ "domwindowopened", "domwindowclosed", "quit-application-requested", "quit-application-granted", "browser-lastwindow-close-granted", "quit-application", "browser:purge-session-history", "private-browsing", "browser:purge-domain-data", "private-browsing-change-granted" @@ -187,17 +191,17 @@ SessionStoreService.prototype = { // the favicon is always saved for the about:sessionrestore page xulAttributes: {"image": true}, // set default load state _loadState: STATE_STOPPED, // During the initial restore and setBrowserState calls tracks the number of // windows yet to be restored - _restoreCount: 0, + _restoreCount: -1, // whether a setBrowserState call is in progress _browserSetState: false, // time in milliseconds (Date.now()) when the session was last written to file _lastSaveTime: 0, // time in milliseconds when the session was started (saved across sessions), @@ -224,19 +228,19 @@ SessionStoreService.prototype = { // whether the last window was closed and should be restored _restoreLastWindow: false, // tabs to restore in order _tabsToRestore: { visible: [], hidden: [] }, _tabsRestoringCount: 0, - // number of tabs to restore concurrently, pref controlled. - _maxConcurrentTabRestores: null, - + // overrides MAX_CONCURRENT_TAB_RESTORES and _restoreHiddenTabs when true + _restoreOnDemand: false, + // whether to restore hidden tabs or not, pref controlled. _restoreHiddenTabs: null, // The state from the previous session (after restoring pinned tabs). This // state is persisted and passed through to the next session during an app // restart to make the third party add-on warning not trash the deferred // session _lastSessionState: null, @@ -276,28 +280,31 @@ SessionStoreService.prototype = { initService: function() { OBSERVING.forEach(function(aTopic) { Services.obs.addObserver(this, aTopic, true); }, this); var pbs = Cc["@mozilla.org/privatebrowsing;1"]. getService(Ci.nsIPrivateBrowsingService); this._inPrivateBrowsing = pbs.privateBrowsingEnabled; - + + // Do pref migration before we store any values and start observing changes + this._migratePrefs(); + // observe prefs changes so we can modify stored data to match this._prefBranch.addObserver("sessionstore.max_tabs_undo", this, true); this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true); // this pref is only read at startup, so no need to observe it this._sessionhistory_max_entries = this._prefBranch.getIntPref("sessionhistory.max_entries"); - this._maxConcurrentTabRestores = - this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs"); - this._prefBranch.addObserver("sessionstore.max_concurrent_tabs", this, true); + this._restoreOnDemand = + this._prefBranch.getBoolPref("sessionstore.restore_on_demand"); + this._prefBranch.addObserver("sessionstore.restore_on_demand", this, true); this._restoreHiddenTabs = this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs"); this._prefBranch.addObserver("sessionstore.restore_hidden_tabs", this, true); // Make sure gRestoreTabsProgressListener has a reference to sessionstore // so that it can make calls back in gRestoreTabsProgressListener.ss = this; @@ -359,16 +366,19 @@ SessionStoreService.prototype = { this._sessionStartTime = this._initialState.session && this._initialState.session.startTime || this._sessionStartTime; // make sure that at least the first window doesn't have anything hidden delete this._initialState.windows[0].hidden; // Since nothing is hidden in the first window, it cannot be a popup delete this._initialState.windows[0].isPopup; + // We don't want to minimize and then open a window at startup. + if (this._initialState.windows[0].sizemode == "minimized") + this._initialState.windows[0].sizemode = "normal"; } } catch (ex) { debug("The session file is invalid: " + ex); } } if (this._resume_from_crash) { // create a backup if the session data file exists try { @@ -435,16 +445,29 @@ SessionStoreService.prototype = { // Make sure to break our cycle with the save timer if (this._saveTimer) { this._saveTimer.cancel(); this._saveTimer = null; } }, + _migratePrefs: function sss__migratePrefs() { + // Added For Firefox 8 + // max_concurrent_tabs is going away. We're going to hard code a max value + // (MAX_CONCURRENT_TAB_RESTORES) and start using a boolean pref restore_on_demand. + if (this._prefBranch.prefHasUserValue("sessionstore.max_concurrent_tabs") && + !this._prefBranch.prefHasUserValue("sessionstore.restore_on_demand")) { + let maxConcurrentTabs = + this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs"); + this._prefBranch.setBoolPref("sessionstore.restore_on_demand", maxConcurrentTabs == 0); + this._prefBranch.clearUserPref("sessionstore.max_concurrent_tabs"); + } + }, + /** * Handle notifications */ observe: function sss_observe(aSubject, aTopic, aData) { // for event listeners var _this = this; switch (aTopic) { @@ -624,19 +647,19 @@ SessionStoreService.prototype = { this._resume_session_once_on_shutdown = null; } // either create the file with crash recovery information or remove it // (when _loadState is not STATE_RUNNING, that file is used for session resuming instead) if (!this._resume_from_crash) this._clearDisk(); this.saveState(true); break; - case "sessionstore.max_concurrent_tabs": - this._maxConcurrentTabRestores = - this._prefBranch.getIntPref("sessionstore.max_concurrent_tabs"); + case "sessionstore.restore_on_demand": + this._restoreOnDemand = + this._prefBranch.getBoolPref("sessionstore.restore_on_demand"); break; case "sessionstore.restore_hidden_tabs": this._restoreHiddenTabs = this._prefBranch.getBoolPref("sessionstore.restore_hidden_tabs"); break; } break; case "timer-callback": // timer call back for delayed saving @@ -2901,17 +2924,17 @@ SessionStoreService.prototype = { event.initEvent("SSTabRestoring", true, false); tab.dispatchEvent(event); // Restore the history in the next tab aWindow.setTimeout(function(){ _this.restoreHistory(aWindow, aTabs, aTabData, aIdMap, aDocIdentMap); }, 0); - // This could cause us to ignore the max_concurrent_tabs pref a bit, but + // This could cause us to ignore MAX_CONCURRENT_TAB_RESTORES a bit, but // it ensures each window will have its selected tab loaded. if (aWindow.gBrowser.selectedBrowser == browser) { this.restoreTab(tab); } else { // Put the tab into the right bucket if (tabData.hidden) this._tabsToRestore.hidden.push(tab); @@ -3037,18 +3060,18 @@ SessionStoreService.prototype = { * if we have already reached the limit for number of tabs to restore */ restoreNextTab: function sss_restoreNextTab() { // If we call in here while quitting, we don't actually want to do anything if (this._loadState == STATE_QUITTING) return; // If it's not possible to restore anything, then just bail out. - if (this._maxConcurrentTabRestores >= 0 && - this._tabsRestoringCount >= this._maxConcurrentTabRestores) + if (this._restoreOnDemand || + this._tabsRestoringCount >= MAX_CONCURRENT_TAB_RESTORES) return; // Look in visible, then hidden let nextTabArray; if (this._tabsToRestore.visible.length) { nextTabArray = this._tabsToRestore.visible; } else if (this._restoreHiddenTabs && this._tabsToRestore.hidden.length) { @@ -3967,26 +3990,33 @@ SessionStoreService.prototype = { function exclude(key, value) { // returning undefined results in the exclusion of that key return internalKeys.indexOf(key) == -1 ? value : undefined; } return JSON.stringify(aJSObject, exclude); }, _sendRestoreCompletedNotifications: function sss_sendRestoreCompletedNotifications() { - if (this._restoreCount) { + // not all windows restored, yet + if (this._restoreCount > 1) { this._restoreCount--; - if (this._restoreCount == 0) { - // This was the last window restored at startup, notify observers. - Services.obs.notifyObservers(null, - this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED, - ""); - this._browserSetState = false; - } + return; } + + // observers were already notified + if (this._restoreCount == -1) + return; + + // This was the last window restored at startup, notify observers. + Services.obs.notifyObservers(null, + this._browserSetState ? NOTIFY_BROWSER_STATE_RESTORED : NOTIFY_WINDOWS_RESTORED, + ""); + + this._browserSetState = false; + this._restoreCount = -1; }, /** * Set the given window's busy state * @param aWindow the window * @param aValue the window's busy state */ _setWindowStateBusyValue:
--- a/browser/components/sessionstore/test/browser/Makefile.in +++ b/browser/components/sessionstore/test/browser/Makefile.in @@ -144,16 +144,17 @@ include $(topsrcdir)/config/rules.mk browser_615394-SSWindowState_events.js \ browser_618151.js \ browser_623779.js \ browser_624727.js \ browser_625257.js \ browser_628270.js \ browser_635418.js \ browser_636279.js \ + browser_645428.js \ browser_659591.js \ browser_662812.js \ $(NULL) ifneq ($(OS_ARCH),Darwin) _BROWSER_TEST_FILES += \ browser_597071.js \ browser_625016.js \
--- a/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js +++ b/browser/components/sessionstore/test/browser/browser_586068-cascaded_restore.js @@ -55,17 +55,17 @@ function test() { let tests = [test_cascade, test_select, test_multiWindowState, test_setWindowStateNoOverwrite, test_setWindowStateOverwrite, test_setBrowserStateInterrupted, test_reload, /* test_reloadReload, */ test_reloadCascadeSetup, /* test_reloadCascade */]; function runNextTest() { // Reset the pref try { - Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs"); + Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand"); } catch (e) {} // set an empty state & run the next test, or finish if (tests.length) { // Enumerate windows and close everything but our primary window. We can't // use waitForFocus() because apparently it's buggy. See bug 599253. var windowsEnum = Services.wm.getEnumerator("navigator:browser"); while (windowsEnum.hasMoreElements()) { @@ -83,19 +83,16 @@ function runNextTest() { else { ss.setBrowserState(stateBackup); executeSoon(finish); } } function test_cascade() { - // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1); - // We have our own progress listener for this test, which we'll attach before our state is set let progressListener = { onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { dump("\n\nload: " + aBrowser.currentURI.spec + "\n" + JSON.stringify(countTabs()) + "\n\n"); if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING && aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) @@ -113,21 +110,21 @@ function test_cascade() { ] }] }; let loadCount = 0; // Since our progress listener is fired before the one in sessionstore, our // expected counts look a little weird. This is because we inspect the state // before sessionstore has marked the tab as finished restoring and before it // starts restoring the next tab let expectedCounts = [ - [5, 1, 0], - [4, 1, 1], - [3, 1, 2], - [2, 1, 3], - [1, 1, 4], + [3, 3, 0], + [2, 3, 1], + [1, 3, 2], + [0, 3, 3], + [0, 2, 4], [0, 1, 5] ]; function test_cascade_progressCallback() { loadCount++; let counts = countTabs(); let expected = expectedCounts[loadCount - 1]; @@ -144,19 +141,19 @@ function test_cascade() { // This progress listener will get attached before the listener in session store. window.gBrowser.addTabsProgressListener(progressListener); ss.setBrowserState(JSON.stringify(state)); } function test_select() { - // Set the pref to 0 so we know exactly how many tabs should be restoring at + // Set the pref to true so we know exactly how many tabs should be restoring at // any given time. This guarantees that a finishing load won't start another. - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0); + Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true); // We have our own progress listener for this test, which we'll attach before our state is set let progressListener = { onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING && aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) @@ -293,19 +290,16 @@ function test_multiWindowState() { Services.ww.registerNotification(windowObserver); window.gBrowser.addTabsProgressListener(progressListener); ss.setBrowserState(JSON.stringify(state)); } function test_setWindowStateNoOverwrite() { - // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1); - // We have our own progress listener for this test, which we'll attach before our state is set let progressListener = { onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { // We only care about load events when the tab still has // __SS_restoreState == TAB_STATE_RESTORING on it. // Since our listener is attached before the sessionstore one, this works out. if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING && aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && @@ -365,19 +359,16 @@ function test_setWindowStateNoOverwrite( } window.gBrowser.addTabsProgressListener(progressListener); ss.setWindowState(window, JSON.stringify(state1), true); } function test_setWindowStateOverwrite() { - // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1); - // We have our own progress listener for this test, which we'll attach before our state is set let progressListener = { onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { // We only care about load events when the tab still has // __SS_restoreState == TAB_STATE_RESTORING on it. // Since our listener is attached before the sessionstore one, this works out. if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING && aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && @@ -437,19 +428,16 @@ function test_setWindowStateOverwrite() } window.gBrowser.addTabsProgressListener(progressListener); ss.setWindowState(window, JSON.stringify(state1), true); } function test_setBrowserStateInterrupted() { - // Set the pref to 1 so we know exactly how many tabs should be restoring at any given time - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 1); - // We have our own progress listener for this test, which we'll attach before our state is set let progressListener = { onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { // We only care about load events when the tab still has // __SS_restoreState == TAB_STATE_RESTORING on it. // Since our listener is attached before the sessionstore one, this works out. if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING && aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && @@ -556,19 +544,19 @@ function test_setBrowserStateInterrupted Services.ww.registerNotification(windowObserver); window.gBrowser.addTabsProgressListener(progressListener); ss.setBrowserState(JSON.stringify(state1)); } function test_reload() { - // Set the pref to 0 so we know exactly how many tabs should be restoring at + // Set the pref to true so we know exactly how many tabs should be restoring at // any given time. This guarantees that a finishing load won't start another. - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0); + Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true); // We have our own progress listener for this test, which we'll attach before our state is set let progressListener = { onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING && aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
--- a/browser/components/sessionstore/test/browser/browser_599909.js +++ b/browser/components/sessionstore/test/browser/browser_599909.js @@ -38,29 +38,29 @@ const TAB_STATE_NEEDS_RESTORE = 1; const TAB_STATE_RESTORING = 2; let stateBackup = ss.getBrowserState(); function cleanup() { // Reset the pref try { - Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs"); + Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand"); } catch (e) {} ss.setBrowserState(stateBackup); executeSoon(finish); } function test() { /** Bug 599909 - to-be-reloaded tabs don't show up in switch-to-tab **/ waitForExplicitFinish(); - // Set the pref to 0 so we know exactly how many tabs should be restoring at + // Set the pref to true so we know exactly how many tabs should be restoring at // any given time. This guarantees that a finishing load won't start another. - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0); + Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true); let state = { windows: [{ tabs: [ { entries: [{ url: "http://example.org/#1" }] }, { entries: [{ url: "http://example.org/#2" }] }, { entries: [{ url: "http://example.org/#3" }] }, { entries: [{ url: "http://example.org/#4" }] } ], selected: 1 }] };
--- a/browser/components/sessionstore/test/browser/browser_607016.js +++ b/browser/components/sessionstore/test/browser/browser_607016.js @@ -38,29 +38,29 @@ const TAB_STATE_NEEDS_RESTORE = 1; const TAB_STATE_RESTORING = 2; let stateBackup = ss.getBrowserState(); function cleanup() { // Reset the pref try { - Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs"); + Services.prefs.clearUserPref("browser.sessionstore.restore_on_demand"); } catch (e) {} ss.setBrowserState(stateBackup); executeSoon(finish); } function test() { /** Bug 607016 - If a tab is never restored, attributes (eg. hidden) aren't updated correctly **/ waitForExplicitFinish(); - // Set the pref to 0 so we know exactly how many tabs should be restoring at + // Set the pref to true so we know exactly how many tabs should be restoring at // any given time. This guarantees that a finishing load won't start another. - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 0); + Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true); // We have our own progress listener for this test, which we'll attach before our state is set let progressListener = { onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) { if (aBrowser.__SS_restoreState == TAB_STATE_RESTORING && aStateFlags & Ci.nsIWebProgressListener.STATE_STOP && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK && aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
--- a/browser/components/sessionstore/test/browser/browser_636279.js +++ b/browser/components/sessionstore/test/browser/browser_636279.js @@ -8,43 +8,42 @@ let stateBackup = ss.getBrowserState(); let statePinned = {windows:[{tabs:[ {entries:[{url:"http://example.com#1"}], pinned: true} ]}]}; let state = {windows:[{tabs:[ {entries:[{url:"http://example.com#1"}]}, {entries:[{url:"http://example.com#2"}]}, - {entries:[{url:"http://example.com#3"}]} + {entries:[{url:"http://example.com#3"}]}, + {entries:[{url:"http://example.com#4"}]}, ]}]}; function test() { waitForExplicitFinish(); registerCleanupFunction(function () { - Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs"); TabsProgressListener.uninit(); ss.setBrowserState(stateBackup); }); - Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 2); TabsProgressListener.init(); window.addEventListener("SSWindowStateReady", function onReady() { window.removeEventListener("SSWindowStateReady", onReady, false); let firstProgress = true; TabsProgressListener.setCallback(function (needsRestore, isRestoring) { if (firstProgress) { firstProgress = false; - is(isRestoring, 2, "restoring 2 tabs concurrently"); + is(isRestoring, 3, "restoring 3 tabs concurrently"); } else { - ok(isRestoring < 3, "restoring max. 2 tabs concurrently"); + ok(isRestoring <= 3, "restoring max. 2 tabs concurrently"); } if (0 == needsRestore) { TabsProgressListener.unsetCallback(); waitForFocus(finish); } });
new file mode 100644 --- /dev/null +++ b/browser/components/sessionstore/test/browser/browser_645428.js @@ -0,0 +1,22 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +const NOTIFICATION = "sessionstore-browser-state-restored"; + +function test() { + waitForExplicitFinish(); + + function observe(subject, topic, data) { + if (NOTIFICATION == topic) { + finish(); + ok(true, "TOPIC received"); + } + } + + Services.obs.addObserver(observe, NOTIFICATION, false); + registerCleanupFunction(function () { + Services.obs.removeObserver(observe, NOTIFICATION, false); + }); + + ss.setBrowserState(JSON.stringify({ windows: [] })); +}
--- a/browser/devtools/sourceeditor/test/Makefile.in +++ b/browser/devtools/sourceeditor/test/Makefile.in @@ -43,9 +43,9 @@ relativesrcdir = browser/devtools/source include $(DEPTH)/config/autoconf.mk include $(topsrcdir)/config/rules.mk _BROWSER_TEST_FILES = \ browser_sourceeditor_initialization.js \ libs:: $(_BROWSER_TEST_FILES) - $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) +# $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/locales/en-US/chrome/browser/preferences/main.dtd +++ b/browser/locales/en-US/chrome/browser/preferences/main.dtd @@ -1,15 +1,18 @@ <!ENTITY startup.label "Startup"> <!ENTITY startupPage.label "When &brandShortName; starts:"> <!ENTITY startupPage.accesskey "s"> <!ENTITY startupHomePage.label "Show my home page"> <!ENTITY startupBlankPage.label "Show a blank page"> <!ENTITY startupLastSession.label "Show my windows and tabs from last time"> +<!ENTITY restoreOnDemand.label "Don’t load tabs until selected"> +<!ENTITY restoreOnDemand.accesskey "l"> + <!ENTITY homepage.label "Home Page:"> <!ENTITY homepage.accesskey "P"> <!ENTITY useCurrentPage.label "Use Current Page"> <!ENTITY useCurrentPage.accesskey "C"> <!ENTITY useMultiple.label "Use Current Pages"> <!ENTITY useBookmark.label "Use Bookmark"> <!ENTITY useBookmark.accesskey "B"> <!ENTITY restoreDefault.label "Restore to Default">
--- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -963,16 +963,24 @@ toolbar[mode="full"] .toolbarbutton-1 > /* tabview button */ #tabview-button, #menu_tabview { list-style-image: url(chrome://browser/skin/tabview/tabview.png); } +%ifdef WINSTRIPE_AERO +#TabsToolbar > #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +#TabsToolbar > toolbarpaletteitem > #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +%endif +#tabview-button:-moz-lwtheme-brighttext { + list-style-image: url(chrome://browser/skin/tabview/tabview-inverted.png); +} + #tabview-button { -moz-image-region: rect(0, 90px, 18px, 72px); } #tabview-button[groups="0"] { -moz-image-region: rect(0, 18px, 18px, 0); } @@ -1880,33 +1888,36 @@ richlistitem[type~="action"][actiontype= .tab-content { min-height: -moz-calc(6.8mozmm - 7px); /* subtract borders from the desired height */ } } .tabbrowser-arrowscrollbox > .scrollbutton-up, .tabbrowser-arrowscrollbox > .scrollbutton-down { list-style-image: url("chrome://browser/skin/tabbrowser/tab-arrow-left.png"); - -moz-image-region: rect(0, 15px, 17px, 0); margin: 0; padding-right: 2px; border-right: 2px solid transparent; background-origin: border-box; } +%ifdef WINSTRIPE_AERO +.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +%endif +.tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-lwtheme-brighttext, +.tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-lwtheme-brighttext { + list-style-image: url(chrome://browser/skin/tabbrowser/tab-arrow-left-inverted.png); +} + .tabbrowser-arrowscrollbox > .scrollbutton-up[disabled], .tabbrowser-arrowscrollbox > .scrollbutton-down[disabled] { opacity: .4; } -.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):hover:active, -.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):hover:active { - -moz-image-region: rect(0, 30px, 17px, 15px); -} - .tabbrowser-arrowscrollbox > .scrollbutton-up:-moz-locale-dir(rtl), .tabbrowser-arrowscrollbox > .scrollbutton-down:-moz-locale-dir(ltr) { -moz-transform: scaleX(-1); } .tabbrowser-arrowscrollbox > .scrollbutton-down { -moz-transition: 1s background-color ease-out; } @@ -1925,59 +1936,63 @@ richlistitem[type~="action"][actiontype= margin-top: -1px; margin-bottom: -1px; } .tabs-newtab-button, #TabsToolbar > #new-tab-button, #TabsToolbar > toolbarpaletteitem > #new-tab-button { list-style-image: url(chrome://browser/skin/tabbrowser/newtab.png); - -moz-image-region: rect(0, 16px, 18px, 0); + -moz-image-region: auto; +} + +%ifdef WINSTRIPE_AERO +#TabsToolbar > #new-tab-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +#TabsToolbar > toolbarpaletteitem > #new-tab-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +%endif +.tabs-newtab-button:-moz-lwtheme-brighttext, +#TabsToolbar > #new-tab-button:-moz-lwtheme-brighttext, +#TabsToolbar > toolbarpaletteitem > #new-tab-button:-moz-lwtheme-brighttext { + list-style-image: url(chrome://browser/skin/tabbrowser/newtab-inverted.png); } .tabs-newtab-button { width: 28px; } #TabsToolbar > #new-tab-button { width: 26px; } -.tabs-newtab-button:hover:active, -#TabsToolbar > #new-tab-button:hover:active { - -moz-image-region: rect(0, 32px, 18px, 16px); -} - #alltabs-button { list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png"); -moz-image-region: rect(0, 14px, 16px, 0); } -#alltabs-button:hover:active { - -moz-image-region: rect(0, 28px, 16px, 14px); -} - #alltabs-button[type="menu"] { list-style-image: url("chrome://browser/skin/mainwindow-dropdown-arrow.png"); -moz-image-region: rect(0, 13px, 11px, 0); } +%ifdef WINSTRIPE_AERO +#TabsToolbar > #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +#TabsToolbar > toolbarpaletteitem > #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme), +%endif +#alltabs-button[type="menu"]:-moz-lwtheme-brighttext { + list-style-image: url(chrome://browser/skin/mainwindow-dropdown-arrow-inverted.png); +} + #alltabs-button[type="menu"] > .toolbarbutton-icon { margin: 3px 0; } #alltabs-button[type="menu"] > .toolbarbutton-menu-dropmarker { display: none; } -#alltabs-button[type="menu"]:hover:active, -#alltabs-button[type="menu"][open="true"] { - -moz-image-region: rect(0, 26px, 11px, 13px); -} - /* All tabs menupopup */ .alltabs-item > .menu-iconic-left > .menu-iconic-icon { list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png"); } .alltabs-item[selected="true"] { font-weight: bold; }
--- a/browser/themes/winstripe/browser/jar.mn +++ b/browser/themes/winstripe/browser/jar.mn @@ -20,16 +20,17 @@ browser.jar: skin/classic/browser/Geolocation-16.png skin/classic/browser/Geolocation-64.png skin/classic/browser/Info.png (Info.png) skin/classic/browser/identity.png (identity.png) skin/classic/browser/keyhole-forward-mask.svg skin/classic/browser/KUI-background.png skin/classic/browser/KUI-close.png skin/classic/browser/mainwindow-dropdown-arrow.png + skin/classic/browser/mainwindow-dropdown-arrow-inverted.png skin/classic/browser/pageInfo.css skin/classic/browser/pageInfo.png (pageInfo.png) skin/classic/browser/page-livemarks.png (feeds/feedIcon16.png) skin/classic/browser/livemark-item.png (livemark-item.png) skin/classic/browser/livemark-folder.png (livemark-folder.png) skin/classic/browser/Privacy-16.png skin/classic/browser/Privacy-48.png skin/classic/browser/reload-stop-go.png @@ -84,28 +85,31 @@ browser.jar: skin/classic/browser/preferences/Options-sync.png (preferences/Options-sync.png) #endif skin/classic/browser/preferences/saveFile.png (preferences/saveFile.png) * skin/classic/browser/preferences/preferences.css (preferences/preferences.css) skin/classic/browser/preferences/applications.css (preferences/applications.css) skin/classic/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css) skin/classic/browser/tabbrowser/alltabs.png (tabbrowser/alltabs.png) skin/classic/browser/tabbrowser/newtab.png (tabbrowser/newtab.png) + skin/classic/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png) skin/classic/browser/tabbrowser/connecting.png (tabbrowser/connecting.png) skin/classic/browser/tabbrowser/loading.png (tabbrowser/loading.png) skin/classic/browser/tabbrowser/tab.png (tabbrowser/tab.png) skin/classic/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png) + skin/classic/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png) skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png) skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png) skin/classic/browser/tabview/close.png (tabview/close.png) skin/classic/browser/tabview/edit-light.png (tabview/edit-light.png) skin/classic/browser/tabview/grain.png (tabview/grain.png) skin/classic/browser/tabview/search.png (tabview/search.png) skin/classic/browser/tabview/stack-expander.png (tabview/stack-expander.png) skin/classic/browser/tabview/tabview.png (tabview/tabview.png) + skin/classic/browser/tabview/tabview-inverted.png (tabview/tabview-inverted.png) skin/classic/browser/tabview/tabview.css (tabview/tabview.css) #ifdef MOZ_SERVICES_SYNC skin/classic/browser/sync-throbber.png skin/classic/browser/sync-16.png skin/classic/browser/sync-32.png skin/classic/browser/sync-bg.png skin/classic/browser/sync-desktopIcon.png skin/classic/browser/sync-mobileIcon.png @@ -135,16 +139,17 @@ browser.jar: skin/classic/aero/browser/Geolocation-16.png skin/classic/aero/browser/Geolocation-64.png skin/classic/aero/browser/Info.png (Info-aero.png) skin/classic/aero/browser/identity.png (identity-aero.png) skin/classic/aero/browser/keyhole-forward-mask.svg skin/classic/aero/browser/KUI-background.png skin/classic/aero/browser/KUI-close.png skin/classic/aero/browser/mainwindow-dropdown-arrow.png (mainwindow-dropdown-arrow-aero.png) + skin/classic/aero/browser/mainwindow-dropdown-arrow-inverted.png skin/classic/aero/browser/pageInfo.css skin/classic/aero/browser/pageInfo.png (pageInfo-aero.png) skin/classic/aero/browser/page-livemarks.png (feeds/feedIcon16-aero.png) skin/classic/aero/browser/livemark-item.png (livemark-item-aero.png) skin/classic/aero/browser/livemark-folder.png (livemark-folder-aero.png) skin/classic/aero/browser/Privacy-16.png (Privacy-16-aero.png) skin/classic/aero/browser/Privacy-48.png (Privacy-48-aero.png) skin/classic/aero/browser/reload-stop-go.png @@ -200,28 +205,31 @@ browser.jar: #endif skin/classic/aero/browser/preferences/plugin.png (preferences/plugin-aero.png) skin/classic/aero/browser/preferences/saveFile.png (preferences/saveFile-aero.png) * skin/classic/aero/browser/preferences/preferences.css (preferences/preferences.css) skin/classic/aero/browser/preferences/applications.css (preferences/applications.css) skin/classic/aero/browser/preferences/aboutPermissions.css (preferences/aboutPermissions.css) skin/classic/aero/browser/tabbrowser/alltabs.png (tabbrowser/alltabs.png) skin/classic/aero/browser/tabbrowser/newtab.png (tabbrowser/newtab.png) + skin/classic/aero/browser/tabbrowser/newtab-inverted.png (tabbrowser/newtab-inverted.png) skin/classic/aero/browser/tabbrowser/connecting.png (tabbrowser/connecting.png) skin/classic/aero/browser/tabbrowser/loading.png (tabbrowser/loading.png) skin/classic/aero/browser/tabbrowser/tab.png (tabbrowser/tab.png) skin/classic/aero/browser/tabbrowser/tab-arrow-left.png (tabbrowser/tab-arrow-left.png) + skin/classic/aero/browser/tabbrowser/tab-arrow-left-inverted.png (tabbrowser/tab-arrow-left-inverted.png) skin/classic/aero/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png) skin/classic/aero/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png) skin/classic/aero/browser/tabview/close.png (tabview/close.png) skin/classic/aero/browser/tabview/edit-light.png (tabview/edit-light.png) skin/classic/aero/browser/tabview/grain.png (tabview/grain.png) skin/classic/aero/browser/tabview/search.png (tabview/search.png) skin/classic/aero/browser/tabview/stack-expander.png (tabview/stack-expander.png) skin/classic/aero/browser/tabview/tabview.png (tabview/tabview.png) + skin/classic/aero/browser/tabview/tabview-inverted.png (tabview/tabview-inverted.png) skin/classic/aero/browser/tabview/tabview.css (tabview/tabview.css) #ifdef MOZ_SERVICES_SYNC skin/classic/aero/browser/sync-throbber.png skin/classic/aero/browser/sync-16.png skin/classic/aero/browser/sync-32.png skin/classic/aero/browser/sync-bg.png skin/classic/aero/browser/sync-desktopIcon.png skin/classic/aero/browser/sync-mobileIcon.png
new file mode 100644 index 0000000000000000000000000000000000000000..ed1791641307105edbe69325356ffc00396dc87b GIT binary patch literal 230 zc%17D@N?(olHy`uVBq!ia0vp@K+MI#1|;*}T{!}zBuiW)N`mv#O3D+9QW+dm@{>{( zJaZG%Q-e|yQz{EjrrH1%b$GfshE&{2`t$$4J@d+j&PJXCOvwgDM!T3b6>YNaGsbQe zKEq_m|LVxS-{0Tw|I^RfaK!Ob!B&r1HySH`&9yGSr;yF?(eaT&WzMV_jS&JJ&+qQ5 z{jKZ4+`;y7;iEG*OcFC(t~ERoHSs+mm9CKOaP0`kWd<JGlHLUq{xTn_pJ>{6b&~K- c21Yi9T|yICd*k;#0=kL8)78&qol`;+0N3GKFaQ7m
new file mode 100644 index 0000000000000000000000000000000000000000..4ac1ebaaf0d7c9d8d1efe4b7b6545b07ed3f121d GIT binary patch literal 247 zc%17D@N?(olHy`uVBq!ia0vp^0zfRp!3HFQtmCqPlw^r(L`iUdT1k0gQ7VIDN`6wR zf@f}GdTLN=VoGJ<$y6JlqM4p9jv*DdlK%YvZ_ljSQ20UOz=;zbdgA;?!UDb-D#Uf@ zaBi14a^PXt%>N4+j<`*4d6}HFKp-H8ft$}h`KyqSw4j<tBExrMMTSuCxdsm34rzW_ z!E$Q?TkoUDj9-3<F6;sZ%!c9upPdY=Ob#7!%$kw-YeMV?@s9lu3*V;Ca#yx;;F}!$ u<OyrQj1A5=?H9%!Il;|5e=Z{v4};&~ZU?t*muCVU$>8bg=d#Wzp$Pz4=2^`E
index 404f3af1cafe487d668a64e062d9e4c79d1b2882..c25c98053b045908ecb130bac2bebab64dbef7bb GIT binary patch literal 445 zc$@*f0Yd(XP)<h;3K|Lk000e1NJLTq000mG000sQ1^@s6QafI100001b5ch_0Itp) z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iph) z1~44N^^DX200BKoL_t(I%gvI_O2bePMo-JNY0?IXU1(}46!Hi@f#9OjMVEr$w$J2N z5PSkRYWJ1+28DtYS{Egnq<;`o7v7fJm|%B0%b9P!nK=ynb>z~L{gEdC1JGq^4$K_e zijrE9VQc^mr5*tTz)uSE83sBa+O4MBZZ%zLq^Ak9Hpl}U*BieM6Dc?6feJvOpsCtQ zf8+tgDie`$^Ect#7Gn*lCYj{q28TfH{Or^%l}!LjrpZyQ4#0T$$}|W7n1%uO58V@B z=-AfV)<oSf*tZS<C=`pVR&u?vi4c1L#PMe;#5oXkpB_7E@#6C88sP5sW-IT7i%9lt z3}(Q<3m0PrqUL|RuNz)~B`{GTZVcpy`RhlKw5!X-OdULNY-_aZAH)D*;Qz?&YA^?) nUhlb+ym?mxl?m$2|1Z7(W9M+*n^KJE00000NkvXXu0mjftlhTH
new file mode 100644 index 0000000000000000000000000000000000000000..16cd7a2775ae1f56274035fcde4ed5e8c211a52c GIT binary patch literal 250 zc%17D@N?(olHy`uVBq!ia0vp^{6H+o!3HFmxV|j}Qj#UE5hcO-X(i=}MX3yqDfvmM z3ZA)%>8U}fi7AzZCsS>JispE_IEGZ*O8WEvzdiHLhR#N&15IZVQ&NsBJI2|O+xWvk z;JtI1rsC|sjW?bjY-YD~xFwk-z|`Q=l&B;1``+H_@1h#t<ueXsE9{qZwdFgjA?0Y& z!M|zdgtQo`zlJeg%OuykInAoRVDe2l+vU2C#+IE85e&<9?9%_YbDUGTc2k5^;%K02 wp^~E^!>Wy<chlTbe3zv0xOsgtc4%N^kb38u%%>m}4Rj}ir>mdKI;Vst0OD<0yZ`_I
index 0ef2b1ae61e2d4396ac70fab94dab6a7b49ef1aa..e0fb348d66f4001a50ca9488b9daddcf37bbc3ac GIT binary patch literal 368 zc%17D@N?(olHy`uVBq!ia0vp^{6H+o!3-o#udq!9QjEnx?oJHr&dIz4a+U`8gt*>$ z{+>wm^265`@4u*NXsXrEQfr!{)-q4q*h1gJ)_wl=!0;GTD?8Kv<(3mySWQ}K<Lu_- z>Ejm?5ioyqP()17{LLZD_l7Rq8kvw1o0J}#oSu-Lm6Vmw*|v;ZNL-+Eg<#)0A!#`= zIYqIVdn6`qkW<rES#Utd#7f`X#=y$n$ja_^()*tv*OvtO1v5B2yO9RsBze2LFm$lW zdH^|`1s;*b3=G`DAk4@xYmNj^kiEpy*OmP?2Me2lnY^DX6Hv(9)5S4F;&O7r0wDnb zho@Pd3kph~2JEQf(ui>u6*+5^_Gm@h>RBC&xK_+w#F-({y?|@Q$w^On88&`r?tb+} Q0%R(Kr>mdKI;Vst0Co0^sQ>@~
new file mode 100644 index 0000000000000000000000000000000000000000..0211d8b650b3db4a8d73e42367aeee6a0b7873fa GIT binary patch literal 524 zc$@(Y0`vWeP)<h;3K|Lk000e1NJLTq003G5000sQ1^@s6+oQ<?0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUzu1Q2eRCwC#n7>ZLFc8LLRH;+D(+x2< zTLMyHNGAp)-Xcsq5IhVFKtz)vQZh#jROuR}17a$7(z!C0o$@C^QFWG1=X1XPQaOqJ z$zrqFINZ8#2RsG;(+095ggAi|Kpa`LuMp1=GtyxF0AIvH6*0%m87RUaj$_C3yp@TP zB#Gnu{v0$PsG%BfywIdUUDsEm(dg8~S+CbG<MDXd76&vJsG-{Fl9OfG&2&1Qm^gW! zm#*s$K@*~e>McjeH5#7SE%aASuRmxGRSnhGng%)H4$fVRoKUNK$>BAcH^cp%=pu@u zcbkx|Ev1HXVUQM8RXy1_bgeax)KKn}lcwpLjYHR#QbT#mQP=2mNOOqEHy=_7aSy+L z)Yd8H7&z)1Jwqpen-m80(Vmo;<(L|%B`c%1-f_@T;3kCuefPH<bQF>U{Y&IvmSbw5 zR^>Fh4&7x~T!?9R?i(j;ay@_KggaaayAF08Jki`*w|Baov)DbO4eA?5wRgI`{0T?3 zce<Uk*gd1Y^XSmzSb`z&Pn{`aUv5l?CW*EIFvq|Vd>+kf^j@`30R{lJMYCZ;;phnf O0000<MNUMnLSTa0Wan!D
--- a/caps/src/nsNullPrincipalURI.cpp +++ b/caps/src/nsNullPrincipalURI.cpp @@ -197,16 +197,30 @@ nsNullPrincipalURI::SetScheme(const nsAC NS_IMETHODIMP nsNullPrincipalURI::GetSpec(nsACString &_spec) { _spec = mScheme + NS_LITERAL_CSTRING(":") + mPath; return NS_OK; } +// result may contain unescaped UTF-8 characters +NS_IMETHODIMP +nsNullPrincipalURI::GetSpecIgnoringRef(nsACString &result) +{ + return GetSpec(result); +} + +NS_IMETHODIMP +nsNullPrincipalURI::GetHasRef(PRBool *result) +{ + *result = PR_FALSE; + return NS_OK; +} + NS_IMETHODIMP nsNullPrincipalURI::SetSpec(const nsACString &aSpec) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsNullPrincipalURI::GetUsername(nsACString &_username)
--- a/config/config.mk +++ b/config/config.mk @@ -83,16 +83,19 @@ check-variable = $(if $(filter-out 0 1,$ core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))) nullstr := space :=$(nullstr) # EOL core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1))))))) +# LIBXUL_DIST is not defined under js/src, thus we make it mean DIST there. +LIBXUL_DIST ?= $(DIST) + # FINAL_TARGET specifies the location into which we copy end-user-shipped # build products (typelibs, components, chrome). # # It will usually be the well-loved $(DIST)/bin, today, but can also be an # XPI-contents staging directory for ambitious and right-thinking extensions. FINAL_TARGET = $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin) ifdef XPI_NAME @@ -738,20 +741,20 @@ MERGE_FILE = $(firstword \ $(wildcard $(LOCALE_SRCDIR)/$(1)) \ $(srcdir)/en-US/$(1) ) else MERGE_FILE = $(LOCALE_SRCDIR)/$(1) endif MERGE_FILES = $(foreach f,$(1),$(call MERGE_FILE,$(f))) ifeq (OS2,$(OS_ARCH)) -RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(DIST)" +RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(LIBXUL_DIST)" else ifneq (WINNT,$(OS_ARCH)) -RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh +RUN_TEST_PROGRAM = $(LIBXUL_DIST)/bin/run-mozilla.sh endif # ! WINNT endif # ! OS2 # # Java macros # # Make sure any compiled classes work with at least JVM 1.4
--- a/configure.in +++ b/configure.in @@ -9317,17 +9317,17 @@ xpcom/xpcom-private.h ) # Hack around an Apple bug that effects the egrep that comes with OS X 10.7. # "arch -arch i386 egrep" always uses the 32-bit Intel part of the egrep fat # binary, even on 64-bit systems. It should work on OS X 10.4.5 and up. We # (apparently) only need this hack when egrep's "pattern" is particularly # long (as in the following code). See bug 655339. case "$host" in -x86_64-apple-darwin*) +*-apple-darwin*) FIXED_EGREP="arch -arch i386 egrep" ;; *) FIXED_EGREP="egrep" ;; esac # Save the defines header file before autoconf removes it. @@ -9361,16 +9361,26 @@ fi cat >> $_CONFIG_TMP <<\EOF /* The c99 defining the limit macros (UINT32_MAX for example), says: * C++ implementations should define these macros only when __STDC_LIMIT_MACROS * is defined before <stdint.h> is included. */ #define __STDC_LIMIT_MACROS +/* Force-include hunspell_alloc_hooks.h for hunspell, so that we don't need to + * modify it directly. + * + * HUNSPELL_STATIC is defined in extensions/spellcheck/hunspell/src/Makefile.in, + * unless --enable-system-hunspell is defined. + */ +#if defined(HUNSPELL_STATIC) +#include "hunspell_alloc_hooks.h" +#endif + #endif /* _MOZILLA_CONFIG_H_ */ EOF # Only write mozilla-config.h when something changes (or it doesn't exist) if cmp -s $_CONFIG_TMP $_CONFIG_DEFS_H; then rm $_CONFIG_TMP else
--- a/content/base/public/nsIMutationObserver.h +++ b/content/base/public/nsIMutationObserver.h @@ -88,16 +88,33 @@ struct CharacterDataChangeInfo /** * The net result is that mChangeStart characters at the beginning of the * text remained as they were. The next mChangeEnd - mChangeStart characters * were removed, and mReplaceLength characters were inserted in their place. * The text that used to begin at mChangeEnd now begins at * mChangeStart + mReplaceLength. */ + + struct Details { + enum { + eMerge, // two text nodes are merged as a result of normalize() + eSplit // a text node is split as a result of splitText() + } mType; + /** + * For eMerge it's the text node that will be removed, for eSplit it's the + * new text node. + */ + nsIContent* mNextSibling; + }; + + /** + * Used for splitText() and normalize(), otherwise null. + */ + Details* mDetails; }; /** * Mutation observer interface * * See nsINode::AddMutationObserver, nsINode::RemoveMutationObserver for how to * attach or remove your observers. *
--- a/content/base/src/nsDOMFile.cpp +++ b/content/base/src/nsDOMFile.cpp @@ -143,31 +143,38 @@ NS_INTERFACE_MAP_BEGIN(nsDOMFileBase) NS_INTERFACE_MAP_ENTRY(nsIDOMBlob) NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIDOMFile, mIsFile) NS_INTERFACE_MAP_ENTRY(nsIXHRSendable) NS_INTERFACE_MAP_ENTRY(nsIMutable) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(File, mIsFile) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO_CONDITIONAL(Blob, !mIsFile) NS_INTERFACE_MAP_END -NS_IMPL_ADDREF(nsDOMFileBase) -NS_IMPL_RELEASE(nsDOMFileBase) +// Threadsafe when GetMutable() == PR_FALSE +NS_IMPL_THREADSAFE_ADDREF(nsDOMFileBase) +NS_IMPL_THREADSAFE_RELEASE(nsDOMFileBase) NS_IMETHODIMP nsDOMFileBase::GetName(nsAString &aFileName) { NS_ASSERTION(mIsFile, "Should only be called on files"); aFileName = mName; return NS_OK; } NS_IMETHODIMP nsDOMFileBase::GetMozFullPath(nsAString &aFileName) { NS_ASSERTION(mIsFile, "Should only be called on files"); + + // It is unsafe to call IsCallerTrustedForCapability on a non-main thread. If + // you hit the following assertion you need to figure out some other way to + // determine privileges and call GetMozFullPathInternal. + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + if (nsContentUtils::IsCallerTrustedForCapability("UniversalFileRead")) { return GetMozFullPathInternal(aFileName); } aFileName.Truncate(); return NS_OK; } NS_IMETHODIMP
--- a/content/base/src/nsGenericDOMDataNode.cpp +++ b/content/base/src/nsGenericDOMDataNode.cpp @@ -282,17 +282,18 @@ nsGenericDOMDataNode::ReplaceData(PRUint { return SetTextInternal(aOffset, aCount, aData.BeginReading(), aData.Length(), PR_TRUE); } nsresult nsGenericDOMDataNode::SetTextInternal(PRUint32 aOffset, PRUint32 aCount, const PRUnichar* aBuffer, - PRUint32 aLength, PRBool aNotify) + PRUint32 aLength, PRBool aNotify, + CharacterDataChangeInfo::Details* aDetails) { NS_PRECONDITION(aBuffer || !aLength, "Null buffer passed to SetTextInternal!"); // sanitize arguments PRUint32 textLength = mText.GetLength(); if (aOffset > textLength) { return NS_ERROR_DOM_INDEX_SIZE_ERR; @@ -325,17 +326,18 @@ nsGenericDOMDataNode::SetTextInternal(PR oldValue = GetCurrentValueAtom(); } if (aNotify) { CharacterDataChangeInfo info = { aOffset == textLength, aOffset, endOffset, - aLength + aLength, + aDetails }; nsNodeUtils::CharacterDataWillChange(this, &info); } if (aOffset == 0 && endOffset == textLength) { // Replacing whole text or old text was empty mText.SetTo(aBuffer, aLength); } @@ -371,17 +373,18 @@ nsGenericDOMDataNode::SetTextInternal(PR UpdateBidiStatus(aBuffer, aLength); // Notify observers if (aNotify) { CharacterDataChangeInfo info = { aOffset == textLength, aOffset, endOffset, - aLength + aLength, + aDetails }; nsNodeUtils::CharacterDataChanged(this, &info); if (haveMutationListeners) { nsMutationEvent mutation(PR_TRUE, NS_MUTATION_CHARACTERDATAMODIFIED); mutation.mPrevAttrValue = oldValue; if (aLength > 0) { @@ -739,35 +742,33 @@ nsGenericDOMDataNode::SplitData(PRUint32 PRUint32 cutStartOffset = aCloneAfterOriginal ? aOffset : 0; PRUint32 cutLength = aCloneAfterOriginal ? length - aOffset : aOffset; rv = SubstringData(cutStartOffset, cutLength, cutText); if (NS_FAILED(rv)) { return rv; } - rv = DeleteData(cutStartOffset, cutLength); + // Use Clone for creating the new node so that the new node is of same class + // as this node! + nsCOMPtr<nsIContent> newContent = CloneDataNode(mNodeInfo, PR_FALSE); + if (!newContent) { + return NS_ERROR_OUT_OF_MEMORY; + } + newContent->SetText(cutText, PR_TRUE); // XXX should be PR_FALSE? + + CharacterDataChangeInfo::Details details = { + CharacterDataChangeInfo::Details::eSplit, newContent + }; + rv = SetTextInternal(cutStartOffset, cutLength, nsnull, 0, PR_TRUE, &details); if (NS_FAILED(rv)) { return rv; } - /* - * Use Clone for creating the new node so that the new node is of same class - * as this node! - */ - - nsCOMPtr<nsIContent> newContent = CloneDataNode(mNodeInfo, PR_FALSE); - if (!newContent) { - return NS_ERROR_OUT_OF_MEMORY; - } - - newContent->SetText(cutText, PR_TRUE); - nsCOMPtr<nsINode> parent = GetNodeParent(); - if (parent) { PRInt32 insertionIndex = parent->IndexOf(this); if (aCloneAfterOriginal) { ++insertionIndex; } parent->InsertChildAt(newContent, insertionIndex, PR_TRUE); }
--- a/content/base/src/nsGenericDOMDataNode.h +++ b/content/base/src/nsGenericDOMDataNode.h @@ -342,17 +342,18 @@ protected: PRInt32 aIndex); static PRInt32 LastLogicallyAdjacentTextNode(nsIContent* aParent, PRInt32 aIndex, PRUint32 aCount); nsresult SetTextInternal(PRUint32 aOffset, PRUint32 aCount, const PRUnichar* aBuffer, PRUint32 aLength, - PRBool aNotify); + PRBool aNotify, + CharacterDataChangeInfo::Details* aDetails = nsnull); /** * Method to clone this node. This needs to be overriden by all derived * classes. If aCloneText is true the text content will be cloned too. * * @param aOwnerDocument the ownerDocument of the clone * @param aCloneText if true the text content will be cloned too * @return the clone
--- a/content/base/src/nsGenericElement.cpp +++ b/content/base/src/nsGenericElement.cpp @@ -125,16 +125,17 @@ #include "nsIView.h" #include "nsIViewManager.h" #include "nsIScrollableFrame.h" #include "nsXBLInsertionPoint.h" #include "mozilla/css/StyleRule.h" /* For nsCSSSelectorList */ #include "nsCSSRuleProcessor.h" #include "nsRuleProcessorData.h" #include "nsPLDOMEvent.h" +#include "nsTextNode.h" #ifdef MOZ_XUL #include "nsIXULDocument.h" #endif /* MOZ_XUL */ #include "nsCycleCollectionParticipant.h" #include "nsCCUncollectableMarker.h" @@ -601,23 +602,24 @@ nsINode::Normalize() if (text->GetLength()) { nsIContent* target = node->GetPreviousSibling(); NS_ASSERTION((target && target->NodeType() == nsIDOMNode::TEXT_NODE) || hasRemoveListeners, "Should always have a previous text sibling unless " "mutation events messed us up"); if (!hasRemoveListeners || (target && target->NodeType() == nsIDOMNode::TEXT_NODE)) { + nsTextNode* t = static_cast<nsTextNode*>(target); if (text->Is2b()) { - target->AppendText(text->Get2b(), text->GetLength(), PR_TRUE); + t->AppendTextForNormalize(text->Get2b(), text->GetLength(), PR_TRUE, node); } else { tmpStr.Truncate(); text->AppendTo(tmpStr); - target->AppendText(tmpStr.get(), tmpStr.Length(), PR_TRUE); + t->AppendTextForNormalize(tmpStr.get(), tmpStr.Length(), PR_TRUE, node); } } } // Remove node nsINode* parent = node->GetNodeParent(); NS_ASSERTION(parent || hasRemoveListeners, "Should always have a parent unless "
--- a/content/base/src/nsRange.cpp +++ b/content/base/src/nsRange.cpp @@ -269,31 +269,68 @@ nsRange::CharacterDataChanged(nsIDocumen nsIContent* aContent, CharacterDataChangeInfo* aInfo) { NS_ASSERTION(mIsPositioned, "shouldn't be notified if not positioned"); // If the changed node contains our start boundary and the change starts // before the boundary we'll need to adjust the offset. if (aContent == mStartParent && - aInfo->mChangeStart < (PRUint32)mStartOffset) { - // If boundary is inside changed text, position it before change - // else adjust start offset for the change in length - mStartOffset = (PRUint32)mStartOffset <= aInfo->mChangeEnd ? - aInfo->mChangeStart : - mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd + - aInfo->mReplaceLength; + aInfo->mChangeStart < static_cast<PRUint32>(mStartOffset)) { + if (aInfo->mDetails) { + // splitText(), aInfo->mDetails->mNextSibling is the new text node + NS_ASSERTION(aInfo->mDetails->mType == + CharacterDataChangeInfo::Details::eSplit, + "only a split can start before the end"); + NS_ASSERTION(static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd, + "mStartOffset is beyond the end of this node"); + mStartOffset = static_cast<PRUint32>(mStartOffset) - aInfo->mChangeStart; + mStartParent = aInfo->mDetails->mNextSibling; + } else { + // If boundary is inside changed text, position it before change + // else adjust start offset for the change in length. + mStartOffset = static_cast<PRUint32>(mStartOffset) <= aInfo->mChangeEnd ? + aInfo->mChangeStart : + mStartOffset + aInfo->mChangeStart - aInfo->mChangeEnd + + aInfo->mReplaceLength; + } } // Do the same thing for the end boundary. - if (aContent == mEndParent && aInfo->mChangeStart < (PRUint32)mEndOffset) { - mEndOffset = (PRUint32)mEndOffset <= aInfo->mChangeEnd ? - aInfo->mChangeStart : - mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd + - aInfo->mReplaceLength; + if (aContent == mEndParent && aInfo->mChangeStart < static_cast<PRUint32>(mEndOffset)) { + if (aInfo->mDetails) { + // splitText(), aInfo->mDetails->mNextSibling is the new text node + NS_ASSERTION(aInfo->mDetails->mType == + CharacterDataChangeInfo::Details::eSplit, + "only a split can start before the end"); + NS_ASSERTION(static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd, + "mEndOffset is beyond the end of this node"); + mEndOffset = static_cast<PRUint32>(mEndOffset) - aInfo->mChangeStart; + mEndParent = aInfo->mDetails->mNextSibling; + } else { + mEndOffset = static_cast<PRUint32>(mEndOffset) <= aInfo->mChangeEnd ? + aInfo->mChangeStart : + mEndOffset + aInfo->mChangeStart - aInfo->mChangeEnd + + aInfo->mReplaceLength; + } + } + + if (aInfo->mDetails && + aInfo->mDetails->mType == CharacterDataChangeInfo::Details::eMerge) { + // normalize(), aInfo->mDetails->mNextSibling is the merged text node + // that will be removed + nsIContent* removed = aInfo->mDetails->mNextSibling; + if (removed == mStartParent) { + mStartOffset = static_cast<PRUint32>(mStartOffset) + aInfo->mChangeStart; + mStartParent = aContent; + } + if (removed == mEndParent) { + mEndOffset = static_cast<PRUint32>(mEndOffset) + aInfo->mChangeStart; + mEndParent = aContent; + } } } void nsRange::ContentInserted(nsIDocument* aDocument, nsIContent* aContainer, nsIContent* aChild, PRInt32 aIndexInContainer)
--- a/content/base/src/nsTextNode.cpp +++ b/content/base/src/nsTextNode.cpp @@ -200,16 +200,26 @@ nsTextNode::UnbindFromAttribute() NS_ASSERTION(GetNodeParent(), "Bind before unbinding!"); NS_ASSERTION(GetNodeParent() && GetNodeParent()->IsNodeOfType(nsINode::eATTRIBUTE), "Use this method only to unbind from an attribute!"); mParent = nsnull; return NS_OK; } +nsresult +nsTextNode::AppendTextForNormalize(const PRUnichar* aBuffer, PRUint32 aLength, + PRBool aNotify, nsIContent* aNextSibling) +{ + CharacterDataChangeInfo::Details details = { + CharacterDataChangeInfo::Details::eMerge, aNextSibling + }; + return SetTextInternal(mText.GetLength(), 0, aBuffer, aLength, aNotify, &details); +} + #ifdef DEBUG void nsTextNode::List(FILE* out, PRInt32 aIndent) const { PRInt32 index; for (index = aIndent; --index >= 0; ) fputs(" ", out); fprintf(out, "Text@%p", static_cast<const void*>(this));
--- a/content/base/src/nsTextNode.h +++ b/content/base/src/nsTextNode.h @@ -78,13 +78,16 @@ public: virtual nsGenericDOMDataNode* CloneDataNode(nsINodeInfo *aNodeInfo, PRBool aCloneText) const; nsresult BindToAttribute(nsIAttribute* aAttr); nsresult UnbindFromAttribute(); virtual nsXPCClassInfo* GetClassInfo(); + nsresult AppendTextForNormalize(const PRUnichar* aBuffer, PRUint32 aLength, + PRBool aNotify, nsIContent* aNextSibling); + #ifdef DEBUG virtual void List(FILE* out, PRInt32 aIndent) const; virtual void DumpContent(FILE* out, PRInt32 aIndent, PRBool aDumpAll) const; #endif };
--- a/content/canvas/src/WebGLContext.cpp +++ b/content/canvas/src/WebGLContext.cpp @@ -932,28 +932,35 @@ WebGLContext::MozGetUnderlyingParamStrin default: return NS_ERROR_INVALID_ARG; } return NS_OK; } +bool WebGLContext::IsExtensionSupported(WebGLExtensionID ei) +{ + if (ei == WebGL_OES_texture_float) { + MakeContextCurrent(); + return gl->IsExtensionSupported(gl->IsGLES2() ? GLContext::OES_texture_float + : GLContext::ARB_texture_float); + } + return false; +} + NS_IMETHODIMP WebGLContext::GetExtension(const nsAString& aName, nsIWebGLExtension **retval) { *retval = nsnull; // handle simple extensions that don't need custom objects first WebGLExtensionID ei = WebGLExtensionID_Max; if (aName.EqualsLiteral("OES_texture_float")) { - MakeContextCurrent(); - - PRBool avail = gl->IsExtensionSupported(gl->IsGLES2() ? "GL_OES_texture_float" : "GL_ARB_texture_float"); - if (avail) + if (IsExtensionSupported(WebGL_OES_texture_float)) ei = WebGL_OES_texture_float; } // create a WebGLExtension object for extensions that don't // have any additional tokens or methods if (ei != WebGLExtensionID_Max) { if (!IsExtensionEnabled(ei)) { mEnabledExtensions[ei] = new WebGLExtension(this); @@ -1240,17 +1247,18 @@ WebGLActiveInfo::GetName(nsAString & aNa NS_IMETHODIMP WebGLContext::GetSupportedExtensions(nsIVariant **retval) { nsCOMPtr<nsIWritableVariant> wrval = do_CreateInstance("@mozilla.org/variant;1"); NS_ENSURE_TRUE(wrval, NS_ERROR_FAILURE); nsTArray<const char *> extList; - /* no extensions to add to extList */ + if (IsExtensionSupported(WebGL_OES_texture_float)) + extList.InsertElementAt(extList.Length(), "OES_texture_float"); nsresult rv; if (extList.Length() > 0) { rv = wrval->SetAsArray(nsIDataType::VTYPE_CHAR_STR, nsnull, extList.Length(), &extList[0]); } else { rv = wrval->SetAsEmptyArray(); }
--- a/content/canvas/src/WebGLContext.h +++ b/content/canvas/src/WebGLContext.h @@ -467,16 +467,17 @@ protected: WebGL_OES_texture_float, WebGLExtensionID_Max }; nsCOMPtr<nsIWebGLExtension> mEnabledExtensions[WebGLExtensionID_Max]; PRBool IsExtensionEnabled(WebGLExtensionID ext) const { NS_ABORT_IF_FALSE(ext >= 0 && ext < WebGLExtensionID_Max, "bogus index!"); return mEnabledExtensions[ext] != nsnull; } + bool IsExtensionSupported(WebGLExtensionID ei); PRBool InitAndValidateGL(); PRBool ValidateBuffers(PRInt32* maxAllowedCount, const char *info); PRBool ValidateCapabilityEnum(WebGLenum cap, const char *info); PRBool ValidateBlendEquationEnum(WebGLenum cap, const char *info); PRBool ValidateBlendFuncDstEnum(WebGLenum mode, const char *info); PRBool ValidateBlendFuncSrcEnum(WebGLenum mode, const char *info); PRBool ValidateBlendFuncEnumsCompatibility(WebGLenum sfactor, WebGLenum dfactor, const char *info);
--- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -488,22 +488,21 @@ PRBool nsBuiltinDecoderStateMachine::IsP } void nsBuiltinDecoderStateMachine::AudioLoop() { NS_ASSERTION(OnAudioThread(), "Should be on audio thread."); LOG(PR_LOG_DEBUG, ("%p Begun audio thread/loop", mDecoder.get())); PRInt64 audioDuration = 0; PRInt64 audioStartTime = -1; + PRInt64 samplesWritten = 0; PRUint32 channels, rate; double volume = -1; PRBool setVolume; PRInt32 minWriteSamples = -1; - PRInt64 samplesAtLastSleep = 0; - { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mAudioCompleted = PR_FALSE; audioStartTime = mAudioStartTime; channels = mInfo.mAudioChannels; rate = mInfo.mAudioRate; NS_ASSERTION(audioStartTime != -1, "Should have audio start time by now"); } @@ -543,17 +542,16 @@ void nsBuiltinDecoderStateMachine::Audio (!IsPlaying() || mState == DECODER_STATE_BUFFERING || (mReader->mAudioQueue.GetSize() == 0 && !mReader->mAudioQueue.AtEndOfStream()))) { if (!IsPlaying() && !mAudioStream->IsPaused()) { mAudioStream->Pause(); } - samplesAtLastSleep = audioDuration; mon.Wait(); } // If we're shutting down, break out and exit the audio thread. if (mState == DECODER_STATE_SHUTDOWN || mStopAudioThread || mReader->mAudioQueue.AtEndOfStream()) { @@ -611,38 +609,38 @@ void nsBuiltinDecoderStateMachine::Audio } if (missingSamples > 0) { // The next sound chunk begins some time after the end of the last chunk // we pushed to the sound hardware. We must push silence into the audio // hardware so that the next sound chunk begins playback at the correct // time. missingSamples = NS_MIN(static_cast<PRInt64>(PR_UINT32_MAX), missingSamples); - audioDuration += PlaySilence(static_cast<PRUint32>(missingSamples), + samplesWritten = PlaySilence(static_cast<PRUint32>(missingSamples), channels, playedSamples); } else { - audioDuration += PlayFromAudioQueue(sampleTime, channels); + samplesWritten = PlayFromAudioQueue(sampleTime, channels); } + audioDuration += samplesWritten; { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); PRInt64 playedUsecs; if (!SamplesToUsecs(audioDuration, rate, playedUsecs)) { NS_WARNING("Int overflow calculating playedUsecs"); break; } if (!AddOverflow(audioStartTime, playedUsecs, mAudioEndTime)) { NS_WARNING("Int overflow calculating audio end time"); break; } PRInt64 audioAhead = mAudioEndTime - GetMediaTime(); if (audioAhead > AMPLE_AUDIO_USECS && - audioDuration - samplesAtLastSleep > minWriteSamples) + samplesWritten > minWriteSamples) { - samplesAtLastSleep = audioDuration; // We've pushed enough audio onto the hardware that we've queued up a // significant amount ahead of the playback position. The decode // thread will be going to sleep, so we won't get any new samples // anyway, so sleep until we need to push to the hardware again. Wait(AMPLE_AUDIO_USECS / 2); // Kick the decode thread; since above we only do a NotifyAll when // we pop an audio chunk of the queue, the decoder won't wake up if // we've got no more decoded chunks to push to the hardware. We can @@ -657,16 +655,31 @@ void nsBuiltinDecoderStateMachine::Audio mState != DECODER_STATE_SHUTDOWN && !mStopAudioThread) { // Last sample pushed to audio hardware, wait for the audio to finish, // before the audio thread terminates. PRBool seeking = PR_FALSE; { ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); + if (samplesWritten < minWriteSamples) { + // We've not written minWriteSamples in the last write, the audio + // may not start playing. Write silence to ensure we've got enough + // samples written to start playback. + PRInt64 samples = minWriteSamples - samplesWritten; + if (samples < PR_UINT32_MAX / channels) { + // Write silence manually rather than using PlaySilence(), so that + // the AudioAPI doesn't get a copy of the samples. + PRUint32 numValues = samples * channels; + nsAutoArrayPtr<SoundDataValue> buf(new SoundDataValue[numValues]); + memset(buf.get(), 0, sizeof(SoundDataValue) * numValues); + mAudioStream->Write(buf, numValues, PR_TRUE); + } + } + PRInt64 oldPosition = -1; PRInt64 position = GetMediaTime(); while (oldPosition != position && mAudioEndTime - position > 0 && mState != DECODER_STATE_SEEKING && mState != DECODER_STATE_SHUTDOWN) { const PRInt64 DRAIN_BLOCK_USECS = 100000;
new file mode 100644 --- /dev/null +++ b/content/smil/crashtests/678822-1.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg"> + <animate repeatCount="2" dur="1s" accumulate="1" /> +</svg>
--- a/content/smil/crashtests/crashtests.list +++ b/content/smil/crashtests/crashtests.list @@ -33,8 +33,9 @@ load 608549-1.svg load 608295-1.html load 611927-1.svg load 615002-1.svg load 615872-1.svg load 665334-1.svg load 669225-1.svg load 670313-1.svg load 669225-2.svg +load 678822-1.svg
--- a/content/smil/nsSMILAnimationFunction.cpp +++ b/content/smil/nsSMILAnimationFunction.cpp @@ -182,17 +182,19 @@ nsSMILAnimationFunction::SampleAt(nsSMIL mHasChanged |= mLastValue; // Are we sampling at a new point in simple duration? And does that matter? mHasChanged |= (mSampleTime != aSampleTime || mSimpleDuration != aSimpleDuration) && !IsValueFixedForSimpleDuration(); // Are we on a new repeat and accumulating across repeats? - mHasChanged |= (mRepeatIteration != aRepeatIteration) && GetAccumulate(); + if (!mErrorFlags) { // (can't call GetAccumulate() if we've had parse errors) + mHasChanged |= (mRepeatIteration != aRepeatIteration) && GetAccumulate(); + } mSampleTime = aSampleTime; mSimpleDuration = aSimpleDuration; mRepeatIteration = aRepeatIteration; mLastValue = PR_FALSE; } void
--- a/docshell/base/nsDownloadHistory.cpp +++ b/docshell/base/nsDownloadHistory.cpp @@ -53,17 +53,18 @@ NS_IMPL_ISUPPORTS1(nsDownloadHistory, nsIDownloadHistory) //////////////////////////////////////////////////////////////////////////////// //// nsIDownloadHistory NS_IMETHODIMP nsDownloadHistory::AddDownload(nsIURI *aSource, nsIURI *aReferrer, - PRTime aStartTime) + PRTime aStartTime, + nsIURI *aDestination) { NS_ENSURE_ARG_POINTER(aSource); nsCOMPtr<nsIGlobalHistory2> history = do_GetService("@mozilla.org/browser/global-history;2"); if (!history) return NS_ERROR_NOT_AVAILABLE;
--- a/docshell/base/nsIDownloadHistory.idl +++ b/docshell/base/nsIDownloadHistory.idl @@ -41,32 +41,36 @@ interface nsIURI; /** * This interface can be used to add a download to history. There is a separate * interface specifically for downloads in case embedders choose to track * downloads differently from other types of history. */ -[scriptable, uuid(202533cd-a8f1-4ee4-8d20-3a6a0d2c6c51)] +[scriptable, uuid(a7a3358c-9af2-41e3-adfe-3bf0b7ac2c38)] interface nsIDownloadHistory : nsISupports { /** * Adds a download to history. This will also notify observers that the * URI aSource is visited with the topic NS_LINK_VISITED_EVENT_TOPIC if * aSource has not yet been visited. * * @param aSource * The source of the download we are adding to history. This cannot be * null. * @param aReferrer * [optional] The referrer of source URI. * @param aStartTime * [optional] The time the download was started. If the start time * is not given, the current time is used. + * @param aDestination + * [optional] The target where the download is to be saved on the local + * filesystem. * @throws NS_ERROR_NOT_AVAILABLE * In a situation where a history implementation is not available, * where 'history implementation' refers to something like * nsIGlobalHistory and friends. */ void addDownload(in nsIURI aSource, [optional] in nsIURI aReferrer, - [optional] in PRTime aStartTime); + [optional] in PRTime aStartTime, + [optional] in nsIURI aDestination); };
--- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -2958,35 +2958,16 @@ NS_JProfStopProfiling() static JSFunctionSpec JProfFunctions[] = { {"JProfStartProfiling", JProfStartProfilingJS, 0, 0}, {"JProfStopProfiling", JProfStopProfilingJS, 0, 0}, {nsnull, nsnull, 0, 0} }; #endif /* defined(MOZ_JPROF) */ -#ifdef MOZ_CALLGRIND -static JSFunctionSpec CallgrindFunctions[] = { - {"startCallgrind", js_StartCallgrind, 0, 0}, - {"stopCallgrind", js_StopCallgrind, 0, 0}, - {"dumpCallgrind", js_DumpCallgrind, 1, 0}, - {nsnull, nsnull, 0, 0} -}; -#endif - -#ifdef MOZ_VTUNE -static JSFunctionSpec VtuneFunctions[] = { - {"startVtune", js_StartVtune, 1, 0}, - {"stopVtune", js_StopVtune, 0, 0}, - {"pauseVtune", js_PauseVtune, 0, 0}, - {"resumeVtune", js_ResumeVtune, 0, 0}, - {nsnull, nsnull, 0, 0} -}; -#endif - #ifdef MOZ_TRACEVIS static JSFunctionSpec EthogramFunctions[] = { {"initEthogram", js_InitEthogram, 0, 0}, {"shutdownEthogram", js_ShutdownEthogram, 0, 0}, {nsnull, nsnull, 0, 0} }; #endif @@ -3012,26 +2993,16 @@ nsJSContext::InitClasses(void *aGlobalOb ::JS_DefineFunctions(mContext, globalObj, TraceMallocFunctions); #endif #ifdef MOZ_JPROF // Attempt to initialize JProf functions ::JS_DefineFunctions(mContext, globalObj, JProfFunctions); #endif -#ifdef MOZ_CALLGRIND - // Attempt to initialize Callgrind functions - ::JS_DefineFunctions(mContext, globalObj, CallgrindFunctions); -#endif - -#ifdef MOZ_VTUNE - // Attempt to initialize Vtune functions - ::JS_DefineFunctions(mContext, globalObj, VtuneFunctions); -#endif - #ifdef MOZ_TRACEVIS // Attempt to initialize Ethogram functions ::JS_DefineFunctions(mContext, globalObj, EthogramFunctions); #endif JSOptionChangedCallback(js_options_dot_str, this); return rv;
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -594,16 +594,29 @@ ContentParent::RecvGetIconForExtension(c bits->AppendElements(aIconSize * aIconSize * 4); AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize, bits->Elements()); #endif return true; } +bool +ContentParent::RecvGetShowPasswordSetting(PRBool* showPassword) +{ + // default behavior is to show the last password character + *showPassword = PR_TRUE; +#ifdef ANDROID + NS_ASSERTION(AndroidBridge::Bridge() != nsnull, "AndroidBridge is not available"); + if (AndroidBridge::Bridge() != nsnull) + *showPassword = AndroidBridge::Bridge()->GetShowPasswordSetting(); +#endif + return true; +} + NS_IMPL_THREADSAFE_ISUPPORTS4(ContentParent, nsIObserver, nsIThreadObserver, nsIDOMGeoPositionCallback, nsIDeviceMotionListener) NS_IMETHODIMP ContentParent::Observe(nsISupports* aSubject,
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -162,16 +162,17 @@ private: virtual bool RecvSetClipboardText(const nsString& text, const PRInt32& whichClipboard); virtual bool RecvGetClipboardText(const PRInt32& whichClipboard, nsString* text); virtual bool RecvEmptyClipboard(); virtual bool RecvClipboardHasText(PRBool* hasText); virtual bool RecvGetSystemColors(const PRUint32& colorsCount, InfallibleTArray<PRUint32>* colors); virtual bool RecvGetIconForExtension(const nsCString& aFileExt, const PRUint32& aIconSize, InfallibleTArray<PRUint8>* bits); + virtual bool RecvGetShowPasswordSetting(PRBool* showPassword); virtual bool RecvStartVisitedQuery(const IPC::URI& uri); virtual bool RecvVisitURI(const IPC::URI& uri, const IPC::URI& referrer, const PRUint32& flags); virtual bool RecvSetURITitle(const IPC::URI& uri,
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -200,15 +200,18 @@ parent: returns (PRBool hasText); sync GetSystemColors(PRUint32 colorsCount) returns (PRUint32[] colors); sync GetIconForExtension(nsCString aFileExt, PRUint32 aIconSize) returns (PRUint8[] bits); + sync GetShowPasswordSetting() + returns (PRBool showPassword); + both: AsyncMessage(nsString aMessage, nsString aJSON); }; } }
--- a/dom/plugins/ipc/PluginInstanceParent.h +++ b/dom/plugins/ipc/PluginInstanceParent.h @@ -348,18 +348,18 @@ private: nsIntRect mSharedSize; HWND mPluginHWND; WNDPROC mPluginWndProc; bool mNestedEventState; #endif // defined(XP_WIN) #if defined(MOZ_WIDGET_COCOA) private: Shmem mShSurface; - size_t mShWidth; - size_t mShHeight; + uint16_t mShWidth; + uint16_t mShHeight; CGColorSpaceRef mShColorSpace; int16_t mDrawingModel; nsRefPtr<nsIOSurface> mIOSurface; nsRefPtr<nsIOSurface> mFrontIOSurface; #endif // definied(MOZ_WIDGET_COCOA) // ObjectFrame layer wrapper nsRefPtr<gfxASurface> mFrontSurface;
--- a/dom/workers/Events.cpp +++ b/dom/workers/Events.cpp @@ -1,9 +1,10 @@ /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * @@ -40,16 +41,17 @@ #include "jsapi.h" #include "jscntxt.h" #include "jsobj.h" #include "nsTraceRefcnt.h" #include "WorkerInlines.h" +#include "WorkerPrivate.h" #define PROPERTY_FLAGS \ JSPROP_ENUMERATE | JSPROP_SHARED #define FUNCTION_FLAGS \ JSPROP_ENUMERATE #define CONSTANT_FLAGS \ @@ -410,16 +412,18 @@ class MessageEvent : public Event static JSClass sMainRuntimeClass; static JSPropertySpec sProperties[]; static JSFunctionSpec sFunctions[]; protected: uint64* mData; size_t mDataByteCount; + nsTArray<nsCOMPtr<nsISupports> > mClonedObjects; + bool mMainRuntime; public: static bool IsThisClass(JSClass* aClass) { return aClass == &sClass || aClass == &sMainRuntimeClass; } @@ -430,46 +434,48 @@ public: JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass; return JS_InitClass(aCx, aObj, aParentProto, clasp, Construct, 0, sProperties, sFunctions, NULL, NULL); } static JSObject* Create(JSContext* aCx, JSObject* aParent, JSAutoStructuredCloneBuffer& aData, - bool aMainRuntime) + nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime) { JSString* type = JS_InternString(aCx, "message"); if (!type) { return NULL; } JSClass* clasp = aMainRuntime ? &sMainRuntimeClass : &sClass; JSObject* obj = JS_NewObject(aCx, clasp, NULL, aParent); if (!obj) { return NULL; } - MessageEvent* priv = new MessageEvent(); + MessageEvent* priv = new MessageEvent(aMainRuntime); if (!SetJSPrivateSafeish(aCx, obj, priv) || !InitMessageEventCommon(aCx, obj, priv, type, false, false, NULL, NULL, NULL, true)) { SetJSPrivateSafeish(aCx, obj, NULL); delete priv; return NULL; } aData.steal(&priv->mData, &priv->mDataByteCount); + priv->mClonedObjects.SwapElements(aClonedObjects); + return obj; } protected: - MessageEvent() - : mData(NULL), mDataByteCount(0) + MessageEvent(bool aMainRuntime) + : mData(NULL), mDataByteCount(0), mMainRuntime(aMainRuntime) { MOZ_COUNT_CTOR(mozilla::dom::workers::MessageEvent); } virtual ~MessageEvent() { MOZ_COUNT_DTOR(mozilla::dom::workers::MessageEvent); JS_ASSERT(!mData); @@ -565,18 +571,24 @@ private: // Deserialize and save the data value if we can. if (slot == SLOT_data && event->mData) { JSAutoStructuredCloneBuffer buffer; buffer.adopt(event->mData, event->mDataByteCount); event->mData = NULL; event->mDataByteCount = 0; + // Release reference to objects that were AddRef'd for + // cloning into worker when array goes out of scope. + nsTArray<nsCOMPtr<nsISupports> > clonedObjects; + clonedObjects.SwapElements(event->mClonedObjects); + jsval data; - if (!buffer.read(aCx, &data) || + if (!buffer.read(aCx, &data, + WorkerStructuredCloneCallbacks(event->mMainRuntime)) || !JS_SetReservedSlot(aCx, aObj, slot, data)) { return false; } *aVp = data; return true; } @@ -1052,20 +1064,21 @@ CreateGenericEvent(JSContext* aCx, JSStr bool aCancelable, bool aMainRuntime) { JSObject* global = JS_GetGlobalForScopeChain(aCx); return Event::Create(aCx, global, aType, aBubbles, aCancelable, aMainRuntime); } JSObject* CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData, + nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime) { JSObject* global = JS_GetGlobalForScopeChain(aCx); - return MessageEvent::Create(aCx, global, aData, aMainRuntime); + return MessageEvent::Create(aCx, global, aData, aClonedObjects, aMainRuntime); } JSObject* CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename, uint32 aLineNumber, bool aMainRuntime) { JSObject* global = JS_GetGlobalForScopeChain(aCx); return ErrorEvent::Create(aCx, global, aMessage, aFilename, aLineNumber,
--- a/dom/workers/Events.h +++ b/dom/workers/Events.h @@ -37,32 +37,35 @@ * ***** END LICENSE BLOCK ***** */ #ifndef mozilla_dom_workers_events_h__ #define mozilla_dom_workers_events_h__ #include "Workers.h" #include "jspubtd.h" +#include "nsTArray.h" +#include "nsCOMPtr.h" class JSAutoStructuredCloneBuffer; BEGIN_WORKERS_NAMESPACE namespace events { bool InitClasses(JSContext* aCx, JSObject* aGlobal, bool aMainRuntime); JSObject* CreateGenericEvent(JSContext* aCx, JSString* aType, bool aBubbles, bool aCancelable, bool aMainRuntime); JSObject* CreateMessageEvent(JSContext* aCx, JSAutoStructuredCloneBuffer& aData, + nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects, bool aMainRuntime); JSObject* CreateErrorEvent(JSContext* aCx, JSString* aMessage, JSString* aFilename, uint32 aLineNumber, bool aMainRuntime); JSObject* CreateProgressEvent(JSContext* aCx, JSString* aType, bool aLengthComputable,
--- a/dom/workers/Exceptions.cpp +++ b/dom/workers/Exceptions.cpp @@ -1,9 +1,10 @@ /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * @@ -184,17 +185,16 @@ private: JSClass DOMException::sClass = { "DOMException", JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, JSCLASS_NO_OPTIONAL_MEMBERS }; - JSPropertySpec DOMException::sProperties[] = { { "code", SLOT_code, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub }, { "name", SLOT_name, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub }, { 0, 0, 0, NULL, NULL } }; JSFunctionSpec DOMException::sFunctions[] = { JS_FN("toString", ToString, 0, 0), @@ -272,32 +272,192 @@ DOMException::Create(JSContext* aCx, int if (!SetJSPrivateSafeish(aCx, obj, priv)) { delete priv; return NULL; } return obj; } +class FileException : public PrivatizableBase +{ + static JSClass sClass; + static JSPropertySpec sProperties[]; + static JSPropertySpec sStaticProperties[]; + + enum SLOT { + SLOT_code = 0, + SLOT_name, + + SLOT_COUNT + }; + +public: + static JSObject* + InitClass(JSContext* aCx, JSObject* aObj) + { + return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties, + NULL, sStaticProperties, NULL); + } + + static JSObject* + Create(JSContext* aCx, intN aCode); + +private: + FileException() + { + MOZ_COUNT_CTOR(mozilla::dom::workers::exceptions::FileException); + } + + ~FileException() + { + MOZ_COUNT_DTOR(mozilla::dom::workers::exceptions::FileException); + } + + static JSBool + Construct(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR, + sClass.name); + return false; + } + + static void + Finalize(JSContext* aCx, JSObject* aObj) + { + JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass); + delete GetJSPrivateSafeish<FileException>(aCx, aObj); + } + + static JSBool + GetProperty(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp) + { + JS_ASSERT(JSID_IS_INT(aIdval)); + + int32 slot = JSID_TO_INT(aIdval); + + JSClass* classPtr = JS_GET_CLASS(aCx, aObj); + + if (classPtr != &sClass || + !GetJSPrivateSafeish<FileException>(aCx, aObj)) { + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, sClass.name, + sProperties[slot].name, + classPtr ? classPtr->name : "object"); + return false; + } + + return JS_GetReservedSlot(aCx, aObj, slot, aVp); + } + + static JSBool + GetConstant(JSContext* aCx, JSObject* aObj, jsid idval, jsval* aVp) + { + JS_ASSERT(JSID_IS_INT(idval)); + *aVp = INT_TO_JSVAL(JSID_TO_INT(idval)); + return true; + } +}; + +JSClass FileException::sClass = { + "FileException", + JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(SLOT_COUNT), + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +JSPropertySpec FileException::sProperties[] = { + { "code", SLOT_code, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub }, + { "name", SLOT_name, PROPERTY_FLAGS, GetProperty, js_GetterOnlyPropertyStub }, + { 0, 0, 0, NULL, NULL } +}; + +JSPropertySpec FileException::sStaticProperties[] = { + +#define EXCEPTION_ENTRY(_name) \ + { #_name, FILE_##_name, CONSTANT_FLAGS, GetConstant, NULL }, + + EXCEPTION_ENTRY(NOT_FOUND_ERR) + EXCEPTION_ENTRY(SECURITY_ERR) + EXCEPTION_ENTRY(ABORT_ERR) + EXCEPTION_ENTRY(NOT_READABLE_ERR) + EXCEPTION_ENTRY(ENCODING_ERR) + +#undef EXCEPTION_ENTRY + + { 0, 0, 0, NULL, NULL } +}; + +// static +JSObject* +FileException::Create(JSContext* aCx, intN aCode) +{ + JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL); + if (!obj) { + return NULL; + } + + size_t foundIndex = size_t(-1); + for (size_t index = 0; + index < JS_ARRAY_LENGTH(sStaticProperties) - 1; + index++) { + if (sStaticProperties[index].tinyid == aCode) { + foundIndex = index; + break; + } + } + + JS_ASSERT(foundIndex != size_t(-1)); + + JSString* name = JS_NewStringCopyZ(aCx, sStaticProperties[foundIndex].name); + if (!name) { + return NULL; + } + + if (!JS_SetReservedSlot(aCx, obj, SLOT_code, INT_TO_JSVAL(aCode)) || + !JS_SetReservedSlot(aCx, obj, SLOT_name, STRING_TO_JSVAL(name))) { + return NULL; + } + + FileException* priv = new FileException(); + if (!SetJSPrivateSafeish(aCx, obj, priv)) { + delete priv; + return NULL; + } + + return obj; +} + } // anonymous namespace BEGIN_WORKERS_NAMESPACE namespace exceptions { bool InitClasses(JSContext* aCx, JSObject* aGlobal) { - return !!DOMException::InitClass(aCx, aGlobal); + return DOMException::InitClass(aCx, aGlobal) && + FileException::InitClass(aCx, aGlobal); } void ThrowDOMExceptionForCode(JSContext* aCx, intN aCode) { JSObject* exception = DOMException::Create(aCx, aCode); JS_ASSERT(exception); JS_SetPendingException(aCx, OBJECT_TO_JSVAL(exception)); } +void +ThrowFileExceptionForCode(JSContext* aCx, intN aCode) +{ + JSObject* exception = FileException::Create(aCx, aCode); + JS_ASSERT(exception); + + JS_SetPendingException(aCx, OBJECT_TO_JSVAL(exception)); +} + } // namespace exceptions END_WORKERS_NAMESPACE
--- a/dom/workers/Exceptions.h +++ b/dom/workers/Exceptions.h @@ -1,9 +1,10 @@ /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * @@ -38,16 +39,17 @@ #ifndef mozilla_dom_workers_exceptions_h__ #define mozilla_dom_workers_exceptions_h__ #include "Workers.h" #include "jspubtd.h" +// DOMException Codes. #define INDEX_SIZE_ERR 1 #define DOMSTRING_SIZE_ERR 2 #define HIERARCHY_REQUEST_ERR 3 #define WRONG_DOCUMENT_ERR 4 #define INVALID_CHARACTER_ERR 5 #define NO_DATA_ALLOWED_ERR 6 #define NO_MODIFICATION_ALLOWED_ERR 7 #define NOT_FOUND_ERR 8 @@ -64,23 +66,33 @@ #define NETWORK_ERR 19 #define ABORT_ERR 20 #define URL_MISMATCH_ERR 21 #define QUOTA_EXCEEDED_ERR 22 #define TIMEOUT_ERR 23 #define INVALID_NODE_TYPE_ERR 24 #define DATA_CLONE_ERR 25 +// FileException Codes +#define FILE_NOT_FOUND_ERR 1 +#define FILE_SECURITY_ERR 2 +#define FILE_ABORT_ERR 3 +#define FILE_NOT_READABLE_ERR 4 +#define FILE_ENCODING_ERR 5 + BEGIN_WORKERS_NAMESPACE namespace exceptions { bool InitClasses(JSContext* aCx, JSObject* aGlobal); void ThrowDOMExceptionForCode(JSContext* aCx, intN aCode); +void +ThrowFileExceptionForCode(JSContext* aCx, intN aCode); + } // namespace exceptions END_WORKERS_NAMESPACE #endif // mozilla_dom_workers_exceptions_h__
new file mode 100644 --- /dev/null +++ b/dom/workers/File.cpp @@ -0,0 +1,452 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * William Chen <wchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "File.h" + +#include "nsIDOMFile.h" + +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "nsCOMPtr.h" +#include "nsJSUtils.h" +#include "nsStringGlue.h" +#include "xpcprivate.h" +#include "xpcquickstubs.h" + +#include "Exceptions.h" +#include "WorkerInlines.h" +#include "WorkerPrivate.h" + +#define PROPERTY_FLAGS \ + JSPROP_ENUMERATE | JSPROP_SHARED + +USING_WORKERS_NAMESPACE + +using mozilla::dom::workers::exceptions::ThrowFileExceptionForCode; + +namespace { + +class Blob +{ + // Blob should never be instantiated. + Blob(); + ~Blob(); + + static JSClass sClass; + static JSPropertySpec sProperties[]; + static JSFunctionSpec sFunctions[]; + +public: + static JSObject* + InitClass(JSContext* aCx, JSObject* aObj) + { + return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties, + sFunctions, NULL, NULL); + } + + static JSObject* + Create(JSContext* aCx, nsIDOMBlob* aBlob) + { + JS_ASSERT(SameCOMIdentity(static_cast<nsISupports*>(aBlob), aBlob)); + + JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL); + if (obj) { + if (!JS_SetPrivate(aCx, obj, aBlob)) { + return NULL; + } + NS_ADDREF(aBlob); + } + return obj; + } + + static nsIDOMBlob* + GetPrivate(JSContext* aCx, JSObject* aObj); + +private: + static nsIDOMBlob* + GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName) + { + JSClass* classPtr = NULL; + + if (aObj) { + nsIDOMBlob* blob = GetPrivate(aCx, aObj); + if (blob) { + return blob; + } + + classPtr = JS_GET_CLASS(aCx, aObj); + } + + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName, + classPtr ? classPtr->name : "Object"); + return NULL; + } + + static JSBool + Construct(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR, + sClass.name); + return false; + } + + static void + Finalize(JSContext* aCx, JSObject* aObj) + { + JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass); + + nsIDOMBlob* blob = GetPrivate(aCx, aObj); + NS_IF_RELEASE(blob); + } + + static JSBool + GetSize(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp) + { + nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "size"); + if (!blob) { + return false; + } + + PRUint64 size; + if (NS_FAILED(blob->GetSize(&size))) { + ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR); + } + + if (!JS_NewNumberValue(aCx, jsdouble(size), aVp)) { + return false; + } + + return true; + } + + static JSBool + GetType(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp) + { + nsIDOMBlob* blob = GetInstancePrivate(aCx, aObj, "type"); + if (!blob) { + return false; + } + + nsString type; + if (NS_FAILED(blob->GetType(type))) { + ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR); + } + + JSString* jsType = JS_NewUCStringCopyN(aCx, type.get(), type.Length()); + if (!jsType) { + return false; + } + + *aVp = STRING_TO_JSVAL(jsType); + + return true; + } + + static JSBool + MozSlice(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JSObject* obj = JS_THIS_OBJECT(aCx, aVp); + + nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "mozSlice"); + if (!blob) { + return false; + } + + jsdouble start = 0, end = 0; + JSString* jsContentType = JS_GetEmptyString(JS_GetRuntime(aCx)); + if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "/IIS", &start, + &end, &jsContentType)) { + return false; + } + + nsDependentJSString contentType; + if (!contentType.init(aCx, jsContentType)) { + return false; + } + + PRUint8 optionalArgc = aArgc; + nsCOMPtr<nsIDOMBlob> rtnBlob; + if (NS_FAILED(blob->MozSlice(xpc_qsDoubleToUint64(start), + xpc_qsDoubleToUint64(end), + contentType, optionalArgc, + getter_AddRefs(rtnBlob)))) { + ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR); + return false; + } + + JSObject* rtnObj = file::CreateBlob(aCx, rtnBlob); + if (!rtnObj) { + return false; + } + + JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(rtnObj)); + return true; + } +}; + +JSClass Blob::sClass = { + "Blob", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +JSPropertySpec Blob::sProperties[] = { + { "size", 0, PROPERTY_FLAGS, GetSize, js_GetterOnlyPropertyStub }, + { "type", 0, PROPERTY_FLAGS, GetType, js_GetterOnlyPropertyStub }, + { 0, 0, 0, NULL, NULL } +}; + +JSFunctionSpec Blob::sFunctions[] = { + JS_FN("mozSlice", MozSlice, 1, JSPROP_ENUMERATE), + JS_FS_END +}; + +class File : public Blob +{ + // File should never be instantiated. + File(); + ~File(); + + static JSClass sClass; + static JSPropertySpec sProperties[]; + +public: + static JSObject* + InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto) + { + return JS_InitClass(aCx, aObj, aParentProto, &sClass, Construct, 0, + sProperties, NULL, NULL, NULL); + } + + static JSObject* + Create(JSContext* aCx, nsIDOMFile* aFile) + { + JS_ASSERT(SameCOMIdentity(static_cast<nsISupports*>(aFile), aFile)); + + JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL); + if (obj) { + if (!JS_SetPrivate(aCx, obj, aFile)) { + return NULL; + } + NS_ADDREF(aFile); + } + return obj; + } + + static nsIDOMFile* + GetPrivate(JSContext* aCx, JSObject* aObj) + { + if (aObj) { + JSClass* classPtr = JS_GET_CLASS(aCx, aObj); + if (classPtr == &sClass) { + nsISupports* priv = static_cast<nsISupports*>(JS_GetPrivate(aCx, aObj)); + nsCOMPtr<nsIDOMFile> file = do_QueryInterface(priv); + JS_ASSERT_IF(priv, file); + return file; + } + } + return NULL; + } + + static JSClass* + Class() + { + return &sClass; + } + +private: + static nsIDOMFile* + GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName) + { + JSClass* classPtr = NULL; + + if (aObj) { + nsIDOMFile* file = GetPrivate(aCx, aObj); + if (file) { + return file; + } + classPtr = JS_GET_CLASS(aCx, aObj); + } + + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName, + classPtr ? classPtr->name : "Object"); + return NULL; + } + + static JSBool + Construct(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_WRONG_CONSTRUCTOR, + sClass.name); + return false; + } + + static void + Finalize(JSContext* aCx, JSObject* aObj) + { + JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass); + + nsIDOMFile* file = GetPrivate(aCx, aObj); + NS_IF_RELEASE(file); + } + + static JSBool + GetMozFullPath(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp) + { + nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "mozFullPath"); + if (!file) { + return false; + } + + nsString fullPath; + + if (GetWorkerPrivateFromContext(aCx)->UsesSystemPrincipal() && + NS_FAILED(file->GetMozFullPathInternal(fullPath))) { + ThrowFileExceptionForCode(aCx, FILE_NOT_READABLE_ERR); + return false; + } + + JSString* jsFullPath = JS_NewUCStringCopyN(aCx, fullPath.get(), + fullPath.Length()); + if (!jsFullPath) { + return false; + } + + *aVp = STRING_TO_JSVAL(jsFullPath); + return true; + } + + static JSBool + GetName(JSContext* aCx, JSObject* aObj, jsid aIdval, jsval* aVp) + { + nsIDOMFile* file = GetInstancePrivate(aCx, aObj, "name"); + if (!file) { + return false; + } + + nsString name; + if (NS_FAILED(file->GetName(name))) { + name.Truncate(); + } + + JSString* jsName = JS_NewUCStringCopyN(aCx, name.get(), name.Length()); + if (!jsName) { + return false; + } + + *aVp = STRING_TO_JSVAL(jsName); + return true; + } +}; + +JSClass File::sClass = { + "File", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +JSPropertySpec File::sProperties[] = { + { "name", 0, PROPERTY_FLAGS, GetName, js_GetterOnlyPropertyStub }, + { "mozFullPath", 0, PROPERTY_FLAGS, GetMozFullPath, + js_GetterOnlyPropertyStub }, + { 0, 0, 0, NULL, NULL } +}; + +nsIDOMBlob* +Blob::GetPrivate(JSContext* aCx, JSObject* aObj) +{ + if (aObj) { + JSClass* classPtr = JS_GET_CLASS(aCx, aObj); + if (classPtr == &sClass || classPtr == File::Class()) { + nsISupports* priv = static_cast<nsISupports*>(JS_GetPrivate(aCx, aObj)); + nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(priv); + JS_ASSERT_IF(priv, blob); + return blob; + } + } + return NULL; +} + +} // anonymous namespace + +BEGIN_WORKERS_NAMESPACE + +namespace file { + +JSObject* +CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob) +{ + return Blob::Create(aCx, aBlob); +} + +bool +InitClasses(JSContext* aCx, JSObject* aGlobal) +{ + JSObject* blobProto = Blob::InitClass(aCx, aGlobal); + return blobProto && File::InitClass(aCx, aGlobal, blobProto); +} + +nsIDOMBlob* +GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj) +{ + return Blob::GetPrivate(aCx, aObj); +} + +JSObject* +CreateFile(JSContext* aCx, nsIDOMFile* aFile) +{ + return File::Create(aCx, aFile); +} + +nsIDOMFile* +GetDOMFileFromJSObject(JSContext* aCx, JSObject* aObj) +{ + return File::GetPrivate(aCx, aObj); +} + +} // namespace file + +END_WORKERS_NAMESPACE
new file mode 100644 --- /dev/null +++ b/dom/workers/File.h @@ -0,0 +1,73 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * William Chen <wchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_dom_workers_file_h__ +#define mozilla_dom_workers_file_h__ + +#include "Workers.h" + +#include "jspubtd.h" + +class nsIDOMFile; +class nsIDOMBlob; + +BEGIN_WORKERS_NAMESPACE + +namespace file { + +bool +InitClasses(JSContext* aCx, JSObject* aGlobal); + +JSObject* +CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob); + +nsIDOMBlob* +GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj); + +JSObject* +CreateFile(JSContext* aCx, nsIDOMFile* aFile); + +nsIDOMFile* +GetDOMFileFromJSObject(JSContext* aCx, JSObject* aObj); + +} // namespace file + +END_WORKERS_NAMESPACE + +#endif /* mozilla_dom_workers_file_h__ */
new file mode 100644 --- /dev/null +++ b/dom/workers/FileReaderSync.cpp @@ -0,0 +1,372 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * William Chen <wchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "FileReaderSync.h" + +#include "nsIDOMFile.h" + +#include "jsapi.h" +#include "jsatom.h" +#include "jscntxt.h" +#include "jstypedarray.h" +#include "nsJSUtils.h" +#include "xpcprivate.h" + +#include "Exceptions.h" +#include "File.h" +#include "FileReaderSyncPrivate.h" +#include "WorkerInlines.h" + +#define FUNCTION_FLAGS \ + JSPROP_ENUMERATE + +USING_WORKERS_NAMESPACE + +using mozilla::dom::workers::exceptions::ThrowFileExceptionForCode; +using js::ArrayBuffer; + +namespace { + +inline bool +EnsureSucceededOrThrow(JSContext* aCx, nsresult rv) +{ + if (NS_SUCCEEDED(rv)) { + return true; + } + + intN code = rv == NS_ERROR_FILE_NOT_FOUND ? + FILE_NOT_FOUND_ERR : + FILE_NOT_READABLE_ERR; + ThrowFileExceptionForCode(aCx, code); + return false; +} + +inline nsIDOMBlob* +GetDOMBlobFromJSObject(JSContext* aCx, JSObject* aObj) { + JSClass* classPtr = NULL; + + if (aObj) { + nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aCx, aObj); + if (blob) { + return blob; + } + + classPtr = JS_GET_CLASS(aCx, aObj); + } + + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE, + classPtr ? classPtr->name : "Object", "not a Blob."); + return NULL; +} + +class FileReaderSync +{ + // FileReaderSync should not be instantiated. + FileReaderSync(); + ~FileReaderSync(); + + static JSClass sClass; + static JSFunctionSpec sFunctions[]; + +public: + static JSObject* + InitClass(JSContext* aCx, JSObject* aObj) + { + return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, + NULL, sFunctions, NULL, NULL); + } + + static FileReaderSyncPrivate* + GetPrivate(JSContext* aCx, JSObject* aObj) + { + if (aObj) { + JSClass* classPtr = JS_GET_CLASS(aCx, aObj); + if (classPtr == &sClass) { + FileReaderSyncPrivate* fileReader = + GetJSPrivateSafeish<FileReaderSyncPrivate>(aCx, aObj); + return fileReader; + } + } + return NULL; + } + +private: + static FileReaderSyncPrivate* + GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName) + { + JSClass* classPtr = NULL; + + if (aObj) { + FileReaderSyncPrivate* fileReader = GetPrivate(aCx, aObj); + if (fileReader) { + return fileReader; + } + + classPtr = JS_GET_CLASS(aCx, aObj); + } + + JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL, + JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName, + classPtr ? classPtr->name : "Object"); + return NULL; + } + + static JSBool + Construct(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL); + if (!obj) { + return false; + } + + FileReaderSyncPrivate* fileReader = new FileReaderSyncPrivate(); + NS_ADDREF(fileReader); + + if (!SetJSPrivateSafeish(aCx, obj, fileReader)) { + NS_RELEASE(fileReader); + return false; + } + + JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(obj)); + return true; + } + + static void + Finalize(JSContext* aCx, JSObject* aObj) + { + JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass); + FileReaderSyncPrivate* fileReader = + GetJSPrivateSafeish<FileReaderSyncPrivate>(aCx, aObj); + NS_IF_RELEASE(fileReader); + } + + static JSBool + ReadAsArrayBuffer(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JSObject* obj = JS_THIS_OBJECT(aCx, aVp); + + FileReaderSyncPrivate* fileReader = + GetInstancePrivate(aCx, obj, "readAsArrayBuffer"); + if (!fileReader) { + return false; + } + + JSObject* jsBlob; + if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) { + return false; + } + + nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob); + if (!blob) { + return false; + } + + PRUint64 blobSize; + nsresult rv = blob->GetSize(&blobSize); + if (!EnsureSucceededOrThrow(aCx, rv)) { + return false; + } + + JSObject* jsArrayBuffer = js_CreateArrayBuffer(aCx, blobSize); + if (!jsArrayBuffer) { + return false; + } + + JSUint32 bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer); + uint8* arrayBuffer = JS_GetArrayBufferData(jsArrayBuffer); + + rv = fileReader->ReadAsArrayBuffer(blob, bufferLength, arrayBuffer); + if (!EnsureSucceededOrThrow(aCx, rv)) { + return false; + } + + JS_SET_RVAL(aCx, aVp, OBJECT_TO_JSVAL(jsArrayBuffer)); + return true; + } + + static JSBool + ReadAsDataURL(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JSObject* obj = JS_THIS_OBJECT(aCx, aVp); + + FileReaderSyncPrivate* fileReader = + GetInstancePrivate(aCx, obj, "readAsDataURL"); + if (!fileReader) { + return false; + } + + JSObject* jsBlob; + if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) { + return false; + } + + nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob); + if (!blob) { + return false; + } + + nsString blobText; + nsresult rv = fileReader->ReadAsDataURL(blob, blobText); + if (!EnsureSucceededOrThrow(aCx, rv)) { + return false; + } + + JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(), + blobText.Length()); + if (!jsBlobText) { + return false; + } + + JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText)); + return true; + } + + static JSBool + ReadAsBinaryString(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JSObject* obj = JS_THIS_OBJECT(aCx, aVp); + + FileReaderSyncPrivate* fileReader = + GetInstancePrivate(aCx, obj, "readAsBinaryString"); + if (!fileReader) { + return false; + } + + JSObject* jsBlob; + if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &jsBlob)) { + return false; + } + + nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob); + if (!blob) { + return false; + } + + nsString blobText; + nsresult rv = fileReader->ReadAsBinaryString(blob, blobText); + if (!EnsureSucceededOrThrow(aCx, rv)) { + return false; + } + + JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(), + blobText.Length()); + if (!jsBlobText) { + return false; + } + + JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText)); + return true; + } + + static JSBool + ReadAsText(JSContext* aCx, uintN aArgc, jsval* aVp) + { + JSObject* obj = JS_THIS_OBJECT(aCx, aVp); + + FileReaderSyncPrivate* fileReader = + GetInstancePrivate(aCx, obj, "readAsText"); + if (!fileReader) { + return false; + } + + JSObject* jsBlob; + JSString* jsEncoding = JS_GetEmptyString(JS_GetRuntime(aCx)); + if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o/S", &jsBlob, + &jsEncoding)) { + return false; + } + + nsDependentJSString encoding; + if (!encoding.init(aCx, jsEncoding)) { + return false; + } + + nsIDOMBlob* blob = GetDOMBlobFromJSObject(aCx, jsBlob); + if (!blob) { + return false; + } + + nsString blobText; + nsresult rv = fileReader->ReadAsText(blob, encoding, blobText); + if (!EnsureSucceededOrThrow(aCx, rv)) { + return false; + } + + JSString* jsBlobText = JS_NewUCStringCopyN(aCx, blobText.get(), + blobText.Length()); + if (!jsBlobText) { + return false; + } + + JS_SET_RVAL(aCx, aVp, STRING_TO_JSVAL(jsBlobText)); + return true; + } +}; + +JSClass FileReaderSync::sClass = { + "FileReaderSync", + JSCLASS_HAS_PRIVATE, + JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, + JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, Finalize, + JSCLASS_NO_OPTIONAL_MEMBERS +}; + +JSFunctionSpec FileReaderSync::sFunctions[] = { + JS_FN("readAsArrayBuffer", ReadAsArrayBuffer, 1, FUNCTION_FLAGS), + JS_FN("readAsBinaryString", ReadAsBinaryString, 1, FUNCTION_FLAGS), + JS_FN("readAsText", ReadAsText, 1, FUNCTION_FLAGS), + JS_FN("readAsDataURL", ReadAsDataURL, 1, FUNCTION_FLAGS), + JS_FS_END +}; + +} // anonymous namespace + +BEGIN_WORKERS_NAMESPACE + +namespace filereadersync { + +bool +InitClass(JSContext* aCx, JSObject* aGlobal) +{ + return !!FileReaderSync::InitClass(aCx, aGlobal); +} + +} // namespace filereadersync + +END_WORKERS_NAMESPACE
new file mode 100644 --- /dev/null +++ b/dom/workers/FileReaderSync.h @@ -0,0 +1,58 @@ +/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Web Workers. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * William Chen <wchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef mozilla_dom_workers_filereadersync_h__ +#define mozilla_dom_workers_filereadersync_h__ + +#include "Workers.h" + +#include "jspubtd.h" + +BEGIN_WORKERS_NAMESPACE + +namespace filereadersync { + +bool +InitClass(JSContext* aCx, JSObject* aGlobal); + +} // namespace filereadersync + +END_WORKERS_NAMESPACE + +#endif // mozilla_dom_workers_filereadersync_h__
new file mode 100644 --- /dev/null +++ b/dom/workers/FileReaderSyncPrivate.cpp @@ -0,0 +1,318 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * William Chen <wchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "FileReaderSyncPrivate.h" + +#include "nsCExternalHandlerService.h" +#include "nsComponentManagerUtils.h" +#include "nsCOMPtr.h" +#include "nsDOMClassInfo.h" +#include "nsDOMError.h" +#include "nsIDOMFile.h" +#include "nsICharsetAlias.h" +#include "nsICharsetDetector.h" +#include "nsIConverterInputStream.h" +#include "nsIInputStream.h" +#include "nsIPlatformCharset.h" +#include "nsISeekableStream.h" +#include "nsISupportsImpl.h" +#include "nsISupportsImpl.h" +#include "nsNetUtil.h" +#include "nsServiceManagerUtils.h" +#include "RuntimeService.h" + +#include "mozilla/Base64.h" + +USING_WORKERS_NAMESPACE + +NS_IMPL_ISUPPORTS1(FileReaderSyncPrivate, nsICharsetDetectionObserver) + +FileReaderSyncPrivate::FileReaderSyncPrivate() +{ + MOZ_COUNT_CTOR(mozilla::dom::workers::FileReaderSyncPrivate); +} + +FileReaderSyncPrivate::~FileReaderSyncPrivate() +{ + MOZ_COUNT_DTOR(mozilla::dom::workers::FileReaderSyncPrivate); +} + +nsresult +FileReaderSyncPrivate::ReadAsArrayBuffer(nsIDOMBlob* aBlob, PRUint32 aLength, + uint8* aBuffer) +{ + nsCOMPtr<nsIInputStream> stream; + nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 numRead; + rv = stream->Read((char*)aBuffer, aLength, &numRead); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(numRead == aLength, "failed to read data"); + + return NS_OK; +} + +nsresult +FileReaderSyncPrivate::ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult) +{ + nsCOMPtr<nsIInputStream> stream; + nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 numRead; + do { + char readBuf[4096]; + rv = stream->Read(readBuf, sizeof(readBuf), &numRead); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 oldLength = aResult.Length(); + AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult); + if (aResult.Length() - oldLength != numRead) { + return NS_ERROR_OUT_OF_MEMORY; + } + } while (numRead > 0); + + return NS_OK; +} + +nsresult +FileReaderSyncPrivate::ReadAsText(nsIDOMBlob* aBlob, + const nsAString& aEncoding, nsAString& aResult) +{ + nsCOMPtr<nsIInputStream> stream; + nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString charsetGuess; + if (aEncoding.IsEmpty()) { + rv = GuessCharset(stream, charsetGuess); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream); + NS_ENSURE_TRUE(seekable, NS_ERROR_FAILURE); + + // Seek to 0 because guessing the charset advances the stream. + rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + NS_ENSURE_SUCCESS(rv, rv); + } else { + CopyUTF16toUTF8(aEncoding, charsetGuess); + } + + nsCOMPtr<nsICharsetAlias> alias = + do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString charset; + rv = alias->GetPreferred(charsetGuess, charset); + NS_ENSURE_SUCCESS(rv, rv); + + return ConvertStream(stream, charset.get(), aResult); +} + +nsresult +FileReaderSyncPrivate::ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult) +{ + nsAutoString scratchResult; + scratchResult.AssignLiteral("data:"); + + nsString contentType; + aBlob->GetType(contentType); + + if (contentType.IsEmpty()) { + scratchResult.AppendLiteral("application/octet-stream"); + } else { + scratchResult.Append(contentType); + } + scratchResult.AppendLiteral(";base64,"); + + nsCOMPtr<nsIInputStream> stream; + nsresult rv = aBlob->GetInternalStream(getter_AddRefs(stream)); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint64 size; + rv = aBlob->GetSize(&size); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIInputStream> bufferedStream; + rv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString encodedData; + rv = Base64EncodeInputStream(bufferedStream, encodedData, size); + NS_ENSURE_SUCCESS(rv, rv); + + scratchResult.Append(encodedData); + + aResult = scratchResult; + return NS_OK; +} + +nsresult +FileReaderSyncPrivate::ConvertStream(nsIInputStream *aStream, + const char *aCharset, + nsAString &aResult) +{ + nsCOMPtr<nsIConverterInputStream> converterStream = + do_CreateInstance("@mozilla.org/intl/converter-input-stream;1"); + NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE); + + nsresult rv = converterStream->Init(aStream, aCharset, 8192, + nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIUnicharInputStream> unicharStream = + do_QueryInterface(converterStream); + NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE); + + PRUint32 numChars; + nsString result; + while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) && + numChars > 0) { + PRUint32 oldLength = aResult.Length(); + aResult.Append(result); + if (aResult.Length() - oldLength != result.Length()) { + return NS_ERROR_OUT_OF_MEMORY; + } + } + + return rv; +} + +nsresult +FileReaderSyncPrivate::GuessCharset(nsIInputStream *aStream, + nsACString &aCharset) +{ + // First try the universal charset detector + nsCOMPtr<nsICharsetDetector> detector + = do_CreateInstance(NS_CHARSET_DETECTOR_CONTRACTID_BASE + "universal_charset_detector"); + if (!detector) { + RuntimeService* runtime = RuntimeService::GetService(); + NS_ASSERTION(runtime, "This should never be null!"); + + // No universal charset detector, try the default charset detector + const nsACString& detectorName = runtime->GetDetectorName(); + + if (!detectorName.IsEmpty()) { + nsCAutoString detectorContractID; + detectorContractID.AssignLiteral(NS_CHARSET_DETECTOR_CONTRACTID_BASE); + detectorContractID += detectorName; + detector = do_CreateInstance(detectorContractID.get()); + } + } + + nsresult rv; + if (detector) { + detector->Init(this); + + PRBool done; + PRUint32 numRead; + do { + char readBuf[4096]; + rv = aStream->Read(readBuf, sizeof(readBuf), &numRead); + NS_ENSURE_SUCCESS(rv, rv); + if (numRead <= 0) { + break; + } + rv = detector->DoIt(readBuf, numRead, &done); + NS_ENSURE_SUCCESS(rv, rv); + } while (!done); + + rv = detector->Done(); + NS_ENSURE_SUCCESS(rv, rv); + } else { + // no charset detector available, check the BOM + unsigned char sniffBuf[4]; + PRUint32 numRead; + rv = aStream->Read(reinterpret_cast<char*>(sniffBuf), + sizeof(sniffBuf), &numRead); + NS_ENSURE_SUCCESS(rv, rv); + + if (numRead >= 4 && + sniffBuf[0] == 0x00 && + sniffBuf[1] == 0x00 && + sniffBuf[2] == 0xfe && + sniffBuf[3] == 0xff) { + mCharset = "UTF-32BE"; + } else if (numRead >= 4 && + sniffBuf[0] == 0xff && + sniffBuf[1] == 0xfe && + sniffBuf[2] == 0x00 && + sniffBuf[3] == 0x00) { + mCharset = "UTF-32LE"; + } else if (numRead >= 2 && + sniffBuf[0] == 0xfe && + sniffBuf[1] == 0xff) { + mCharset = "UTF-16BE"; + } else if (numRead >= 2 && + sniffBuf[0] == 0xff && + sniffBuf[1] == 0xfe) { + mCharset = "UTF-16LE"; + } else if (numRead >= 3 && + sniffBuf[0] == 0xef && + sniffBuf[1] == 0xbb && + sniffBuf[2] == 0xbf) { + mCharset = "UTF-8"; + } + } + + if (mCharset.IsEmpty()) { + RuntimeService* runtime = RuntimeService::GetService(); + mCharset = runtime->GetSystemCharset(); + } + + if (mCharset.IsEmpty()) { + // no sniffed or default charset, try UTF-8 + mCharset.AssignLiteral("UTF-8"); + } + + aCharset = mCharset; + mCharset.Truncate(); + + return NS_OK; +} + +NS_IMETHODIMP +FileReaderSyncPrivate::Notify(const char* aCharset, nsDetectionConfident aConf) +{ + mCharset.Assign(aCharset); + + return NS_OK; +}
new file mode 100644 --- /dev/null +++ b/dom/workers/FileReaderSyncPrivate.h @@ -0,0 +1,80 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is mozilla.org code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * William Chen <wchen@mozilla.com> (Original Author) + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef nsDOMFileReaderSyncPrivate_h +#define nsDOMFileReaderSyncPrivate_h + +#include "Workers.h" + +#include "nsICharsetDetectionObserver.h" +#include "nsStringGlue.h" + +class nsIInputStream; +class nsIDOMBlob; + +BEGIN_WORKERS_NAMESPACE + +class FileReaderSyncPrivate : public PrivatizableBase, + public nsICharsetDetectionObserver +{ + nsCString mCharset; + nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset, + nsAString &aResult); + nsresult GuessCharset(nsIInputStream *aStream, nsACString &aCharset); + +public: + NS_DECL_ISUPPORTS + + FileReaderSyncPrivate(); + ~FileReaderSyncPrivate(); + + nsresult ReadAsArrayBuffer(nsIDOMBlob* aBlob, PRUint32 aLength, + uint8* aBuffer); + nsresult ReadAsBinaryString(nsIDOMBlob* aBlob, nsAString& aResult); + nsresult ReadAsText(nsIDOMBlob* aBlob, const nsAString& aEncoding, + nsAString& aResult); + nsresult ReadAsDataURL(nsIDOMBlob* aBlob, nsAString& aResult); + + // From nsICharsetDetectionObserver + NS_IMETHOD Notify(const char *aCharset, nsDetectionConfident aConf); +}; + +END_WORKERS_NAMESPACE + +#endif
--- a/dom/workers/Makefile.in +++ b/dom/workers/Makefile.in @@ -52,16 +52,19 @@ EXPORTS_NAMESPACES = mozilla/dom/workers EXPORTS_mozilla/dom/workers = Workers.h CPPSRCS = \ ChromeWorkerScope.cpp \ Events.cpp \ EventTarget.cpp \ Exceptions.cpp \ + File.cpp \ + FileReaderSync.cpp \ + FileReaderSyncPrivate.cpp \ ListenerManager.cpp \ Location.cpp \ Navigator.cpp \ Principal.cpp \ RuntimeService.cpp \ ScriptLoader.cpp \ Worker.cpp \ WorkerPrivate.cpp \
--- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -1,9 +1,10 @@ /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * @@ -37,16 +38,17 @@ * ***** END LICENSE BLOCK ***** */ #include "RuntimeService.h" #include "nsIDOMChromeWindow.h" #include "nsIDocument.h" #include "nsIEffectiveTLDService.h" #include "nsIObserverService.h" +#include "nsIPlatformCharset.h" #include "nsIPrincipal.h" #include "nsIJSContextStack.h" #include "nsIMemoryReporter.h" #include "nsIScriptSecurityManager.h" #include "nsISupportsPriority.h" #include "nsITimer.h" #include "nsPIDOMWindow.h" @@ -72,18 +74,23 @@ USING_WORKERS_NAMESPACE using mozilla::MutexAutoLock; using mozilla::MutexAutoUnlock; using mozilla::Preferences; using namespace mozilla::xpconnect::memory; // The size of the worker runtime heaps in bytes. #define WORKER_RUNTIME_HEAPSIZE 32 * 1024 * 1024 -// The size of the C stack in bytes. -#define WORKER_CONTEXT_NATIVE_STACK_LIMIT sizeof(size_t) * 32 * 1024 +// The C stack size. We use the same stack size on all platforms for +// consistency. +#define WORKER_STACK_SIZE 256 * sizeof(size_t) * 1024 + +// The stack limit the JS engine will check. Half the size of the +// actual C stack, to be safe. +#define WORKER_CONTEXT_NATIVE_STACK_LIMIT 128 * sizeof(size_t) * 1024 // The maximum number of threads to use for workers, overridable via pref. #define MAX_WORKERS_PER_DOMAIN 10 // The default number of seconds that close handlers will be allowed to run. #define MAX_SCRIPT_RUN_TIME_SEC 10 // The number of seconds that idle threads can hang around before being killed. @@ -238,25 +245,16 @@ CreateJSContextForWorker(WorkerPrivate* NS_ASSERTION(!aWorkerPrivate->GetJSContext(), "Already has a context!"); JSRuntime* runtime = JS_NewRuntime(WORKER_RUNTIME_HEAPSIZE); if (!runtime) { NS_WARNING("Could not create new runtime!"); return nsnull; } - // ChromeWorker has extra clone callbacks for passing threadsafe XPCOM - // components. - JSStructuredCloneCallbacks* callbacks = - aWorkerPrivate->IsChromeWorker() ? - ChromeWorkerStructuredCloneCallbacks() : - WorkerStructuredCloneCallbacks(); - - JS_SetStructuredCloneCallbacks(runtime, callbacks); - JSContext* workerCx = JS_NewContext(runtime, 0); if (!workerCx) { JS_DestroyRuntime(runtime); NS_WARNING("Could not create new context!"); return nsnull; } JS_SetContextPrivate(workerCx, aWorkerPrivate); @@ -793,17 +791,18 @@ RuntimeService::ScheduleWorker(JSContext if (!mIdleThreadArray.IsEmpty()) { PRUint32 index = mIdleThreadArray.Length() - 1; mIdleThreadArray[index].mThread.swap(thread); mIdleThreadArray.RemoveElementAt(index); } } if (!thread) { - if (NS_FAILED(NS_NewThread(getter_AddRefs(thread), nsnull))) { + if (NS_FAILED(NS_NewThread(getter_AddRefs(thread), nsnull, + WORKER_STACK_SIZE))) { UnregisterWorker(aCx, aWorkerPrivate); JS_ReportError(aCx, "Could not create new thread!"); return false; } nsCOMPtr<nsISupportsPriority> priority = do_QueryInterface(thread); if (!priority || NS_FAILED(priority->SetPriority(nsISupportsPriority::PRIORITY_LOW))) { @@ -925,16 +924,25 @@ RuntimeService::Init() MAX_SCRIPT_RUN_TIME_SEC))) { NS_WARNING("Failed to register timeout cache?!"); } PRInt32 maxPerDomain = Preferences::GetInt(PREF_WORKERS_MAX_PER_DOMAIN, MAX_WORKERS_PER_DOMAIN); gMaxWorkersPerDomain = NS_MAX(0, maxPerDomain); + mDetectorName = Preferences::GetLocalizedCString("intl.charset.detector"); + + nsCOMPtr<nsIPlatformCharset> platformCharset = + do_GetService(NS_PLATFORMCHARSET_CONTRACTID, &rv); + if (NS_SUCCEEDED(rv)) { + rv = platformCharset->GetCharset(kPlatformCharsetSel_PlainTextInFile, + mSystemCharset); + } + return NS_OK; } // This spins the event loop until all workers are finished and their threads // have been joined. void RuntimeService::Cleanup() {
--- a/dom/workers/RuntimeService.h +++ b/dom/workers/RuntimeService.h @@ -1,9 +1,10 @@ /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * @@ -94,16 +95,19 @@ class RuntimeService : public nsIObserve nsTArray<IdleThreadInfo> mIdleThreadArray; // *Not* protected by mMutex. nsClassHashtable<nsVoidPtrHashKey, nsTArray<WorkerPrivate*> > mWindowMap; // Only used on the main thread. nsCOMPtr<nsITimer> mIdleThreadTimer; + nsCString mDetectorName; + nsCString mSystemCharset; + static PRUint32 sDefaultJSContextOptions; static PRInt32 sCloseHandlerTimeoutSeconds; #ifdef JS_GC_ZEAL static PRUint8 sDefaultGCZeal; #endif public: @@ -143,16 +147,28 @@ public: CancelWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow); void SuspendWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow); void ResumeWorkersForWindow(JSContext* aCx, nsPIDOMWindow* aWindow); + const nsACString& + GetDetectorName() const + { + return mDetectorName; + } + + const nsACString& + GetSystemCharset() const + { + return mSystemCharset; + } + const NavigatorStrings& GetNavigatorStrings() const { return mNavigatorStrings; } void NoteIdleThread(nsIThread* aThread);
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -1,9 +1,10 @@ /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * @@ -35,16 +36,17 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "WorkerPrivate.h" #include "nsIClassInfo.h" #include "nsIConsoleService.h" +#include "nsIDOMFile.h" #include "nsIDocument.h" #include "nsIEffectiveTLDService.h" #include "nsIJSContextStack.h" #include "nsIScriptError.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" #include "nsPIDOMWindow.h" #include "nsITextToSubURI.h" @@ -63,16 +65,17 @@ #include "nsJSEnvironment.h" #include "nsJSUtils.h" #include "nsNetUtil.h" #include "nsThreadUtils.h" #include "xpcpublic.h" #include "Events.h" #include "Exceptions.h" +#include "File.h" #include "Principal.h" #include "RuntimeService.h" #include "ScriptLoader.h" #include "WorkerFeature.h" #include "WorkerScope.h" #include "WorkerInlines.h" @@ -138,24 +141,107 @@ SwapToISupportsArray(SmartPtr<T>& aSrc, } struct WorkerStructuredCloneCallbacks { static JSObject* Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32 aTag, uint32 aData, void* aClosure) { + // See if object is a nsIDOMFile pointer. + if (aTag == DOMWORKER_SCTAG_FILE) { + JS_ASSERT(!aData); + + nsIDOMFile* file; + if (JS_ReadBytes(aReader, &file, sizeof(file))) { + JS_ASSERT(file); + +#ifdef DEBUG + { + // File should not be mutable. + nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); + PRBool isMutable; + NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) && + !isMutable, + "Only immutable file should be passed to worker"); + } +#endif + + // nsIDOMFiles should be threadsafe, thus we will use the same instance + // in the worker. + JSObject* jsFile = file::CreateFile(aCx, file); + return jsFile; + } + } + // See if object is a nsIDOMBlob pointer. + else if (aTag == DOMWORKER_SCTAG_BLOB) { + JS_ASSERT(!aData); + + nsIDOMBlob* blob; + if (JS_ReadBytes(aReader, &blob, sizeof(blob))) { + JS_ASSERT(blob); + +#ifdef DEBUG + { + // Blob should not be mutable. + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); + PRBool isMutable; + NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) && + !isMutable, + "Only immutable blob should be passed to worker"); + } +#endif + + // nsIDOMBlob should be threadsafe, thus we will use the same instance + // in the worker. + JSObject* jsBlob = file::CreateBlob(aCx, blob); + return jsBlob; + } + } + Error(aCx, 0); return nsnull; } static JSBool Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj, void* aClosure) { + NS_ASSERTION(aClosure, "Null pointer!"); + + // We'll stash any nsISupports pointers that need to be AddRef'd here. + nsTArray<nsCOMPtr<nsISupports> >* clonedObjects = + static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure); + + // See if this is a File object. + { + nsIDOMFile* file = file::GetDOMFileFromJSObject(aCx, aObj); + if (file) { + if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) && + JS_WriteBytes(aWriter, &file, sizeof(file))) { + clonedObjects->AppendElement(file); + return true; + } + } + } + + // See if this is a Blob object. + { + nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aCx, aObj); + if (blob) { + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); + if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(PR_FALSE)) && + JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) && + JS_WriteBytes(aWriter, &blob, sizeof(blob))) { + clonedObjects->AppendElement(blob); + return true; + } + } + } + Error(aCx, 0); return false; } static void Error(JSContext* aCx, uint32 /* aErrorId */) { ThrowDOMExceptionForCode(aCx, DATA_CLONE_ERR); @@ -171,32 +257,144 @@ JSStructuredCloneCallbacks gWorkerStruct struct MainThreadWorkerStructuredCloneCallbacks { static JSObject* Read(JSContext* aCx, JSStructuredCloneReader* aReader, uint32 aTag, uint32 aData, void* aClosure) { AssertIsOnMainThread(); + // See if object is a nsIDOMFile pointer. + if (aTag == DOMWORKER_SCTAG_FILE) { + JS_ASSERT(!aData); + + nsIDOMFile* file; + if (JS_ReadBytes(aReader, &file, sizeof(file))) { + JS_ASSERT(file); + +#ifdef DEBUG + { + // File should not be mutable. + nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); + PRBool isMutable; + NS_ASSERTION(NS_SUCCEEDED(mutableFile->GetMutable(&isMutable)) && + !isMutable, + "Only immutable file should be passed to worker"); + } +#endif + + // nsIDOMFiles should be threadsafe, thus we will use the same instance + // on the main thread. + jsval wrappedFile; + nsresult rv = + nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), file, + &NS_GET_IID(nsIDOMFile), &wrappedFile); + if (NS_FAILED(rv)) { + Error(aCx, DATA_CLONE_ERR); + return nsnull; + } + + return JSVAL_TO_OBJECT(wrappedFile); + } + } + // See if object is a nsIDOMBlob pointer. + else if (aTag == DOMWORKER_SCTAG_BLOB) { + JS_ASSERT(!aData); + + nsIDOMBlob* blob; + if (JS_ReadBytes(aReader, &blob, sizeof(blob))) { + JS_ASSERT(blob); + +#ifdef DEBUG + { + // Blob should not be mutable. + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); + PRBool isMutable; + NS_ASSERTION(NS_SUCCEEDED(mutableBlob->GetMutable(&isMutable)) && + !isMutable, + "Only immutable blob should be passed to worker"); + } +#endif + + // nsIDOMBlobs should be threadsafe, thus we will use the same instance + // on the main thread. + jsval wrappedBlob; + nsresult rv = + nsContentUtils::WrapNative(aCx, JS_GetGlobalForScopeChain(aCx), blob, + &NS_GET_IID(nsIDOMBlob), &wrappedBlob); + if (NS_FAILED(rv)) { + Error(aCx, DATA_CLONE_ERR); + return nsnull; + } + + return JSVAL_TO_OBJECT(wrappedBlob); + } + } + JSObject* clone = WorkerStructuredCloneCallbacks::Read(aCx, aReader, aTag, aData, aClosure); if (clone) { return clone; } JS_ClearPendingException(aCx); return NS_DOMReadStructuredClone(aCx, aReader, aTag, aData, nsnull); } static JSBool Write(JSContext* aCx, JSStructuredCloneWriter* aWriter, JSObject* aObj, void* aClosure) { AssertIsOnMainThread(); + NS_ASSERTION(aClosure, "Null pointer!"); + + // We'll stash any nsISupports pointers that need to be AddRef'd here. + nsTArray<nsCOMPtr<nsISupports> >* clonedObjects = + static_cast<nsTArray<nsCOMPtr<nsISupports> >*>(aClosure); + + // See if this is a wrapped native. + nsCOMPtr<nsIXPConnectWrappedNative> wrappedNative; + nsContentUtils::XPConnect()-> + GetWrappedNativeOfJSObject(aCx, aObj, getter_AddRefs(wrappedNative)); + + if (wrappedNative) { + // Get the raw nsISupports out of it. + nsISupports* wrappedObject = wrappedNative->Native(); + NS_ASSERTION(wrappedObject, "Null pointer?!"); + + // See if the wrapped native is a nsIDOMFile. + nsCOMPtr<nsIDOMFile> file = do_QueryInterface(wrappedObject); + if (file) { + nsCOMPtr<nsIMutable> mutableFile = do_QueryInterface(file); + if (mutableFile && NS_SUCCEEDED(mutableFile->SetMutable(PR_FALSE))) { + nsIDOMFile* filePtr = file; + if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_FILE, 0) && + JS_WriteBytes(aWriter, &filePtr, sizeof(filePtr))) { + clonedObjects->AppendElement(file); + return true; + } + } + } + + // See if the wrapped native is a nsIDOMBlob. + nsCOMPtr<nsIDOMBlob> blob = do_QueryInterface(wrappedObject); + if (blob) { + nsCOMPtr<nsIMutable> mutableBlob = do_QueryInterface(blob); + if (mutableBlob && NS_SUCCEEDED(mutableBlob->SetMutable(PR_FALSE))) { + nsIDOMBlob* blobPtr = blob; + if (JS_WriteUint32Pair(aWriter, DOMWORKER_SCTAG_BLOB, 0) && + JS_WriteBytes(aWriter, &blobPtr, sizeof(blobPtr))) { + clonedObjects->AppendElement(blob); + return true; + } + } + } + } + JSBool ok = WorkerStructuredCloneCallbacks::Write(aCx, aWriter, aObj, aClosure); if (ok) { return ok; } JS_ClearPendingException(aCx); return NS_DOMWriteStructuredClone(aCx, aWriter, aObj, nsnull); @@ -514,25 +712,31 @@ public: aWorkerPrivate->CloseHandlerFinished(); } }; class MessageEventRunnable : public WorkerRunnable { uint64* mData; size_t mDataByteCount; + nsTArray<nsCOMPtr<nsISupports> > mClonedObjects; public: MessageEventRunnable(WorkerPrivate* aWorkerPrivate, Target aTarget, - JSAutoStructuredCloneBuffer& aData) + JSAutoStructuredCloneBuffer& aData, + nsTArray<nsCOMPtr<nsISupports> >& aClonedObjects) : WorkerRunnable(aWorkerPrivate, aTarget, aTarget == WorkerThread ? ModifyBusyCount : UnchangedBusyCount) { aData.steal(&mData, &mDataByteCount); + + if (!mClonedObjects.SwapElements(aClonedObjects)) { + NS_ERROR("This should never fail!"); + } } bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) { JSAutoStructuredCloneBuffer buffer; buffer.adopt(mData, mDataByteCount); @@ -564,17 +768,18 @@ public: NS_ASSERTION(aWorkerPrivate == GetWorkerPrivateFromContext(aCx), "Badness!"); mainRuntime = false; target = JS_GetGlobalObject(aCx); } NS_ASSERTION(target, "This should never be null!"); - JSObject* event = events::CreateMessageEvent(aCx, buffer, mainRuntime); + JSObject* event = events::CreateMessageEvent(aCx, buffer, mClonedObjects, + mainRuntime); if (!event) { return false; } bool dummy; return events::DispatchEventToTarget(aCx, target, event, &dummy); } }; @@ -1385,17 +1590,17 @@ WorkerPrivateParent<Derived>::WorkerPriv nsCOMPtr<nsIURI>& aBaseURI, nsCOMPtr<nsIPrincipal>& aPrincipal, nsCOMPtr<nsIDocument>& aDocument) : mMutex("WorkerPrivateParent Mutex"), mCondVar(mMutex, "WorkerPrivateParent CondVar"), mJSObject(aObject), mParent(aParent), mParentJSContext(aParentJSContext), mScriptURL(aScriptURL), mDomain(aDomain), mBusyCount(0), mParentStatus(Pending), mJSObjectRooted(false), mParentSuspended(false), - mIsChromeWorker(aIsChromeWorker) + mIsChromeWorker(aIsChromeWorker), mPrincipalIsSystem(false) { MOZ_COUNT_CTOR(mozilla::dom::workers::WorkerPrivateParent); if (aWindow) { NS_ASSERTION(aWindow->IsInnerWindow(), "Should have inner window here!"); } mWindow.swap(aWindow); @@ -1688,37 +1893,45 @@ WorkerPrivateParent<Derived>::ForgetMain template <class Derived> bool WorkerPrivateParent<Derived>::PostMessage(JSContext* aCx, jsval aMessage) { AssertIsOnParentThread(); JSStructuredCloneCallbacks* callbacks; if (GetParent()) { - callbacks = nsnull; + if (IsChromeWorker()) { + callbacks = &gChromeWorkerStructuredCloneCallbacks; + } + else { + callbacks = &gWorkerStructuredCloneCallbacks; + } } else { AssertIsOnMainThread(); if (IsChromeWorker()) { callbacks = &gMainThreadChromeWorkerStructuredCloneCallbacks; } else { callbacks = &gMainThreadWorkerStructuredCloneCallbacks; } } + nsTArray<nsCOMPtr<nsISupports> > clonedObjects; + JSAutoStructuredCloneBuffer buffer; - if (!buffer.write(aCx, aMessage, callbacks, nsnull)) { + if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) { return false; } nsRefPtr<MessageEventRunnable> runnable = new MessageEventRunnable(ParentAsWorkerPrivate(), - WorkerRunnable::WorkerThread, buffer); + WorkerRunnable::WorkerThread, buffer, + clonedObjects); return runnable->Dispatch(aCx); } template <class Derived> PRUint64 WorkerPrivateParent<Derived>::GetOuterWindowId() { AssertIsOnMainThread(); @@ -1835,16 +2048,26 @@ WorkerPrivateParent<Derived>::SetBaseURI else { mLocationInfo.mHost.Assign(mLocationInfo.mHostname); } return NS_OK; } template <class Derived> +void +WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal) +{ + AssertIsOnMainThread(); + + mPrincipal = aPrincipal; + mPrincipalIsSystem = nsContentUtils::IsSystemPrincipal(aPrincipal); +} + +template <class Derived> JSContext* WorkerPrivateParent<Derived>::ParentJSContext() const { AssertIsOnParentThread(); if (!mParent) { AssertIsOnMainThread(); @@ -2624,23 +2847,31 @@ WorkerPrivate::StopSyncLoop(PRUint32 aSy syncQueue->mComplete = true; } bool WorkerPrivate::PostMessageToParent(JSContext* aCx, jsval aMessage) { AssertIsOnWorkerThread(); + JSStructuredCloneCallbacks* callbacks = + IsChromeWorker() ? + &gChromeWorkerStructuredCloneCallbacks : + &gWorkerStructuredCloneCallbacks; + + nsTArray<nsCOMPtr<nsISupports> > clonedObjects; + JSAutoStructuredCloneBuffer buffer; - if (!buffer.write(aCx, aMessage, nsnull, nsnull)) { + if (!buffer.write(aCx, aMessage, callbacks, &clonedObjects)) { return false; } nsRefPtr<MessageEventRunnable> runnable = - new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer); + new MessageEventRunnable(this, WorkerRunnable::ParentThread, buffer, + clonedObjects); return runnable->Dispatch(aCx); } bool WorkerPrivate::NotifyInternal(JSContext* aCx, Status aStatus) { AssertIsOnWorkerThread(); @@ -3205,20 +3436,24 @@ template class WorkerPrivateParent<Worke WorkerPrivate* GetWorkerPrivateFromContext(JSContext* aCx) { NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); return static_cast<WorkerPrivate*>(JS_GetContextPrivate(aCx)); } JSStructuredCloneCallbacks* -WorkerStructuredCloneCallbacks() +WorkerStructuredCloneCallbacks(bool aMainRuntime) { - return &gWorkerStructuredCloneCallbacks; + return aMainRuntime ? + &gMainThreadWorkerStructuredCloneCallbacks : + &gWorkerStructuredCloneCallbacks; } JSStructuredCloneCallbacks* -ChromeWorkerStructuredCloneCallbacks() +ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime) { - return &gChromeWorkerStructuredCloneCallbacks; + return aMainRuntime ? + &gMainThreadChromeWorkerStructuredCloneCallbacks : + &gChromeWorkerStructuredCloneCallbacks; } END_WORKERS_NAMESPACE
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -214,16 +214,17 @@ private: PRUint64 mBusyCount; Status mParentStatus; PRUint32 mJSContextOptions; PRUint8 mGCZeal; bool mJSObjectRooted; bool mParentSuspended; bool mIsChromeWorker; + bool mPrincipalIsSystem; protected: WorkerPrivateParent(JSContext* aCx, JSObject* aObject, WorkerPrivate* aParent, JSContext* aParentJSContext, const nsAString& aScriptURL, bool aIsChromeWorker, const nsACString& aDomain, nsCOMPtr<nsPIDOMWindow>& aWindow, nsCOMPtr<nsIScriptContext>& aScriptContext, nsCOMPtr<nsIURI>& aBaseURI, @@ -393,20 +394,22 @@ public: nsIPrincipal* GetPrincipal() const { AssertIsOnMainThread(); return mPrincipal; } void - SetPrincipal(nsIPrincipal* aPrincipal) + SetPrincipal(nsIPrincipal* aPrincipal); + + bool + UsesSystemPrincipal() const { - AssertIsOnMainThread(); - mPrincipal = aPrincipal; + return mPrincipalIsSystem; } nsIDocument* GetDocument() const { AssertIsOnMainThread(); return mDocument; } @@ -740,17 +743,25 @@ private: ClearQueue(&mQueue); ClearQueue(&mControlQueue); } }; WorkerPrivate* GetWorkerPrivateFromContext(JSContext* aCx); -JSStructuredCloneCallbacks* -WorkerStructuredCloneCallbacks(); +enum WorkerStructuredDataType +{ + DOMWORKER_SCTAG_FILE = JS_SCTAG_USER_MIN + 0x1000, + DOMWORKER_SCTAG_BLOB, + + DOMWORKER_SCTAG_END +}; JSStructuredCloneCallbacks* -ChromeWorkerStructuredCloneCallbacks(); +WorkerStructuredCloneCallbacks(bool aMainRuntime); + +JSStructuredCloneCallbacks* +ChromeWorkerStructuredCloneCallbacks(bool aMainRuntime); END_WORKERS_NAMESPACE #endif /* mozilla_dom_workers_workerprivate_h__ */
--- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -1,9 +1,10 @@ /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * @@ -44,16 +45,18 @@ #include "nsTraceRefcnt.h" #include "xpcprivate.h" #include "ChromeWorkerScope.h" #include "Events.h" #include "EventTarget.h" #include "Exceptions.h" +#include "File.h" +#include "FileReaderSync.h" #include "ListenerManager.h" #include "Location.h" #include "Navigator.h" #include "Principal.h" #include "ScriptLoader.h" #include "Worker.h" #include "WorkerPrivate.h" #include "XMLHttpRequest.h" @@ -870,16 +873,18 @@ CreateDedicatedWorkerGlobalScope(JSConte if (!chromeworker::InitClass(aCx, global, workerProto, false) || !chromeworker::DefineChromeWorkerFunctions(aCx, global)) { return NULL; } } // Init other classes we care about. if (!events::InitClasses(aCx, global, false) || + !file::InitClasses(aCx, global) || + !filereadersync::InitClass(aCx, global) || !exceptions::InitClasses(aCx, global) || !xhr::InitClasses(aCx, global, eventTargetProto) || !location::InitClass(aCx, global) || !navigator::InitClass(aCx, global)) { return NULL; } if (!JS_DefineProfilingFunctions(aCx, global)) {
--- a/dom/workers/test/Makefile.in +++ b/dom/workers/test/Makefile.in @@ -115,16 +115,34 @@ include $(topsrcdir)/config/rules.mk relativeLoad_sub_worker.js \ relativeLoad_sub_worker2.js \ relativeLoad_sub_import.js \ $(NULL) _CHROME_TEST_FILES = \ test_chromeWorker.xul \ test_chromeWorkerJSM.xul \ + test_file.xul \ + test_fileMozSlice.xul \ + test_fileBlobPosting.xul \ + test_filePosting.xul \ + test_fileReaderSync.xul \ + test_fileReaderSyncErrors.xul \ + test_fileReadMozSlice.xul \ + test_fileSubWorker.xul \ + test_fileBlobSubWorker.xul \ + file_worker.js \ + fileBlob_worker.js \ + fileMozSlice_worker.js \ + filePosting_worker.js \ + fileReaderSync_worker.js \ + fileReaderSyncErrors_worker.js \ + fileReadMozSlice_worker.js \ + fileSubWorker_worker.js \ + fileBlobSubWorker_worker.js \ WorkerTest.jsm \ WorkerTest_worker.js \ WorkerTest_subworker.js \ chromeWorker_worker.js \ chromeWorker_subworker.js \ test_workersDisabled.xul \ workersDisabled_worker.js \ $(NULL)
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fileBlobSubWorker_worker.js @@ -0,0 +1,17 @@ +/** + * Expects a blob. Returns an object containing the size, type. + * Used to test posting of blob from worker to worker. + */ +onmessage = function(event) { + var worker = new Worker("fileBlob_worker.js"); + + worker.postMessage(event.data); + + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.onerror = function(event) { + postMessage(undefined); + } +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fileBlob_worker.js @@ -0,0 +1,13 @@ +/** + * Expects a blob. Returns an object containing the size, type. + */ +onmessage = function(event) { + var file = event.data; + + var rtnObj = new Object(); + + rtnObj.size = file.size; + rtnObj.type = file.type; + + postMessage(rtnObj); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fileMozSlice_worker.js @@ -0,0 +1,27 @@ +/** + * Expects an object containing a blob, a start offset, an end offset + * and an optional content type to slice the blob. Returns an object + * containing the size and type of the sliced blob. + */ +onmessage = function(event) { + var blob = event.data.blob; + var start = event.data.start; + var end = event.data.end; + var contentType = event.data.contentType; + + var slicedBlob; + if (contentType == undefined && end == undefined) { + slicedBlob = blob.mozSlice(start); + } else if (contentType == undefined) { + slicedBlob = blob.mozSlice(start, end); + } else { + slicedBlob = blob.mozSlice(start, end, contentType); + } + + var rtnObj = new Object(); + + rtnObj.size = slicedBlob.size; + rtnObj.type = slicedBlob.type; + + postMessage(rtnObj); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/filePosting_worker.js @@ -0,0 +1,3 @@ +onmessage = function(event) { + postMessage(event.data); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fileReadMozSlice_worker.js @@ -0,0 +1,16 @@ +/** + * Expects an object containing a blob, a start index and an end index + * for slicing. Returns the contents of the blob read as text. + */ +onmessage = function(event) { + var blob = event.data.blob; + var start = event.data.start; + var end = event.data.end; + + var slicedBlob = blob.mozSlice(start, end); + + var fileReader = new FileReaderSync(); + var text = fileReader.readAsText(slicedBlob); + + postMessage(text); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fileReaderSyncErrors_worker.js @@ -0,0 +1,79 @@ +/** + * Delegates "is" evaluation back to main thread. + */ +function is(actual, expected, message) { + var rtnObj = new Object(); + rtnObj.actual = actual; + rtnObj.expected = expected; + rtnObj.message = message; + postMessage(rtnObj); +} + +/** + * Tries to write to property. + */ +function writeProperty(file, property) { + try { + var oldValue = file[property]; + file[property] = -1; + is(false, true, "Should have thrown an exception setting a read only property."); + } catch (ex) { + is(true, true, "Should have thrown an exception setting a read only property."); + } +} + +/** + * Passes junk arguments to FileReaderSync methods and expects an exception to + * be thrown. + */ +function fileReaderJunkArgument(blob) { + var fileReader = new FileReaderSync(); + + try { + fileReader.readAsBinaryString(blob); + is(false, true, "Should have thrown an exception calling readAsBinaryString."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsDataURL(blob); + is(false, true, "Should have thrown an exception calling readAsDataURL."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsArrayBuffer(blob); + is(false, true, "Should have thrown an exception calling readAsArrayBuffer."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } + + try { + fileReader.readAsText(blob); + is(false, true, "Should have thrown an exception calling readAsText."); + } catch(ex) { + is(true, true, "Should have thrown an exception."); + } +} + +onmessage = function(event) { + var file = event.data; + + // Test read only properties. + writeProperty(file, "size"); + writeProperty(file, "type"); + writeProperty(file, "name"); + writeProperty(file, "mozFullPath"); + + // Bad types. + fileReaderJunkArgument(undefined); + fileReaderJunkArgument(-1); + fileReaderJunkArgument(1); + fileReaderJunkArgument(new Object()); + fileReaderJunkArgument("hello"); + + // Post undefined to indicate that testing has finished. + postMessage(undefined); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fileReaderSync_worker.js @@ -0,0 +1,25 @@ +var reader = new FileReaderSync(); + +/** + * Expects an object containing a file and an encoding then uses a + * FileReaderSync to read the file. Returns an object containing the + * file read a binary string, text, url and ArrayBuffer. + */ +onmessage = function(event) { + var file = event.data.file; + var encoding = event.data.encoding; + + var rtnObj = new Object(); + + if (encoding != undefined) { + rtnObj.text = reader.readAsText(file, encoding); + } else { + rtnObj.text = reader.readAsText(file); + } + + rtnObj.bin = reader.readAsBinaryString(file); + rtnObj.url = reader.readAsDataURL(file); + rtnObj.arrayBuffer = reader.readAsArrayBuffer(file); + + postMessage(rtnObj); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/fileSubWorker_worker.js @@ -0,0 +1,17 @@ +/** + * Expects a file. Returns an object containing the size, type, name and path + * using another worker. Used to test posting of file from worker to worker. + */ +onmessage = function(event) { + var worker = new Worker("file_worker.js"); + + worker.postMessage(event.data); + + worker.onmessage = function(event) { + postMessage(event.data); + } + + worker.onerror = function(event) { + postMessage(undefined); + } +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/file_worker.js @@ -0,0 +1,15 @@ +/** + * Expects a file. Returns an object containing the size, type, name and path. + */ +onmessage = function(event) { + var file = event.data; + + var rtnObj = new Object(); + + rtnObj.size = file.size; + rtnObj.type = file.type; + rtnObj.name = file.name; + rtnObj.mozFullPath = file.mozFullPath; + + postMessage(rtnObj); +};
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_file.xul @@ -0,0 +1,97 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=123456 +--> +<window title="Mozilla Bug 123456" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=123456" + target="_blank">Mozilla Bug 123456</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 123456 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerFile" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker to access file properties. + */ + function accessFileProperties(file, expectedSize, expectedType) { + var worker = new Worker("file_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread."); + is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect."); + is(event.data.name, file.name, "name proproperty accessed from worker is incorrect."); + is(event.data.mozFullPath, file.mozFullPath, "mozFullPath proproperty accessed from worker is not the same as on main thread."); + SimpleTest.finish(); + }; + + worker.postMessage(file); + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + accessFileProperties(createFileWithData(""), 0, ""); + + // Typical use case. + accessFileProperties(createFileWithData("Hello"), 5, ""); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + accessFileProperties(createFileWithData(text), 40000, ""); + + // Type detection based on extension. + accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain"); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_fileBlobPosting.xul @@ -0,0 +1,87 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerBlobPosting" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker which posts the same blob given. Used to test cloning of blobs. + * Checks the size, type, name and path of the file posted from the worker to ensure it + * is the same as the original. + */ + function postBlob(file) { + var worker = new Worker("filePosting_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + console.log(event.data); + is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker."); + SimpleTest.finish(); + }; + + var blob = file.mozSlice(); + worker.postMessage(blob); + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + postBlob(createFileWithData("")); + + // Typical use case. + postBlob(createFileWithData("Hello")); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_fileBlobSubWorker.xul @@ -0,0 +1,99 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerBlobSubWorker" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker to access blob properties. + */ + function accessFileProperties(file, expectedSize) { + var worker = new Worker("fileBlobSubWorker_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + if (event.data == undefined) { + ok(false, "Worker had an error."); + } else { + is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread."); + } + SimpleTest.finish(); + }; + + var blob = file.mozSlice(); + worker.postMessage(blob); + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + accessFileProperties(createFileWithData(""), 0); + + // Typical use case. + accessFileProperties(createFileWithData("Hello"), 5); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + accessFileProperties(createFileWithData(text), 40000); + + // Type detection based on extension. + accessFileProperties(createFileWithData("text", "txt"), 4); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_fileMozSlice.xul @@ -0,0 +1,107 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerMozSlice" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Starts a worker which slices the blob to the given start offset and optional end offset and + * content type. It then verifies that the size and type of the sliced blob is correct. + */ + function createMozSlice(blob, start, expectedLength, /** optional */ end, /** optional */ contentType) { + var worker = new Worker("fileMozSlice_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + is(event.data.size, expectedLength, "size property of slice is incorrect."); + is(event.data.type, contentType ? contentType : blob.type, "type property of slice is incorrect."); + SimpleTest.finish(); + }; + + var params = {blob: blob, start: start, end: end, contentType: contentType}; + worker.postMessage(params); + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + createMozSlice(createFileWithData(""), 0, 0, 0); + + // Typical use case. + createMozSlice(createFileWithData("Hello"), 1, 1, 2); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + createMozSlice(createFileWithData(text), 2000, 2000, 4000); + + // Slice to different type. + createMozSlice(createFileWithData("text", "txt"), 0, 2, 2, "image/png"); + + // Length longer than blob. + createMozSlice(createFileWithData("text"), 0, 4, 20); + + // Start longer than blob. + createMozSlice(createFileWithData("text"), 20, 0, 4); + + // No optional arguments + createMozSlice(createFileWithData("text"), 0, 4); + createMozSlice(createFileWithData("text"), 2, 2); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_filePosting.xul @@ -0,0 +1,88 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerFilePosting" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker which posts the same file given. Used to test cloning of files. + * Checks the size, type, name and path of the file posted from the worker to ensure it + * is the same as the original. + */ + function postFile(file) { + var worker = new Worker("file_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + is(event.data.size, file.size, "size of file posted from worker does not match file posted to worker."); + is(event.data.type, file.type, "type of file posted from worker does not match file posted to worker."); + is(event.data.name, file.name, "name of file posted from worker does not match file posted to worker."); + is(event.data.mozFullPath, file.mozFullPath, "mozFullPath of file posted from worker does not match file posted to worker."); + SimpleTest.finish(); + }; + + worker.postMessage(file); + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + postFile(createFileWithData("")); + + // Typical use case. + postFile(createFileWithData("Hello")); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_fileReadMozSlice.xul @@ -0,0 +1,91 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerReadMozSlice" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Creates a worker which slices a blob to the given start and end offset and + * reads the content as text. + */ + function readMozSlice(blob, start, end, expectedText) { + var worker = new Worker("fileReadMozSlice_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + is(event.data, expectedText, "Text from sliced blob in worker is incorrect."); + SimpleTest.finish(); + }; + + var params = {blob: blob, start: start, end: end}; + worker.postMessage(params); + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + readMozSlice(createFileWithData(""), 0, 0, ""); + + // Typical use case. + readMozSlice(createFileWithData("HelloBye"), 5, 8, "Bye"); + + // End offset too large. + readMozSlice(createFileWithData("HelloBye"), 5, 9, "Bye"); + + // Start of file. + readMozSlice(createFileWithData("HelloBye"), 0, 5, "Hello"); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_fileReaderSync.xul @@ -0,0 +1,200 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerFileReaderSync" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + function convertToUTF16(s) { + res = ""; + for (var i = 0; i < s.length; ++i) { + c = s.charCodeAt(i); + res += String.fromCharCode(c >>> 8, c & 255); + } + return res; + } + + /** + * Converts the given string to a data URL of the specified mime type. + */ + function convertToDataURL(mime, s) { + return "data:" + mime + ";base64," + btoa(s); + } + + /** + * Create a worker to read a file containing fileData using FileReaderSync and + * checks the return type against the expected type. Optionally set an encoding + * for reading the file as text. + */ + function readFileData(fileData, expectedText, /** optional */ encoding) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onmessage = function(event) { + is(event.data.text, expectedText, "readAsText in worker returned incorrect result."); + is(event.data.bin, fileData, "readAsBinaryString in worker returned incorrect result."); + is(event.data.url, convertToDataURL("application/octet-stream", fileData), "readAsDataURL in worker returned incorrect result."); + is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length."); + SimpleTest.finish(); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + var params = {file: createFileWithData(fileData), encoding: encoding}; + + worker.postMessage(params); + + SimpleTest.waitForExplicitFinish(); + } + + /** + * Create a worker which reuses a FileReaderSync to read multiple files as DataURLs. + */ + function reuseReaderForURL(files, expected) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + var k = 0; + worker.onmessage = function(event) { + is(event.data.url, expected[k], "readAsDataURL in worker returned incorrect result when reusing FileReaderSync."); + k++; + SimpleTest.finish(); + }; + + for (var i = 0; i < files.length; ++i) { + var params = {file: files[i], encoding: undefined}; + worker.postMessage(params); + SimpleTest.waitForExplicitFinish(); + } + } + + /** + * Create a worker which reuses a FileReaderSync to read multiple files as text. + */ + function reuseReaderForText(fileData, expected) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + var k = 0; + worker.onmessage = function(event) { + is(event.data.text, expected[k++], "readAsText in worker returned incorrect result when reusing FileReaderSync."); + SimpleTest.finish(); + }; + + for (var i = 0; i < fileData.length; ++i) { + var params = {file: createFileWithData(fileData[i]), encoding: undefined}; + worker.postMessage(params); + SimpleTest.waitForExplicitFinish(); + } + } + + + /** + * Creates a a worker which reads a file containing fileData as an ArrayBuffer. + * Verifies that the ArrayBuffer when interpreted as a string matches the original data. + */ + function readArrayBuffer(fileData) { + var worker = new Worker("fileReaderSync_worker.js"); + + worker.onmessage = function(event) { + var view = new Uint8Array(event.data.arrayBuffer); + is(event.data.arrayBuffer.byteLength, fileData.length, "readAsArrayBuffer returned buffer of incorrect length."); + is(String.fromCharCode.apply(String, view), fileData, "readAsArrayBuffer returned buffer containing incorrect data."); + SimpleTest.finish(); + }; + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + var params = {file: createFileWithData(fileData), encoding: undefined}; + + worker.postMessage(params); + + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + readFileData("", ""); + + // Typical use case. + readFileData("text", "text"); + + // Test reading UTF-16 characters. + readFileData(convertToUTF16("text"), "text", "UTF-16"); + + // First read a file of type "text/plain", then read a file of type "application/octet-stream". + reuseReaderForURL([createFileWithData("text", "txt"), createFileWithData("text")], + [convertToDataURL("text/plain", "text"), + convertToDataURL("application/octet-stream", "text")]); + + // First read UTF-16 characters marked using BOM, then read UTF-8 characters. + reuseReaderForText([convertToUTF16("\ufefftext"), "text"], + ["text", "text"]); + + // Reading data as ArrayBuffer. + readArrayBuffer(""); + readArrayBuffer("text"); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_fileReaderSyncErrors.xul @@ -0,0 +1,85 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data. + */ + function createFileWithData(fileData) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + testFile.append("workerFileReaderSyncErrors" + fileNum++); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Creates a worker which runs errors cases. + */ + function runWorkerErrors(file) { + var worker = new Worker("fileReaderSyncErrors_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + if(event.data == undefined) { + // Worker returns undefined when tests have finished running. + SimpleTest.finish(); + } else { + // Otherwise worker will return results of tests to be evaluated. + is(event.data.actual, event.data.expected, event.data.message); + } + }; + + worker.postMessage(file); + SimpleTest.waitForExplicitFinish(); + } + + // Run worker which creates exceptions. + runWorkerErrors(createFileWithData("text")); + + ]]> + </script> +</window>
new file mode 100644 --- /dev/null +++ b/dom/workers/test/test_fileSubWorker.xul @@ -0,0 +1,101 @@ +<?xml version="1.0"?> +<?xml-stylesheet type="text/css" href="chrome://global/skin"?> +<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=664783 +--> +<window title="Mozilla Bug 664783" + xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> + <script type="application/javascript" + src="chrome://mochikit/content/MochiKit/packed.js"/> + <script type="application/javascript" + src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/> + + <!-- test results are displayed in the html:body --> + <body xmlns="http://www.w3.org/1999/xhtml"> + <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=664783" + target="_blank">Mozilla Bug 664783</a> + + <div id="content" style="display: none"> + <input id="fileList" type="file"></input> + </div> + + </body> + + <!-- test code goes here --> + <script type="application/javascript"> + <![CDATA[ + + /** Test for Bug 664783 **/ + + var fileNum = 0; + + /** + * Create a file which contains the given data and optionally adds the specified file extension. + */ + function createFileWithData(fileData, /** optional */ extension) { + var testFile = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsIFile); + var fileExtension = (extension == undefined) ? "" : "." + extension; + testFile.append("workerSubWorker" + fileNum++ + fileExtension); + + var outStream = Components.classes["@mozilla.org/network/file-output-stream;1"] + .createInstance(Components.interfaces.nsIFileOutputStream); + outStream.init(testFile, 0x02 | 0x08 | 0x20, // write, create, truncate + 0666, 0); + outStream.write(fileData, fileData.length); + outStream.close(); + + var fileList = document.getElementById('fileList'); + fileList.value = testFile.path; + + return fileList.files[0]; + } + + /** + * Create a worker to access file properties. + */ + function accessFileProperties(file, expectedSize, expectedType) { + var worker = new Worker("fileSubWorker_worker.js"); + + worker.onerror = function(event) { + ok(false, "Worker had an error: " + event.data); + SimpleTest.finish(); + }; + + worker.onmessage = function(event) { + if (event.data == undefined) { + ok(false, "Worker had an error."); + } else { + is(event.data.size, expectedSize, "size proproperty accessed from worker is not the same as on main thread."); + is(event.data.type, expectedType, "type proproperty accessed from worker is incorrect."); + is(event.data.name, file.name, "name proproperty accessed from worker is incorrect."); + is(event.data.mozFullPath, file.mozFullPath, "mozFullPath proproperty accessed from worker is not the same as on main thread."); + } + SimpleTest.finish(); + }; + + worker.postMessage(file); + SimpleTest.waitForExplicitFinish(); + } + + // Empty file. + accessFileProperties(createFileWithData(""), 0, ""); + + // Typical use case. + accessFileProperties(createFileWithData("Hello"), 5, ""); + + // Longish file. + var text = ""; + for (var i = 0; i < 10000; ++i) { + text += "long"; + } + accessFileProperties(createFileWithData(text), 40000, ""); + + // Type detection based on extension. + accessFileProperties(createFileWithData("text", "txt"), 4, "text/plain"); + + ]]> + </script> +</window>
--- a/editor/composer/src/nsEditorSpellCheck.cpp +++ b/editor/composer/src/nsEditorSpellCheck.cpp @@ -59,32 +59,50 @@ #include "nsReadableUtils.h" #include "nsITextServicesFilter.h" #include "nsUnicharUtils.h" #include "mozilla/Services.h" #include "mozilla/Preferences.h" using namespace mozilla; +class UpdateDictionnaryHolder { + private: + nsEditorSpellCheck* mSpellCheck; + public: + UpdateDictionnaryHolder(nsEditorSpellCheck* esc): mSpellCheck(esc) { + if (mSpellCheck) { + mSpellCheck->BeginUpdateDictionary(); + } + } + ~UpdateDictionnaryHolder() { + if (mSpellCheck) { + mSpellCheck->EndUpdateDictionary(); + } + } +}; + NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEditorSpellCheck) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditorSpellCheck) NS_INTERFACE_MAP_BEGIN(nsEditorSpellCheck) NS_INTERFACE_MAP_ENTRY(nsIEditorSpellCheck) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditorSpellCheck) NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsEditorSpellCheck) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_2(nsEditorSpellCheck, mSpellChecker, mTxtSrvFilter) nsEditorSpellCheck::nsEditorSpellCheck() : mSuggestedWordIndex(0) , mDictionaryIndex(0) + , mUpdateDictionaryRunning(PR_FALSE) + , mDictWasSetManually(PR_FALSE) { } nsEditorSpellCheck::~nsEditorSpellCheck() { // Make sure we blow the spellchecker away, just in // case it hasn't been destroyed already. mSpellChecker = nsnull; @@ -355,72 +373,60 @@ nsEditorSpellCheck::GetDictionaryList(PR { tmpPtr[i] = ToNewUnicode(dictList[i]); } return rv; } NS_IMETHODIMP -nsEditorSpellCheck::GetCurrentDictionary(PRUnichar **aDictionary) +nsEditorSpellCheck::GetCurrentDictionary(nsAString& aDictionary) { NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); - NS_ENSURE_TRUE(aDictionary, NS_ERROR_NULL_POINTER); - - *aDictionary = 0; - - nsAutoString dictStr; - nsresult rv = mSpellChecker->GetCurrentDictionary(dictStr); - NS_ENSURE_SUCCESS(rv, rv); - - *aDictionary = ToNewUnicode(dictStr); - - return rv; + return mSpellChecker->GetCurrentDictionary(aDictionary); } NS_IMETHODIMP -nsEditorSpellCheck::SetCurrentDictionary(const PRUnichar *aDictionary) +nsEditorSpellCheck::SetCurrentDictionary(const nsAString& aDictionary) { NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); - NS_ENSURE_TRUE(aDictionary, NS_ERROR_NULL_POINTER); - - return mSpellChecker->SetCurrentDictionary(nsDependentString(aDictionary)); + if (!mUpdateDictionaryRunning) { + mDictWasSetManually = PR_TRUE; + } + return mSpellChecker->SetCurrentDictionary(aDictionary); } NS_IMETHODIMP nsEditorSpellCheck::UninitSpellChecker() { NS_ENSURE_TRUE(mSpellChecker, NS_ERROR_NOT_INITIALIZED); // Cleanup - kill the spell checker DeleteSuggestedWordList(); mDictionaryList.Clear(); mDictionaryIndex = 0; mSpellChecker = 0; return NS_OK; } -// Save the last used dictionary to the user's preferences. +// Save the last set dictionary to the user's preferences. NS_IMETHODIMP nsEditorSpellCheck::SaveDefaultDictionary() { - PRUnichar *dictName = nsnull; - nsresult rv = GetCurrentDictionary(&dictName); - - if (NS_SUCCEEDED(rv) && dictName && *dictName) { - rv = Preferences::SetString("spellchecker.dictionary", dictName); + if (!mDictWasSetManually) { + return NS_OK; } - if (dictName) { - nsMemory::Free(dictName); - } + nsAutoString dictName; + nsresult rv = GetCurrentDictionary(dictName); + NS_ENSURE_SUCCESS(rv, rv); - return rv; + return Preferences::SetString("spellchecker.dictionary", dictName); } /* void setFilter (in nsITextServicesFilter filter); */ NS_IMETHODIMP nsEditorSpellCheck::SetFilter(nsITextServicesFilter *filter) { mTxtSrvFilter = filter; @@ -433,18 +439,24 @@ nsEditorSpellCheck::DeleteSuggestedWordL mSuggestedWordList.Clear(); mSuggestedWordIndex = 0; return NS_OK; } NS_IMETHODIMP nsEditorSpellCheck::UpdateCurrentDictionary(nsIEditor* aEditor) { + if (mDictWasSetManually) { // user has set dictionary manually; we better not change it. + return NS_OK; + } + nsresult rv; + UpdateDictionnaryHolder holder(this); + // Tell the spellchecker what dictionary to use: nsAutoString dictName; // First, try to get language with html5 algorithm nsAutoString editorLang; nsCOMPtr<nsIContent> rootContent; @@ -487,65 +499,65 @@ nsEditorSpellCheck::UpdateCurrentDiction if (packageRegistry) { nsCAutoString utf8DictName; rv = packageRegistry->GetSelectedLocale(NS_LITERAL_CSTRING("global"), utf8DictName); AppendUTF8toUTF16(utf8DictName, dictName); } } - SetCurrentDictionary(NS_LITERAL_STRING("").get()); + SetCurrentDictionary(EmptyString()); if (NS_SUCCEEDED(rv) && !dictName.IsEmpty()) { - rv = SetCurrentDictionary(dictName.get()); + rv = SetCurrentDictionary(dictName); if (NS_FAILED(rv)) { // required dictionary was not available. Try to get a dictionary // matching at least language part of dictName: If required dictionary is // "aa-bb", we try "aa", then we try any available dictionary aa-XX nsAutoString langCode; PRInt32 dashIdx = dictName.FindChar('-'); if (dashIdx != -1) { langCode.Assign(Substring(dictName, 0, dashIdx)); // try to use langCode - rv = SetCurrentDictionary(langCode.get()); + rv = SetCurrentDictionary(langCode); } else { langCode.Assign(dictName); } if (NS_FAILED(rv)) { // loop over avaible dictionaries; if we find one with required // language, use it nsTArray<nsString> dictList; rv = mSpellChecker->GetDictionaryList(&dictList); NS_ENSURE_SUCCESS(rv, rv); nsDefaultStringComparator comparator; PRInt32 i, count = dictList.Length(); for (i = 0; i < count; i++) { nsAutoString dictStr(dictList.ElementAt(i)); if (nsStyleUtil::DashMatchCompare(dictStr, langCode, comparator) && - NS_SUCCEEDED(SetCurrentDictionary(dictStr.get()))) { + NS_SUCCEEDED(SetCurrentDictionary(dictStr))) { break; } } } } } // If we have not set dictionary, and the editable element doesn't have a // lang attribute, we try to get a dictionary. First try, en-US. If it does // not work, pick the first one. if (editorLang.IsEmpty()) { - nsAutoString currentDictonary; - rv = mSpellChecker->GetCurrentDictionary(currentDictonary); - if (NS_FAILED(rv) || currentDictonary.IsEmpty()) { - rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US").get()); + nsAutoString currentDictionary; + rv = GetCurrentDictionary(currentDictionary); + if (NS_FAILED(rv) || currentDictionary.IsEmpty()) { + rv = SetCurrentDictionary(NS_LITERAL_STRING("en-US")); if (NS_FAILED(rv)) { nsTArray<nsString> dictList; rv = mSpellChecker->GetDictionaryList(&dictList); if (NS_SUCCEEDED(rv) && dictList.Length() > 0) { - SetCurrentDictionary(dictList[0].get()); + SetCurrentDictionary(dictList[0]); } } } } // If an error was thrown while setting the dictionary, just // fail silently so that the spellchecker dialog is allowed to come // up. The user can manually reset the language to their choice on
--- a/editor/composer/src/nsEditorSpellCheck.h +++ b/editor/composer/src/nsEditorSpellCheck.h @@ -73,13 +73,20 @@ protected: // these are the words in the current personal dictionary, // GetPersonalDictionary must be called to load them. nsTArray<nsString> mDictionaryList; PRInt32 mDictionaryIndex; nsresult DeleteSuggestedWordList(); nsCOMPtr<nsITextServicesFilter> mTxtSrvFilter; + + PRPackedBool mUpdateDictionaryRunning; + PRPackedBool mDictWasSetManually; + +public: + void BeginUpdateDictionary() { mUpdateDictionaryRunning = PR_TRUE ;} + void EndUpdateDictionary() { mUpdateDictionaryRunning = PR_FALSE ;} }; #endif // nsEditorSpellCheck_h___
--- a/editor/composer/test/Makefile.in +++ b/editor/composer/test/Makefile.in @@ -48,15 +48,16 @@ include $(topsrcdir)/config/rules.mk test_bug348497.html \ test_bug384147.html \ test_bug389350.html \ test_bug519928.html \ $(NULL) _CHROME_TEST_FILES = \ test_bug434998.xul \ + test_bug338427.html \ $(NULL) libs:: $(_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir) libs:: $(_CHROME_TEST_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644 --- /dev/null +++ b/editor/composer/test/test_bug338427.html @@ -0,0 +1,54 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=338427 +--> +<head> + <title>Test for Bug 338427</title> + <script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" /> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=338427">Mozilla Bug 338427</a> +<p id="display"></p> +<div id="content"> +<textarea id="editor" lang="testing-XX"></textarea> + +</div> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +/** Test for Bug 338427 **/ +function init() { + var textarea = document.getElementById("editor"); + var editor = textarea.QueryInterface(Components.interfaces.nsIDOMNSEditableElement).editor; + var spellchecker = editor.getInlineSpellChecker(true); + spellchecker.enableRealTimeSpell = true; + + var list = {}, count = {}; + spellchecker.spellChecker.GetDictionaryList(list, count); + if (count.value === 0) { + return; // no dictionary, no test possible + } + var lang = list.value[0]; + spellchecker.spellChecker.SetCurrentDictionary(lang); + + textarea.addEventListener("focus", function() { + var dictionary = ""; + try { + dictionary = spellchecker.spellChecker.GetCurrentDictionary(); + } catch(e) {} + is(dictionary, lang, "Unexpected spell check dictionary"); + SimpleTest.finish(); + }, false); + textarea.focus(); +} + +SimpleTest.waitForExplicitFinish(); +addLoadEvent(init); + +</script> +</pre> +</body> +</html> +
--- a/editor/idl/nsIEditorSpellCheck.idl +++ b/editor/idl/nsIEditorSpellCheck.idl @@ -36,17 +36,17 @@ * * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" interface nsIEditor; interface nsITextServicesFilter; -[scriptable, uuid(803ff0dd-07f2-4438-b3a6-ab9c2fe4e1dd)] +[scriptable, uuid(3da0ce96-7d3a-48d0-80b7-2d90dab09747)] interface nsIEditorSpellCheck : nsISupports { /** * Returns true if we can enable spellchecking. If there are no available * dictionaries, this will return false. */ boolean canSpellCheck(); @@ -138,22 +138,22 @@ interface nsIEditorSpellCheck : nsISuppo * * @see mozISpellCheckingEngine::GetDictionaryList */ void GetDictionaryList([array, size_is(count)] out wstring dictionaryList, out PRUint32 count); /** * @see nsISpellChecker::GetCurrentDictionary */ - wstring GetCurrentDictionary(); + AString GetCurrentDictionary(); /** * @see nsISpellChecker::SetCurrentDictionary */ - void SetCurrentDictionary(in wstring dictionary); + void SetCurrentDictionary(in AString dictionary); /** * Call to save the currently selected dictionary as the default. The * function UninitSpellChecker will also do this, but that function may not * be called in some situations. This function allows the caller to force the * default right now. */ void saveDefaultDictionary();
--- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -57,16 +57,17 @@ import android.content.pm.*; import android.graphics.*; import android.widget.*; import android.hardware.*; import android.location.*; import android.telephony.*; import android.webkit.MimeTypeMap; import android.media.MediaScannerConnection; import android.media.MediaScannerConnection.MediaScannerConnectionClient; +import android.provider.Settings; import android.util.*; import android.net.Uri; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.graphics.drawable.*; import android.graphics.Bitmap; @@ -1361,9 +1362,21 @@ public class GeckoAppShell if (resolveInfo == null) return null; ActivityInfo activityInfo = resolveInfo.activityInfo; return activityInfo.loadIcon(pm); } + + public static boolean getShowPasswordSetting() { + try { + int showPassword = + Settings.System.getInt(GeckoApp.mAppContext.getContentResolver(), + Settings.System.TEXT_SHOW_PASSWORD); + return (showPassword > 0); + } + catch (Exception e) { + return false; + } + } }
--- a/extensions/spellcheck/hunspell/src/Makefile.in +++ b/extensions/spellcheck/hunspell/src/Makefile.in @@ -59,16 +59,18 @@ CPPSRCS += affentry.cpp \ hashmgr.cpp \ hunspell.cpp \ hunzip.cpp \ phonet.cpp \ replist.cpp \ suggestmgr.cpp \ $(NULL) +# This variable is referenced in configure.in. Make sure to change that file +# too if you need to change this variable. DEFINES = -DHUNSPELL_STATIC endif include $(topsrcdir)/config/rules.mk ifdef MOZ_NATIVE_HUNSPELL CXXFLAGS += $(MOZ_HUNSPELL_CFLAGS) endif
new file mode 100644 --- /dev/null +++ b/extensions/spellcheck/hunspell/src/hunspell_alloc_hooks.h @@ -0,0 +1,108 @@ +/******* BEGIN LICENSE BLOCK ******* + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Initial Developers of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developers + * are Copyright (C) 2011 the Initial Developers. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + ******* END LICENSE BLOCK *******/ + +#ifndef alloc_hooks_h__ +#define alloc_hooks_h__ + +/** + * This file is force-included in hunspell code. Its purpose is to add memory + * reporting to hunspell without modifying its code, in order to ease future + * upgrades. + * + * This file is force-included through mozilla-config.h which is generated + * during the configure step. + * + * Currently, the memory allocated using operator new/new[] is not being + * tracked, but that's OK, since almost all of the memory used by Hunspell is + * allocated using C memory allocation functions. + */ + +// Prevent the standard macros from being redefined +#define mozilla_mozalloc_macro_wrappers_h + +#include "mozilla/mozalloc.h" + +extern void HunspellReportMemoryAllocation(void*); +extern void HunspellReportMemoryDeallocation(void*); + +inline void* hunspell_malloc(size_t size) +{ + void* result = moz_malloc(size); + HunspellReportMemoryAllocation(result); + return result; +} +#define malloc(size) hunspell_malloc(size) + +inline void* hunspell_calloc(size_t count, size_t size) +{ + void* result = moz_calloc(count, size); + HunspellReportMemoryAllocation(result); + return result; +} +#define calloc(count, size) hunspell_calloc(count, size) + +inline void hunspell_free(void* ptr) +{ + HunspellReportMemoryDeallocation(ptr); + moz_free(ptr); +} +#define free(ptr) hunspell_free(ptr) + +inline void* hunspell_realloc(void* ptr, size_t size) +{ + HunspellReportMemoryDeallocation(ptr); + void* result = moz_realloc(ptr, size); + HunspellReportMemoryAllocation(result); + return result; +} +#define realloc(ptr, size) hunspell_realloc(ptr, size) + +inline char* hunspell_strdup(const char* str) +{ + char* result = moz_strdup(str); + HunspellReportMemoryAllocation(result); + return result; +} +#define strdup(str) hunspell_strdup(str) + +#if defined(HAVE_STRNDUP) +inline char* hunspell_strndup(const char* str, size_t size) +{ + char* result = moz_strndup(str, size); + HunspellReportMemoryAllocation(result); + return result; +} +#define strndup(str, size) hunspell_strndup(str, size) +#endif + +#endif
--- a/extensions/spellcheck/hunspell/src/mozHunspell.cpp +++ b/extensions/spellcheck/hunspell/src/mozHunspell.cpp @@ -66,16 +66,17 @@ #include "nsDirectoryServiceUtils.h" #include "nsDirectoryServiceDefs.h" #include "mozISpellI18NManager.h" #include "nsICharsetConverterManager.h" #include "nsUnicharUtilCIID.h" #include "nsUnicharUtils.h" #include "nsCRT.h" #include <stdlib.h> +#include "nsIMemoryReporter.h" static NS_DEFINE_CID(kCharsetConverterManagerCID, NS_ICHARSETCONVERTERMANAGER_CID); static NS_DEFINE_CID(kUnicharUtilCID, NS_UNICHARUTIL_CID); NS_IMPL_CYCLE_COLLECTING_ADDREF(mozHunspell) NS_IMPL_CYCLE_COLLECTING_RELEASE(mozHunspell) NS_INTERFACE_MAP_BEGIN(mozHunspell) @@ -86,37 +87,64 @@ NS_INTERFACE_MAP_BEGIN(mozHunspell) NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(mozHunspell) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_3(mozHunspell, mPersonalDictionary, mEncoder, mDecoder) +// Memory reporting stuff +static PRInt64 gHunspellAllocatedSize = 0; + +void HunspellReportMemoryAllocation(void* ptr) { + gHunspellAllocatedSize += moz_malloc_usable_size(ptr); +} +void HunspellReportMemoryDeallocation(void* ptr) { + gHunspellAllocatedSize -= moz_malloc_usable_size(ptr); +} +static PRInt64 HunspellGetCurrentAllocatedSize() { + return gHunspellAllocatedSize; +} + +NS_MEMORY_REPORTER_IMPLEMENT(Hunspell, + "explicit/spell-check", + KIND_HEAP, + UNITS_BYTES, + HunspellGetCurrentAllocatedSize, + "Memory used by the Hunspell spell checking engine. This number accounts " + "for the memory in use by Hunspell's internal data structures." +) + nsresult mozHunspell::Init() { if (!mDictionaries.Init()) return NS_ERROR_OUT_OF_MEMORY; LoadDictionaryList(); nsCOMPtr<nsIObserverService> obs = do_GetService("@mozilla.org/observer-service;1"); if (obs) { obs->AddObserver(this, "profile-do-change", PR_TRUE); } + mHunspellReporter = new NS_MEMORY_REPORTER_NAME(Hunspell); + NS_RegisterMemoryReporter(mHunspellReporter); + return NS_OK; } mozHunspell::~mozHunspell() { mPersonalDictionary = nsnull; delete mHunspell; + + NS_UnregisterMemoryReporter(mHunspellReporter); } /* attribute wstring dictionary; */ NS_IMETHODIMP mozHunspell::GetDictionary(PRUnichar **aDictionary) { NS_ENSURE_ARG_POINTER(aDictionary); if (mDictionary.IsEmpty())
--- a/extensions/spellcheck/hunspell/src/mozHunspell.h +++ b/extensions/spellcheck/hunspell/src/mozHunspell.h @@ -72,27 +72,29 @@ #include "nsCycleCollectionParticipant.h" #define MOZ_HUNSPELL_CONTRACTID "@mozilla.org/spellchecker/engine;1" #define MOZ_HUNSPELL_CID \ /* 56c778e4-1bee-45f3-a689-886692a97fe7 */ \ { 0x56c778e4, 0x1bee, 0x45f3, \ { 0xa6, 0x89, 0x88, 0x66, 0x92, 0xa9, 0x7f, 0xe7 } } +class nsIMemoryReporter; + class mozHunspell : public mozISpellCheckingEngine, public nsIObserver, public nsSupportsWeakReference { public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_MOZISPELLCHECKINGENGINE NS_DECL_NSIOBSERVER NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(mozHunspell, mozISpellCheckingEngine) - mozHunspell() : mHunspell(nsnull) { } + mozHunspell() : mHunspell(nsnull), mHunspellReporter(nsnull) { } virtual ~mozHunspell(); nsresult Init(); void LoadDictionaryList(); // helper method for converting a word to the charset of the dictionary nsresult ConvertCharset(const PRUnichar* aStr, char ** aDst); @@ -104,11 +106,13 @@ protected: nsCOMPtr<nsIUnicodeDecoder> mDecoder; // Hashtable matches dictionary name to .aff file nsInterfaceHashtable<nsStringHashKey, nsIFile> mDictionaries; nsString mDictionary; nsString mLanguage; Hunspell *mHunspell; + + nsIMemoryReporter* mHunspellReporter; }; #endif
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp +++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp @@ -1440,18 +1440,18 @@ mozInlineSpellChecker::ResumeCheck(mozIn nsresult rv = wordUtil.Init(mEditor); if (NS_FAILED(rv)) return NS_OK; // editor doesn't like us, don't assert nsCOMPtr<nsISelection> spellCheckSelection; rv = GetSpellCheckSelection(getter_AddRefs(spellCheckSelection)); NS_ENSURE_SUCCESS(rv, rv); - PRUnichar *currentDictionary = nsnull; - rv = mSpellCheck->GetCurrentDictionary(¤tDictionary); + nsAutoString currentDictionary; + rv = mSpellCheck->GetCurrentDictionary(currentDictionary); if (NS_FAILED(rv)) { // no active dictionary PRInt32 count; spellCheckSelection->GetRangeCount(&count); for (PRInt32 index = count - 1; index >= 0; index--) { nsCOMPtr<nsIDOMRange> checkRange; spellCheckSelection->GetRangeAt(index, getter_AddRefs(checkRange)); if (checkRange) { @@ -1755,36 +1755,27 @@ nsresult mozInlineSpellChecker::KeyPress } NS_IMETHODIMP mozInlineSpellChecker::UpdateCurrentDictionary() { if (!mSpellCheck) { return NS_OK; } - PRUnichar *previousDictionary = nsnull; - nsDependentString previousDictionaryStr; - if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(&previousDictionary))) { - previousDictionaryStr.Assign(previousDictionary); + nsAutoString previousDictionary; + if (NS_FAILED(mSpellCheck->GetCurrentDictionary(previousDictionary))) { + previousDictionary.Truncate(); } nsCOMPtr<nsIEditor> editor (do_QueryReferent(mEditor)); nsresult rv = mSpellCheck->UpdateCurrentDictionary(editor); - PRUnichar *currentDictionary = nsnull; - nsDependentString currentDictionaryStr; - if (NS_SUCCEEDED(mSpellCheck->GetCurrentDictionary(¤tDictionary))) { - currentDictionaryStr.Assign(currentDictionary); + nsAutoString currentDictionary; + if (NS_FAILED(mSpellCheck->GetCurrentDictionary(currentDictionary))) { + currentDictionary.Truncate(); } - if (!previousDictionary || !currentDictionary || !previousDictionaryStr.Equals(currentDictionaryStr)) { + if (!previousDictionary.Equals(currentDictionary)) { rv = SpellCheckRange(nsnull); } - if (previousDictionary) { - nsMemory::Free(previousDictionary); - } - if (currentDictionary) { - nsMemory::Free(currentDictionary); - } - return rv; }
--- a/gfx/2d/DrawTargetD2D.cpp +++ b/gfx/2d/DrawTargetD2D.cpp @@ -905,19 +905,26 @@ DrawTargetD2D::PushClip(const Path *aPat mPushedClips.push_back(clip); // The transform of clips is relative to the world matrix, since we use the total // transform for the clips, make the world matrix identity. mRT->SetTransform(D2D1::IdentityMatrix()); mTransformDirty = true; if (mClipsArePushed) { + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + + if (mFormat == FORMAT_B8G8R8X8) { + options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; + } + mRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), pathD2D->mGeometry, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - clip.mTransform), layer); + clip.mTransform, 1.0f, NULL, + options), layer); } } void DrawTargetD2D::PopClip() { if (mClipsArePushed) { mRT->PopLayer(); @@ -1186,32 +1193,43 @@ DrawTargetD2D::InitD2DRenderTarget() if (!mRT) { return false; } mRT->BeginDraw(); mRT->Clear(D2D1::ColorF(0, 0)); + if (mFormat == FORMAT_B8G8R8X8) { + mRT->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_CLEARTYPE); + } + return InitD3D10Data(); } void DrawTargetD2D::PrepareForDrawing(ID2D1RenderTarget *aRT) { if (!mClipsArePushed || aRT == mTempRT) { if (mPushedClips.size()) { // The transform of clips is relative to the world matrix, since we use the total // transform for the clips, make the world matrix identity. mRT->SetTransform(D2D1::IdentityMatrix()); for (std::vector<PushedClip>::iterator iter = mPushedClips.begin(); iter != mPushedClips.end(); iter++) { + D2D1_LAYER_OPTIONS options = D2D1_LAYER_OPTIONS_NONE; + + if (mFormat == FORMAT_B8G8R8X8) { + options = D2D1_LAYER_OPTIONS_INITIALIZE_FOR_CLEARTYPE; + } + aRT->PushLayer(D2D1::LayerParameters(D2D1::InfiniteRect(), iter->mPath->mGeometry, D2D1_ANTIALIAS_MODE_PER_PRIMITIVE, - iter->mTransform), iter->mLayer); + iter->mTransform, 1.0f, NULL, + options), iter->mLayer); } if (aRT == mRT) { mClipsArePushed = true; } } } mRT->SetTransform(D2DMatrix(mTransform)); MarkChanged(); @@ -1470,18 +1488,24 @@ DrawTargetD2D::CreateRTForTexture(ID3D10 if (FAILED(hr)) { gfxWarning() << "Failed to QI texture to surface."; return NULL; } D3D10_TEXTURE2D_DESC desc; aTexture->GetDesc(&desc); + D2D1_ALPHA_MODE alphaMode = D2D1_ALPHA_MODE_PREMULTIPLIED; + + if (mFormat == FORMAT_B8G8R8X8) { + alphaMode = D2D1_ALPHA_MODE_IGNORE; + } + D2D1_RENDER_TARGET_PROPERTIES props = - D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, D2D1_ALPHA_MODE_PREMULTIPLIED)); + D2D1::RenderTargetProperties(D2D1_RENDER_TARGET_TYPE_DEFAULT, D2D1::PixelFormat(desc.Format, alphaMode)); hr = factory()->CreateDxgiSurfaceRenderTarget(surface, props, byRef(rt)); if (FAILED(hr)) { gfxWarning() << "Failed to create D2D render target for texture."; return NULL; } return rt;
--- a/gfx/angle/Makefile.in +++ b/gfx/angle/Makefile.in @@ -57,16 +57,17 @@ EXPORTS_angle = \ LOCAL_INCLUDES += -I$(srcdir)/include -I$(srcdir)/src VPATH += $(srcdir)/src VPATH += $(srcdir)/src/compiler VPATH += $(srcdir)/src/compiler/preprocessor CPPSRCS = \ Compiler.cpp \ + DetectRecursion.cpp \ InfoSink.cpp \ Initialize.cpp \ InitializeDll.cpp \ Intermediate.cpp \ intermOut.cpp \ IntermTraverse.cpp \ MozAngleLink.cpp \ parseConst.cpp \
--- a/gfx/angle/README.mozilla +++ b/gfx/angle/README.mozilla @@ -1,24 +1,26 @@ This is the ANGLE project, from http://code.google.com/p/angleproject/. -Current revision: r653 +Current revision: r686 == Applied local patches == In this order: angle-nspr-misc.patch - don't bother with ANGLE_OS detection with NSPR angle-renaming.patch - rename debug.h to compilerdebug.h to avoid conflict in our makefiles angle-intrinsic-msvc2005.patch - work around a MSVC 2005 compile error angle-amap-arev-fix.patch - plain bug fix, this is ANGLE r699 angle-r702.patch - this is ANGLE r702 angle-limit-identifiers-to-250-chars.patch - see bug 675625 angle-r712.patch - this is ANGLE r712 angle-win64.patch - Win64 support. This is ANGLE r697 angle-r707.patch - this is ANGLE r707 for Win64 bug fix + angle-r719.patch - this is ANGLE r719 + angle-r711.patch - this is ANGLE r711 In addition to these patches, the Makefile.in files are ours, they're not present in upsteam ANGLE. == How to update this ANGLE copy == 1. Unapply patches 2. Apply diff with new ANGLE version 3. Reapply patches.
--- a/gfx/angle/src/compiler/Compiler.cpp +++ b/gfx/angle/src/compiler/Compiler.cpp @@ -1,14 +1,15 @@ // // Copyright (c) 2002-2010 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // +#include "compiler/DetectRecursion.h" #include "compiler/Initialize.h" #include "compiler/ParseHelper.h" #include "compiler/ShHandle.h" #include "compiler/ValidateLimitations.h" #include "compiler/MapLongVariableNames.h" namespace { bool InitializeSymbolTable( @@ -140,23 +141,26 @@ bool TCompiler::compile(const char* cons // Parse shader. bool success = (PaParseStrings(numStrings - firstSource, &shaderStrings[firstSource], NULL, &parseContext) == 0) && (parseContext.treeRoot != NULL); if (success) { TIntermNode* root = parseContext.treeRoot; success = intermediate.postProcess(root); + if (success) + success = detectRecursion(root); + if (success && (compileOptions & SH_VALIDATE_LOOP_INDEXING)) success = validateLimitations(root); // Call mapLongVariableNames() before collectAttribsUniforms() so in // collectAttribsUniforms() we already have the mapped symbol names and // we could composite mapped and original variable names. - if (compileOptions & SH_MAP_LONG_VARIABLE_NAMES) + if (success && (compileOptions & SH_MAP_LONG_VARIABLE_NAMES)) mapLongVariableNames(root); if (success && (compileOptions & SH_ATTRIBUTES_UNIFORMS)) collectAttribsUniforms(root); if (success && (compileOptions & SH_INTERMEDIATE_TREE)) intermediate.outputTree(root); @@ -188,16 +192,35 @@ void TCompiler::clearResults() infoSink.info.erase(); infoSink.obj.erase(); infoSink.debug.erase(); attribs.clear(); uniforms.clear(); } +bool TCompiler::detectRecursion(TIntermNode* root) +{ + DetectRecursion detect; + root->traverse(&detect); + switch (detect.detectRecursion()) { + case DetectRecursion::kErrorNone: + return true; + case DetectRecursion::kErrorMissingMain: + infoSink.info.message(EPrefixError, "Missing main()"); + return false; + case DetectRecursion::kErrorRecursion: + infoSink.info.message(EPrefixError, "Function recursion detected"); + return false; + default: + UNREACHABLE(); + return false; + } +} + bool TCompiler::validateLimitations(TIntermNode* root) { ValidateLimitations validate(shaderType, infoSink.info); root->traverse(&validate); return validate.numErrors() == 0; } void TCompiler::collectAttribsUniforms(TIntermNode* root) {
new file mode 100644 --- /dev/null +++ b/gfx/angle/src/compiler/DetectRecursion.cpp @@ -0,0 +1,158 @@ +// +// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +#include "compiler/DetectRecursion.h" + +DetectRecursion::FunctionNode::FunctionNode(const TString& fname) + : name(fname), + visit(PreVisit) +{ +} + +const TString& DetectRecursion::FunctionNode::getName() const +{ + return name; +} + +void DetectRecursion::FunctionNode::addCallee( + DetectRecursion::FunctionNode* callee) +{ + for (size_t i = 0; i < callees.size(); ++i) { + if (callees[i] == callee) + return; + } + callees.push_back(callee); +} + +bool DetectRecursion::FunctionNode::detectRecursion() +{ + ASSERT(visit == PreVisit); + visit = InVisit; + for (size_t i = 0; i < callees.size(); ++i) { + switch (callees[i]->visit) { + case InVisit: + // cycle detected, i.e., recursion detected. + return true; + case PostVisit: + break; + case PreVisit: { + bool recursion = callees[i]->detectRecursion(); + if (recursion) + return true; + break; + } + default: + UNREACHABLE(); + break; + } + } + visit = PostVisit; + return false; +} + +DetectRecursion::DetectRecursion() + : currentFunction(NULL) +{ +} + +DetectRecursion::~DetectRecursion() +{ + for (int i = 0; i < functions.size(); ++i) + delete functions[i]; +} + +void DetectRecursion::visitSymbol(TIntermSymbol*) +{ +} + +void DetectRecursion::visitConstantUnion(TIntermConstantUnion*) +{ +} + +bool DetectRecursion::visitBinary(Visit, TIntermBinary*) +{ + return true; +} + +bool DetectRecursion::visitUnary(Visit, TIntermUnary*) +{ + return true; +} + +bool DetectRecursion::visitSelection(Visit, TIntermSelection*) +{ + return true; +} + +bool DetectRecursion::visitAggregate(Visit visit, TIntermAggregate* node) +{ + switch (node->getOp()) + { + case EOpPrototype: + // Function declaration. + // Don't add FunctionNode here because node->getName() is the + // unmangled function name. + break; + case EOpFunction: { + // Function definition. + if (visit == PreVisit) { + currentFunction = findFunctionByName(node->getName()); + if (currentFunction == NULL) { + currentFunction = new FunctionNode(node->getName()); + functions.push_back(currentFunction); + } + } + break; + } + case EOpFunctionCall: { + // Function call. + if (visit == PreVisit) { + ASSERT(currentFunction != NULL); + FunctionNode* func = findFunctionByName(node->getName()); + if (func == NULL) { + func = new FunctionNode(node->getName()); + functions.push_back(func); + } + currentFunction->addCallee(func); + } + break; + } + default: + break; + } + return true; +} + +bool DetectRecursion::visitLoop(Visit, TIntermLoop*) +{ + return true; +} + +bool DetectRecursion::visitBranch(Visit, TIntermBranch*) +{ + return true; +} + +DetectRecursion::ErrorCode DetectRecursion::detectRecursion() +{ + FunctionNode* main = findFunctionByName("main("); + if (main == NULL) + return kErrorMissingMain; + if (main->detectRecursion()) + return kErrorRecursion; + return kErrorNone; +} + +DetectRecursion::FunctionNode* DetectRecursion::findFunctionByName( + const TString& name) +{ + for (size_t i = 0; i < functions.size(); ++i) { + if (functions[i]->getName() == name) + return functions[i]; + } + return NULL; +} +
new file mode 100644 --- /dev/null +++ b/gfx/angle/src/compiler/DetectRecursion.h @@ -0,0 +1,67 @@ +// +// Copyright (c) 2002-2011 The ANGLE Project Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// + +#ifndef COMPILER_DETECT_RECURSION_H_ +#define COMPILER_DETECT_RECURSION_H_ + +#include "GLSLANG/ShaderLang.h" + +#include "compiler/intermediate.h" +#include "compiler/VariableInfo.h" + +// Traverses intermediate tree to detect function recursion. +class DetectRecursion : public TIntermTraverser { +public: + enum ErrorCode { + kErrorMissingMain, + kErrorRecursion, + kErrorNone + }; + + DetectRecursion(); + ~DetectRecursion(); + + virtual void visitSymbol(TIntermSymbol*); + virtual void visitConstantUnion(TIntermConstantUnion*); + virtual bool visitBinary(Visit, TIntermBinary*); + virtual bool visitUnary(Visit, TIntermUnary*); + virtual bool visitSelection(Visit, TIntermSelection*); + virtual bool visitAggregate(Visit, TIntermAggregate*); + virtual bool visitLoop(Visit, TIntermLoop*); + virtual bool visitBranch(Visit, TIntermBranch*); + + ErrorCode detectRecursion(); + +private: + class FunctionNode { + public: + FunctionNode(const TString& fname); + + const TString& getName() const; + + // If a function is already in the callee list, this becomes a no-op. + void addCallee(FunctionNode* callee); + + // Return true if recursive function calls are detected. + bool detectRecursion(); + + private: + // mangled function name is unique. + TString name; + + // functions that are directly called by this function. + TVector<FunctionNode*> callees; + + Visit visit; + }; + + FunctionNode* findFunctionByName(const TString& name); + + TVector<FunctionNode*> functions; + FunctionNode* currentFunction; +}; + +#endif // COMPILER_DETECT_RECURSION_H_
--- a/gfx/angle/src/compiler/ShHandle.h +++ b/gfx/angle/src/compiler/ShHandle.h @@ -61,16 +61,18 @@ public: protected: ShShaderType getShaderType() const { return shaderType; } ShShaderSpec getShaderSpec() const { return shaderSpec; } // Initialize symbol-table with built-in symbols. bool InitBuiltInSymbolTable(const ShBuiltInResources& resources); // Clears the results from the previous compilation. void clearResults(); + // Return true if function recursion is detected. + bool detectRecursion(TIntermNode* root); // Returns true if the given shader does not exceed the minimum // functionality mandated in GLSL 1.0 spec Appendix A. bool validateLimitations(TIntermNode* root); // Collect info for all attribs and uniforms. void collectAttribsUniforms(TIntermNode* root); // Map long variable names into shorter ones. void mapLongVariableNames(TIntermNode* root); // Translate to object code.
--- a/gfx/angle/src/libEGL/Makefile.in +++ b/gfx/angle/src/libEGL/Makefile.in @@ -71,16 +71,17 @@ VPATH += $(srcdir)/.. \ $(srcdir)/../compiler/preprocessor \ $(srcdir)/../common \ $(NULL) # Translator/compiler first CPPSRCS = \ Compiler.cpp \ + DetectRecursion.cpp \ InfoSink.cpp \ Initialize.cpp \ InitializeDll.cpp \ Intermediate.cpp \ intermOut.cpp \ IntermTraverse.cpp \ MozAngleLink.cpp \ parseConst.cpp \
--- a/gfx/angle/src/libGLESv2/Context.cpp +++ b/gfx/angle/src/libGLESv2/Context.cpp @@ -2104,26 +2104,26 @@ void Context::readPixels(GLint x, GLint IDirect3DDevice9 *device = getDevice(); D3DSURFACE_DESC desc; renderTarget->GetDesc(&desc); IDirect3DSurface9 *systemSurface; HRESULT result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, NULL); - if (result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY) + if (FAILED(result)) { + ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); return error(GL_OUT_OF_MEMORY); } - ASSERT(SUCCEEDED(result)); - if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) { UNIMPLEMENTED(); // FIXME: Requires resolve using StretchRect into non-multisampled render target + return error(GL_OUT_OF_MEMORY); } result = device->GetRenderTargetData(renderTarget, systemSurface); if (FAILED(result)) { systemSurface->Release();
--- a/gfx/angle/src/libGLESv2/Makefile.in +++ b/gfx/angle/src/libGLESv2/Makefile.in @@ -71,16 +71,17 @@ VPATH += $(srcdir)/.. VPATH += $(srcdir)/../compiler VPATH += $(srcdir)/../compiler/preprocessor VPATH += $(srcdir)/../common # Translator/compiler first CPPSRCS = \ Compiler.cpp \ + DetectRecursion.cpp \ InfoSink.cpp \ Initialize.cpp \ InitializeDll.cpp \ Intermediate.cpp \ intermOut.cpp \ IntermTraverse.cpp \ MozAngleLink.cpp \ parseConst.cpp \
--- a/gfx/thebes/GLContext.cpp +++ b/gfx/thebes/GLContext.cpp @@ -429,16 +429,18 @@ static const char *sExtensionNames[] = { "GL_OES_depth_texture", "GL_OES_packed_depth_stencil", "GL_IMG_read_format", "GL_EXT_read_format_bgra", "GL_APPLE_client_storage", "GL_ARB_texture_non_power_of_two", "GL_ARB_pixel_buffer_object", "GL_ARB_ES2_compatibility", + "GL_OES_texture_float", + "GL_ARB_texture_float", NULL }; void GLContext::InitExtensions() { MakeCurrent(); const GLubyte *extensions = fGetString(LOCAL_GL_EXTENSIONS);
--- a/gfx/thebes/GLContext.h +++ b/gfx/thebes/GLContext.h @@ -954,16 +954,18 @@ public: OES_depth_texture, OES_packed_depth_stencil, IMG_read_format, EXT_read_format_bgra, APPLE_client_storage, ARB_texture_non_power_of_two, ARB_pixel_buffer_object, ARB_ES2_compatibility, + OES_texture_float, + ARB_texture_float, Extensions_Max }; PRBool IsExtensionSupported(GLExtensions aKnownExtension) { return mAvailableExtensions[aKnownExtension]; } // for unknown extensions
--- a/gfx/thebes/gfxASurface.cpp +++ b/gfx/thebes/gfxASurface.cpp @@ -525,17 +525,16 @@ gfxASurface::BytePerPixelFromFormat(gfxI return 0; } void gfxASurface::FastMovePixels(const nsIntRect& aSourceRect, const nsIntPoint& aDestTopLeft) { // Used when the backend can internally handle self copies. - gfxIntSize size = GetSize(); nsIntRect dest(aDestTopLeft, aSourceRect.Size()); nsRefPtr<gfxContext> ctx = new gfxContext(this); ctx->SetOperator(gfxContext::OPERATOR_SOURCE); nsIntPoint srcOrigin = dest.TopLeft() - aSourceRect.TopLeft(); ctx->SetSource(this, gfxPoint(srcOrigin.x, srcOrigin.y)); ctx->Rectangle(gfxRect(dest.x, dest.y, dest.width, dest.height)); ctx->Fill();
--- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -2605,35 +2605,16 @@ gfxFontGroup::FindFontForChar(PRUint32 a // 1. check fonts in the font group for (PRUint32 i = 0; i < FontListLength(); i++) { nsRefPtr<gfxFont> font = GetFontAt(i); if (font->HasCharacter(aCh)) { *aMatchType = gfxTextRange::kFontGroup; return font.forget(); } - // check other faces of the family - // XXX optimization point: give the family a charmap that is the union - // of the char maps of all its faces, so we can quickly test whether - // it's worth doing this search - gfxFontFamily *family = font->GetFontEntry()->Family(); - if (family) { - FontSearch matchData(aCh, font); - family->FindFontForChar(&matchData); - gfxFontEntry *fe = matchData.mBestMatch; - if (fe) { - PRBool needsBold = - font->GetStyle()->weight >= 600 && !fe->IsBold(); - selectedFont = - fe->FindOrMakeFont(font->GetStyle(), needsBold); - if (selectedFont) { - return selectedFont.forget(); - } - } - } } // if character is in Private Use Area, don't do matching against pref or system fonts if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD)) return nsnull; // 2. search pref fonts if ((selectedFont = WhichPrefFontSupportsChar(aCh))) {
--- a/ipc/testshell/XPCShellEnvironment.cpp +++ b/ipc/testshell/XPCShellEnvironment.cpp @@ -554,21 +554,16 @@ JSFunctionSpec gGlobalFunctions[] = {"gc", GC, 0,0}, #ifdef JS_GC_ZEAL {"gczeal", GCZeal, 1,0}, #endif {"clear", Clear, 1,0}, #ifdef DEBUG {"dumpHeap", DumpHeap, 5,0}, #endif -#ifdef MOZ_CALLGRIND - {"startCallgrind", js_StartCallgrind, 0,0}, - {"stopCallgrind", js_StopCallgrind, 0,0}, - {"dumpCallgrind", js_DumpCallgrind, 1,0}, -#endif {nsnull,nsnull,0,0} }; typedef enum JSShellErrNum { #define MSG_DEF(name, number, count, exception, format) \ name = number, #include "jsshell.msg"
--- a/js/src/config/config.mk +++ b/js/src/config/config.mk @@ -83,16 +83,19 @@ check-variable = $(if $(filter-out 0 1,$ core_abspath = $(if $(findstring :,$(1)),$(1),$(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))) nullstr := space :=$(nullstr) # EOL core_winabspath = $(firstword $(subst /, ,$(call core_abspath,$(1)))):$(subst $(space),,$(patsubst %,\\%,$(wordlist 2,$(words $(subst /, ,$(call core_abspath,$(1)))), $(strip $(subst /, ,$(call core_abspath,$(1))))))) +# LIBXUL_DIST is not defined under js/src, thus we make it mean DIST there. +LIBXUL_DIST ?= $(DIST) + # FINAL_TARGET specifies the location into which we copy end-user-shipped # build products (typelibs, components, chrome). # # It will usually be the well-loved $(DIST)/bin, today, but can also be an # XPI-contents staging directory for ambitious and right-thinking extensions. FINAL_TARGET = $(if $(XPI_NAME),$(DIST)/xpi-stage/$(XPI_NAME),$(DIST)/bin) ifdef XPI_NAME @@ -738,20 +741,20 @@ MERGE_FILE = $(firstword \ $(wildcard $(LOCALE_SRCDIR)/$(1)) \ $(srcdir)/en-US/$(1) ) else MERGE_FILE = $(LOCALE_SRCDIR)/$(1) endif MERGE_FILES = $(foreach f,$(1),$(call MERGE_FILE,$(f))) ifeq (OS2,$(OS_ARCH)) -RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(DIST)" +RUN_TEST_PROGRAM = $(topsrcdir)/build/os2/test_os2.cmd "$(LIBXUL_DIST)" else ifneq (WINNT,$(OS_ARCH)) -RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh +RUN_TEST_PROGRAM = $(LIBXUL_DIST)/bin/run-mozilla.sh endif # ! WINNT endif # ! OS2 # # Java macros # # Make sure any compiled classes work with at least JVM 1.4
--- a/js/src/jsapi-tests/testIndexToString.cpp +++ b/js/src/jsapi-tests/testIndexToString.cpp @@ -38,20 +38,20 @@ static const struct TestPair { { 1000, "1000" }, { 4095, "4095" }, { 4096, "4096" }, { 9999, "9999" }, { 1073741823, "1073741823" }, { 1073741824, "1073741824" }, { 1073741825, "1073741825" }, { 2147483647, "2147483647" }, - { 2147483648, "2147483648" }, - { 2147483649, "2147483649" }, - { 4294967294, "4294967294" }, - { 4294967295, "4294967295" }, + { 2147483648u, "2147483648" }, + { 2147483649u, "2147483649" }, + { 4294967294u, "4294967294" }, + { 4294967295u, "4294967295" }, }; BEGIN_TEST(testIndexToString) { for (size_t i = 0, sz = JS_ARRAY_LENGTH(tests); i < sz; i++) { uint32 u = tests[i].num; JSString *str = js::IndexToString(cx, u); CHECK(str);
--- a/js/src/jsatom.cpp +++ b/js/src/jsatom.cpp @@ -769,19 +769,17 @@ js_InternNonIntElementIdSlow(JSContext * jsid *idp) { JS_ASSERT(idval.isObject()); if (obj->isXML()) { *idp = OBJECT_TO_JSID(&idval.toObject()); return true; } - if (!js_IsFunctionQName(cx, &idval.toObject(), idp)) - return JS_FALSE; - if (!JSID_IS_VOID(*idp)) + if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx)) return true; return js_ValueToStringId(cx, idval, idp); } bool js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval, jsid *idp, Value *vp) @@ -789,19 +787,17 @@ js_InternNonIntElementIdSlow(JSContext * JS_ASSERT(idval.isObject()); if (obj->isXML()) { JSObject &idobj = idval.toObject(); *idp = OBJECT_TO_JSID(&idobj); vp->setObject(idobj); return true; } - if (!js_IsFunctionQName(cx, &idval.toObject(), idp)) - return JS_FALSE; - if (!JSID_IS_VOID(*idp)) { + if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx)) { *vp = IdToValue(*idp); return true; } if (js_ValueToStringId(cx, idval, idp)) { vp->setString(JSID_TO_STRING(*idp)); return true; }
--- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -530,17 +530,17 @@ struct JSRuntime JSTraceDataOp gcExtraRootsTraceOp; void *gcExtraRootsData; /* Well-known numbers held for use by this runtime's contexts. */ js::Value NaNValue; js::Value negativeInfinityValue; js::Value positiveInfinityValue; - JSFlatString *emptyString; + JSAtom *emptyString; /* List of active contexts sharing this runtime; protected by gcLock. */ JSCList contextList; /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ JSDebugHooks globalDebugHooks; /* If true, new compartments are initially in debug mode. */
--- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -38,16 +38,17 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ /* * JS debugging API. */ #include <string.h> +#include <stdarg.h> #include "jsprvtd.h" #include "jstypes.h" #include "jsstdint.h" #include "jsutil.h" #include "jsclist.h" #include "jshashtable.h" #include "jsapi.h" #include "jscntxt.h" @@ -78,16 +79,20 @@ #include "vm/Stack-inl.h" #include "jsautooplen.h" #include "methodjit/MethodJIT.h" #include "methodjit/Retcon.h" +#ifdef __APPLE__ +#include "sharkctl.h" +#endif + using namespace js; using namespace js::gc; JS_PUBLIC_API(JSBool) JS_GetDebugMode(JSContext *cx) { return cx->compartment->debugMode(); } @@ -1208,66 +1213,364 @@ JS_SetContextDebugHooks(JSContext *cx, c } JS_PUBLIC_API(JSDebugHooks *) JS_ClearContextDebugHooks(JSContext *cx) { return JS_SetContextDebugHooks(cx, &js_NullDebugHooks); } +/************************************************************************/ + +/* Profiling-related API */ + +/* Thread-unsafe error management */ + +static char gLastError[2000]; + +static void +#ifdef _GNU_SOURCE +__attribute__((unused,format(printf,1,2))) +#endif +UnsafeError(const char *format, ...) +{ + va_list args; + va_start(args, format); + (void) vsnprintf(gLastError, sizeof(gLastError), format, args); + va_end(args); + + gLastError[sizeof(gLastError) - 1] = '\0'; +} + +JS_PUBLIC_API(const char *) +JS_UnsafeGetLastProfilingError() +{ + return gLastError; +} + JS_PUBLIC_API(JSBool) -JS_StartProfiling() +JS_StartProfiling(const char *profileName) { - return Probes::startProfiling(); + JSBool ok = JS_TRUE; +#if defined(MOZ_SHARK) && defined(__APPLE__) + if (!Shark::Start()) { + UnsafeError("Failed to start Shark for %s", profileName); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (!js_StartVtune(profileName)) + ok = JS_FALSE; +#endif + return ok; +} + +JS_PUBLIC_API(JSBool) +JS_StopProfiling(const char *profileName) +{ + JSBool ok = JS_TRUE; +#if defined(MOZ_SHARK) && defined(__APPLE__) + Shark::Stop(); +#endif +#ifdef MOZ_VTUNE + if (!js_StopVtune()) + ok = JS_FALSE; +#endif + return ok; } -JS_PUBLIC_API(void) -JS_StopProfiling() +/* + * Start or stop whatever platform- and configuration-specific profiling + * backends are available. + */ +static JSBool +ControlProfilers(bool toState) { - Probes::stopProfiling(); + JSBool ok = JS_TRUE; + + if (! Probes::ProfilingActive && toState) { +#if defined(MOZ_SHARK) && defined(__APPLE__) + if (!Shark::Start()) { + UnsafeError("Failed to start Shark"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_CALLGRIND + if (! js_StartCallgrind()) { + UnsafeError("Failed to start Callgrind"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (! js_ResumeVtune()) + ok = JS_FALSE; +#endif + } else if (Probes::ProfilingActive && ! toState) { +#if defined(MOZ_SHARK) && defined(__APPLE__) + Shark::Stop(); +#endif +#ifdef MOZ_CALLGRIND + if (! js_StopCallgrind()) { + UnsafeError("failed to stop Callgrind"); + ok = JS_FALSE; + } +#endif +#ifdef MOZ_VTUNE + if (! js_PauseVtune()) + ok = JS_FALSE; +#endif + } + + Probes::ProfilingActive = toState; + + return ok; +} + +/* + * Pause/resume whatever profiling mechanism is currently compiled + * in, if applicable. This will not affect things like dtrace. + * + * Do not mix calls to these APIs with calls to the individual + * profilers' pause/resume functions, because only overall state is + * tracked, not the state of each profiler. + */ +JS_PUBLIC_API(JSBool) +JS_PauseProfilers(const char *profileName) +{ + return ControlProfilers(false); +} + +JS_PUBLIC_API(JSBool) +JS_ResumeProfilers(const char *profileName) +{ + return ControlProfilers(true); +} + +JS_PUBLIC_API(JSBool) +JS_DumpProfile(const char *outfile, const char *profileName) +{ + JSBool ok = JS_TRUE; +#ifdef MOZ_CALLGRIND + js_DumpCallgrind(outfile); +#endif + return ok; } #ifdef MOZ_PROFILING +struct RequiredStringArg { + JSContext *mCx; + char *mBytes; + RequiredStringArg(JSContext *cx, uintN argc, jsval *vp, size_t argi, const char *caller) + : mCx(cx), mBytes(NULL) + { + if (argc <= argi) { + JS_ReportError(cx, "%s: not enough arguments", caller); + } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) { + JS_ReportError(cx, "%s: invalid arguments (string expected)", caller); + } else { + mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi])); + } + } + operator void*() { + return (void*) mBytes; + } + ~RequiredStringArg() { + if (mBytes) + mCx->free_(mBytes); + } +}; + static JSBool StartProfiling(JSContext *cx, uintN argc, jsval *vp) { - JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling())); - return true; + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes))); + return JS_TRUE; } static JSBool StopProfiling(JSContext *cx, uintN argc, jsval *vp) { - JS_StopProfiling(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +PauseProfilers(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +ResumeProfilers(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL))); + return JS_TRUE; + } + + RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes))); + return JS_TRUE; +} + +/* Usage: DumpProfile([filename[, profileName]]) */ +static JSBool +DumpProfile(JSContext *cx, uintN argc, jsval *vp) +{ + bool ret; + if (argc == 0) { + ret = JS_DumpProfile(NULL, NULL); + } else { + RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile"); + if (!filename) + return JS_FALSE; + + if (argc == 1) { + ret = JS_DumpProfile(filename.mBytes, NULL); + } else { + RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile"); + if (!profileName) + return JS_FALSE; + + ret = JS_DumpProfile(filename.mBytes, profileName.mBytes); + } + } + + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret)); return true; } #ifdef MOZ_SHARK static JSBool IgnoreAndReturnTrue(JSContext *cx, uintN argc, jsval *vp) { JS_SET_RVAL(cx, vp, JSVAL_TRUE); return true; } #endif +#ifdef MOZ_CALLGRIND +static JSBool +StartCallgrind(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind())); + return JS_TRUE; +} + +static JSBool +StopCallgrind(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind())); + return JS_TRUE; +} + +static JSBool +DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) +{ + if (argc == 0) { + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL))); + return JS_TRUE; + } + + RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind"); + if (!outFile) + return JS_FALSE; + + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes))); + return JS_TRUE; +} +#endif + +#ifdef MOZ_VTUNE +static JSBool +StartVtune(JSContext *cx, uintN argc, jsval *vp) +{ + RequiredStringArg profileName(cx, argc, vp, 0, "startVtune"); + if (!profileName) + return JS_FALSE; + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartVtune(profileName.mBytes))); + return JS_TRUE; +} + +static JSBool +StopVtune(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopVtune())); + return JS_TRUE; +} + +static JSBool +PauseVtune(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_PauseVtune())); + return JS_TRUE; +} + +static JSBool +ResumeVtune(JSContext *cx, uintN argc, jsval *vp) +{ + JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_ResumeVtune())); + return JS_TRUE; +} +#endif + static JSFunctionSpec profiling_functions[] = { - JS_FN("startProfiling", StartProfiling, 0,0), - JS_FN("stopProfiling", StopProfiling, 0,0), + JS_FN("startProfiling", StartProfiling, 1,0), + JS_FN("stopProfiling", StopProfiling, 1,0), + JS_FN("pauseProfilers", PauseProfilers, 1,0), + JS_FN("resumeProfilers", ResumeProfilers, 1,0), + JS_FN("dumpProfile", DumpProfile, 2,0), #ifdef MOZ_SHARK /* Keep users of the old shark API happy. */ JS_FN("connectShark", IgnoreAndReturnTrue, 0,0), JS_FN("disconnectShark", IgnoreAndReturnTrue, 0,0), JS_FN("startShark", StartProfiling, 0,0), JS_FN("stopShark", StopProfiling, 0,0), #endif +#ifdef MOZ_CALLGRIND + JS_FN("startCallgrind", StartCallgrind, 0,0), + JS_FN("stopCallgrind", StopCallgrind, 0,0), + JS_FN("dumpCallgrind", DumpCallgrind, 1,0), +#endif +#ifdef MOZ_VTUNE + JS_FN("startVtune", js_StartVtune, 1,0), + JS_FN("stopVtune", js_StopVtune, 0,0), + JS_FN("pauseVtune", js_PauseVtune, 0,0), + JS_FN("resumeVtune", js_ResumeVtune, 0,0), +#endif JS_FS_END }; #endif JS_PUBLIC_API(JSBool) JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj) { @@ -1279,50 +1582,40 @@ JS_DefineProfilingFunctions(JSContext *c #endif } #ifdef MOZ_CALLGRIND #include <valgrind/callgrind.h> JS_FRIEND_API(JSBool) -js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_StartCallgrind() { CALLGRIND_START_INSTRUMENTATION; CALLGRIND_ZERO_STATS; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; -} - -JS_FRIEND_API(JSBool) -js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp) -{ - CALLGRIND_STOP_INSTRUMENTATION; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + return true; } JS_FRIEND_API(JSBool) -js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp) +js_StopCallgrind() { - JSString *str; + CALLGRIND_STOP_INSTRUMENTATION; + return true; +} - jsval *argv = JS_ARGV(cx, vp); - if (argc > 0 && JSVAL_IS_STRING(argv[0])) { - str = JSVAL_TO_STRING(argv[0]); - JSAutoByteString bytes(cx, str); - if (!!bytes) { - CALLGRIND_DUMP_STATS_AT(bytes.ptr()); - return JS_TRUE; - } +JS_FRIEND_API(JSBool) +js_DumpCallgrind(const char *outfile) +{ + if (outfile) { + CALLGRIND_DUMP_STATS_AT(outfile); + } else { + CALLGRIND_DUMP_STATS; } - CALLGRIND_DUMP_STATS; - JS_SET_RVAL(cx, vp, JSVAL_VOID); - return JS_TRUE; + return true; } #endif /* MOZ_CALLGRIND */ #ifdef MOZ_VTUNE #include <VTuneApi.h> static const char *vtuneErrorMessages[] = { @@ -1347,18 +1640,18 @@ static const char *vtuneErrorMessages[] "invalid 'event size' field", "sampling file already bound", "invalid event path", "invalid license", "invalid 'global options' field", }; -JS_FRIEND_API(JSBool) -js_StartVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_StartVtune(const char *profileName) { VTUNE_EVENT events[] = { { 1000000, 0, 0, 0, "CPU_CLK_UNHALTED.CORE" }, { 1000000, 0, 0, 0, "INST_RETIRED.ANY" }, }; U32 n_events = sizeof(events) / sizeof(VTUNE_EVENT); char *default_filename = "mozilla-vtune.tb5"; @@ -1375,72 +1668,64 @@ js_StartVtune(JSContext *cx, uintN argc, 0.1, /* Sampling interval in ms */ 1, /* 1 for event-based sampling, 0 for time-based */ n_events, events, default_filename, }; - jsval *argv = JS_ARGV(cx, vp); - if (argc > 0 && JSVAL_IS_STRING(argv[0])) { - str = JSVAL_TO_STRING(argv[0]); - params.tb5Filename = DeflateString(cx, str->chars(), str->length()); + if (profileName) { + char filename[strlen(profileName) + strlen("-vtune.tb5") + 1]; + snprintf(filename, sizeof(filename), "%s-vtune.tb5", profileName); + params.tb5Filename = filename; } status = VTStartSampling(¶ms); if (params.tb5Filename != default_filename) - cx->free_(params.tb5Filename); + Foreground::free_(params.tb5Filename); if (status != 0) { if (status == VTAPI_MULTIPLE_RUNS) VTStopSampling(0); if (status < sizeof(vtuneErrorMessages)) - JS_ReportError(cx, "Vtune setup error: %s", - vtuneErrorMessages[status]); + UnsafeError("Vtune setup error: %s", vtuneErrorMessages[status]); else - JS_ReportError(cx, "Vtune setup error: %d", - status); + UnsafeError("Vtune setup error: %d", status); return false; } - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_StopVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_StopVtune() { U32 status = VTStopSampling(1); if (status) { if (status < sizeof(vtuneErrorMessages)) - JS_ReportError(cx, "Vtune shutdown error: %s", - vtuneErrorMessages[status]); + UnsafeError("Vtune shutdown error: %s", vtuneErrorMessages[status]); else - JS_ReportError(cx, "Vtune shutdown error: %d", - status); + UnsafeError("Vtune shutdown error: %d", status); return false; } - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_PauseVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_PauseVtune() { VTPause(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } -JS_FRIEND_API(JSBool) -js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp) +bool +js_ResumeVtune() { VTResume(); - JS_SET_RVAL(cx, vp, JSVAL_VOID); return true; } #endif /* MOZ_VTUNE */ #ifdef MOZ_TRACEVIS /* * Ethogram - Javascript wrapper for TraceVis state @@ -1901,37 +2186,34 @@ JS_PUBLIC_API(JSFunctionCallback) JS_GetFunctionCallback(JSContext *cx) { return cx->functionCallback; } #endif /* MOZ_TRACE_JSCALLS */ JS_PUBLIC_API(void) -JS_DumpProfile(JSContext *cx, JSScript *script) +JS_DumpBytecode(JSContext *cx, JSScript *script) { JS_ASSERT(!cx->runtime->gcRunning); #if defined(DEBUG) - if (script->pcCounters) { - // Display hit counts for every JS code line - AutoArenaAllocator mark(&cx->tempPool); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); + AutoArenaAllocator mark(&cx->tempPool); + Sprinter sprinter; + INIT_SPRINTER(cx, &sprinter, &cx->tempPool, 0); - fprintf(stdout, "--- PC COUNTS %s:%d ---\n", script->filename, script->lineno); - js_Disassemble(cx, script, true, &sprinter); - fprintf(stdout, "%s\n", sprinter.base); - fprintf(stdout, "--- END PC COUNTS %s:%d ---\n", script->filename, script->lineno); - } + fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); + js_Disassemble(cx, script, true, &sprinter); + fprintf(stdout, "%s\n", sprinter.base); + fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno); #endif } JS_PUBLIC_API(void) -JS_DumpAllProfiles(JSContext *cx) +JS_DumpCompartmentBytecode(JSContext *cx) { for (JSScript *script = (JSScript *) JS_LIST_HEAD(&cx->compartment->scripts); script != (JSScript *) &cx->compartment->scripts; script = (JSScript *) JS_NEXT_LINK((JSCList *)script)) { - JS_DumpProfile(cx, script); + JS_DumpBytecode(cx, script); } }
--- a/js/src/jsdbgapi.h +++ b/js/src/jsdbgapi.h @@ -492,55 +492,102 @@ JS_GetGlobalDebugHooks(JSRuntime *rt); extern JS_PUBLIC_API(JSDebugHooks *) JS_SetContextDebugHooks(JSContext *cx, const JSDebugHooks *hooks); /* Disable debug hooks for this context. */ extern JS_PUBLIC_API(JSDebugHooks *) JS_ClearContextDebugHooks(JSContext *cx); +/** + * Start any profilers that are available and have been configured on for this + * platform. This is NOT thread safe. + * + * The profileName is used by some profilers to describe the current profiling + * run. It may be used for part of the filename of the output, but the + * specifics depend on the profiler. Many profilers will ignore it. Passing in + * NULL is legal; some profilers may use it to output to stdout or similar. + * + * Returns true if no profilers fail to start. + */ extern JS_PUBLIC_API(JSBool) -JS_StartProfiling(); +JS_StartProfiling(const char *profileName); + +/** + * Stop any profilers that were previously started with JS_StartProfiling. + * Returns true if no profilers fail to stop. + */ +extern JS_PUBLIC_API(JSBool) +JS_StopProfiling(const char *profileName); -extern JS_PUBLIC_API(void) -JS_StopProfiling(); +/** + * Write the current profile data to the given file, if applicable to whatever + * profiler is being used. + */ +extern JS_PUBLIC_API(JSBool) +JS_DumpProfile(const char *outfile, const char *profileName); +/** + * Pause currently active profilers (only supported by some profilers). Returns + * whether any profilers failed to pause. (Profilers that do not support + * pause/resume do not count.) + */ +extern JS_PUBLIC_API(JSBool) +JS_PauseProfilers(const char *profileName); + +/** + * Resume suspended profilers + */ +extern JS_PUBLIC_API(JSBool) +JS_ResumeProfilers(const char *profileName); + +/** + * Add various profiling-related functions as properties of the given object. + */ extern JS_PUBLIC_API(JSBool) JS_DefineProfilingFunctions(JSContext *cx, JSObject *obj); /* Defined in vm/Debugger.cpp. */ extern JS_PUBLIC_API(JSBool) JS_DefineDebuggerObject(JSContext *cx, JSObject *obj); +/** + * The profiling API calls are not able to report errors, so they use a + * thread-unsafe global memory buffer to hold the last error encountered. This + * should only be called after something returns false. + */ +JS_PUBLIC_API(const char *) +JS_UnsafeGetLastProfilingError(); + #ifdef MOZ_CALLGRIND extern JS_FRIEND_API(JSBool) -js_StopCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_StopCallgrind(); extern JS_FRIEND_API(JSBool) -js_StartCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_StartCallgrind(); extern JS_FRIEND_API(JSBool) -js_DumpCallgrind(JSContext *cx, uintN argc, jsval *vp); +js_DumpCallgrind(const char *outfile); #endif /* MOZ_CALLGRIND */ #ifdef MOZ_VTUNE -extern JS_FRIEND_API(JSBool) -js_StartVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_StartVtune(const char *profileName); -extern JS_FRIEND_API(JSBool) -js_StopVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_StopVtune(); -extern JS_FRIEND_API(JSBool) -js_PauseVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_PauseVtune(); -extern JS_FRIEND_API(JSBool) -js_ResumeVtune(JSContext *cx, uintN argc, jsval *vp); +extern JS_FRIEND_API(bool) +js_ResumeVtune(); #endif /* MOZ_VTUNE */ #ifdef MOZ_TRACEVIS extern JS_FRIEND_API(JSBool) js_InitEthogram(JSContext *cx, uintN argc, jsval *vp); extern JS_FRIEND_API(JSBool) js_ShutdownEthogram(JSContext *cx, uintN argc, jsval *vp); @@ -564,16 +611,16 @@ typedef void (*JSFunctionCallback)(const extern JS_PUBLIC_API(void) JS_SetFunctionCallback(JSContext *cx, JSFunctionCallback fcb); extern JS_PUBLIC_API(JSFunctionCallback) JS_GetFunctionCallback(JSContext *cx); #endif /* MOZ_TRACE_JSCALLS */ extern JS_PUBLIC_API(void) -JS_DumpProfile(JSContext *cx, JSScript *script); +JS_DumpBytecode(JSContext *cx, JSScript *script); extern JS_PUBLIC_API(void) -JS_DumpAllProfiles(JSContext *cx); +JS_DumpCompartmentBytecode(JSContext *cx); JS_END_EXTERN_C #endif /* jsdbgapi_h___ */
--- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2197,17 +2197,16 @@ SweepCompartments(JSContext *cx, JSGCInv while (read < end) { JSCompartment *compartment = *read++; if (!compartment->hold && (compartment->arenaListsAreEmpty() || gckind == GC_LAST_CONTEXT)) { compartment->freeLists.checkEmpty(); - Probes::GCEndSweepPhase(compartment); if (callback) JS_ALWAYS_TRUE(callback(cx, compartment, JSCOMPARTMENT_DESTROY)); if (compartment->principals) JSPRINCIPALS_DROP(cx, compartment->principals); cx->delete_(compartment); continue; } *write++ = compartment; @@ -2333,23 +2332,25 @@ MarkAndSweep(JSContext *cx, JSCompartmen /* * We finalize objects before other GC things to ensure that object's finalizer * can access them even if they will be freed. Sweep the runtime's property trees * after finalizing objects, in case any had watchpoints referencing tree nodes. * Do this before sweeping compartments, so that we sweep all shapes in * unreachable compartments. */ if (comp) { + Probes::GCStartSweepPhase(comp); comp->sweep(cx, 0); comp->finalizeObjectArenaLists(cx); GCTIMESTAMP(sweepObjectEnd); comp->finalizeStringArenaLists(cx); GCTIMESTAMP(sweepStringEnd); comp->finalizeShapeArenaLists(cx); GCTIMESTAMP(sweepShapeEnd); + Probes::GCEndSweepPhase(comp); } else { /* * Some sweeping is not compartment-specific. Start a NULL-compartment * phase to demarcate all of that. (The compartment sweeps will nest * within.) */ Probes::GCStartSweepPhase(NULL);
--- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -533,19 +533,17 @@ js_OnUnknownMethod(JSContext *cx, Value return false; if (tvr.value().isPrimitive()) { vp[0] = tvr.value(); } else { #if JS_HAS_XML_SUPPORT /* Extract the function name from function::name qname. */ if (vp[0].isObject()) { obj = &vp[0].toObject(); - if (!js_IsFunctionQName(cx, obj, &id)) - return false; - if (!JSID_IS_VOID(id)) + if (js_GetLocalNameFromFunctionQName(obj, &id, cx)) vp[0] = IdToValue(id); } #endif obj = js_NewGCObject(cx, FINALIZE_OBJECT2); if (!obj) return false; obj->init(cx, &js_NoSuchMethodClass, NULL, NULL, NULL, false);
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1078,19 +1078,19 @@ struct JSObject : js::gc::Cell { inline JSLinearString *getNameURI() const; inline jsval getNameURIVal() const; inline void setNameURI(JSLinearString *uri); inline jsval getNamespaceDeclared() const; inline void setNamespaceDeclared(jsval decl); - inline JSLinearString *getQNameLocalName() const; + inline JSAtom *getQNameLocalName() const; inline jsval getQNameLocalNameVal() const; - inline void setQNameLocalName(JSLinearString *name); + inline void setQNameLocalName(JSAtom *name); /* * Proxy-specific getters and setters. */ inline js::JSProxyHandler *getProxyHandler() const; inline const js::Value &getProxyPrivate() const; inline void setProxyPrivate(const js::Value &priv);
--- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -640,33 +640,33 @@ JSObject::getNamespaceDeclared() const inline void JSObject::setNamespaceDeclared(jsval decl) { JS_ASSERT(isNamespace()); setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl)); } -inline JSLinearString * +inline JSAtom * JSObject::getQNameLocalName() const { JS_ASSERT(isQName()); const js::Value &v = getSlot(JSSLOT_QNAME_LOCAL_NAME); - return !v.isUndefined() ? &v.toString()->asLinear() : NULL; + return !v.isUndefined() ? &v.toString()->asAtom() : NULL; } inline jsval JSObject::getQNameLocalNameVal() const { JS_ASSERT(isQName()); return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME)); } inline void -JSObject::setQNameLocalName(JSLinearString *name) +JSObject::setQNameLocalName(JSAtom *name) { JS_ASSERT(isQName()); setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue()); } inline JSObject * JSObject::getWithThis() const {
--- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -544,17 +544,17 @@ extern size_t GetBytecodeLength(JSContext *cx, JSScript *script, jsbytecode *pc); extern bool IsValidBytecodeOffset(JSContext *cx, JSScript *script, size_t offset); inline bool FlowsIntoNext(JSOp op) { - // JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. + /* JSOP_YIELD is considered to flow into the next instruction, like JSOP_CALL. */ return op != JSOP_STOP && op != JSOP_RETURN && op != JSOP_RETRVAL && op != JSOP_THROW && op != JSOP_GOTO && op != JSOP_GOTOX && op != JSOP_RETSUB; } } #endif #if defined(DEBUG) && defined(__cplusplus)
--- a/js/src/jsparse.cpp +++ b/js/src/jsparse.cpp @@ -1119,17 +1119,17 @@ Compiler::compileScript(JSContext *cx, J script = JSScript::NewScriptFromCG(cx, &cg); if (!script) goto out; JS_ASSERT(script->savedCallerFun == savedCallerFun); { - AutoShapeRooter shapeRoot(cx, script->bindings.lastShape()); + AutoScriptRooter root(cx, script); if (!defineGlobals(cx, globalScope, script)) goto late_error; } out: JS_FinishArenaPool(&codePool); JS_FinishArenaPool(¬ePool); Probes::compileScriptEnd(cx, script, filename, lineno);
--- a/js/src/jsprobes.cpp +++ b/js/src/jsprobes.cpp @@ -48,154 +48,98 @@ #include "jsdbgapi.h" #include "jsfun.h" #include "jsinterp.h" #include "jsobj.h" #include "jsscript.h" #include "jsstaticcheck.h" #include "jsstr.h" -#ifdef __APPLE__ -#include "sharkctl.h" -#endif - #include "jsprobes.h" #include <sys/types.h> #define TYPEOF(cx,v) (JSVAL_IS_NULL(v) ? JSTYPE_NULL : JS_TypeOfValue(cx,v)) using namespace js; const char Probes::nullName[] = "(null)"; const char Probes::anonymousName[] = "(anonymous)"; bool Probes::ProfilingActive = true; -bool -Probes::controlProfilers(JSContext *cx, bool toState) +#ifdef INCLUDE_MOZILLA_DTRACE +static const char * +ScriptFilename(const JSScript *script) { - JSBool ok = JS_TRUE; -#if defined(MOZ_CALLGRIND) || defined(MOZ_VTUNE) - jsval dummy; -#endif + if (!script) + return Probes::nullName; + if (!script->filename) + return Probes::anonymousName; + return script->filename; +} + +static const char * +FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes) +{ + if (!fun) + return Probes::nullName; + JSAtom *atom = const_cast<JSAtom*>(fun->atom); + if (!atom) + return Probes::anonymousName; + return bytes->encode(cx, atom) ? bytes->ptr() : Probes::nullName; +} - if (! ProfilingActive && toState) { -#if defined(MOZ_SHARK) && defined(__APPLE__) - if (!Shark::Start()) - ok = JS_FALSE; -#endif -#ifdef MOZ_CALLGRIND - if (! js_StartCallgrind(cx, 0, &dummy)) - ok = JS_FALSE; -#endif -#ifdef MOZ_VTUNE - if (! js_ResumeVtune(cx, 0, &dummy)) - ok = JS_FALSE; -#endif - } else if (ProfilingActive && ! toState) { -#if defined(MOZ_SHARK) && defined(__APPLE__) - Shark::Stop(); -#endif -#ifdef MOZ_CALLGRIND - if (! js_StopCallgrind(cx, 0, &dummy)) - ok = JS_FALSE; -#endif -#ifdef MOZ_VTUNE - if (! js_PauseVtune(cx, 0, &dummy)) - ok = JS_FALSE; -#endif - } +static const char * +FunctionClassname(const JSFunction *fun) +{ + if (!fun || FUN_INTERPRETED(fun)) + return Probes::nullName; + if (!(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun)) + return (char *)FUN_CLASP(fun)->name; + return Probes::nullName; +} - ProfilingActive = toState; - - return ok; +/* + * These functions call the DTrace macros for the JavaScript USDT probes. + * Originally this code was inlined in the JavaScript code; however since + * a number of operations are called, these have been placed into functions + * to reduce any negative compiler optimization effect that the addition of + * a number of usually unused lines of code would cause. + */ +void +Probes::DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script) +{ + JSAutoByteString funNameBytes; + JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun), + FunctionName(cx, fun, &funNameBytes)); } void -Probes::current_location(JSContext *cx, int* lineno, char const **filename) +Probes::DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script) +{ + JSAutoByteString funNameBytes; + JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun), + FunctionName(cx, fun, &funNameBytes)); +} +#endif + +#ifdef MOZ_ETW +static void +current_location(JSContext *cx, int* lineno, char const **filename) { JSScript *script = js_GetCurrentScript(cx); if (! script) { *lineno = -1; *filename = "(uninitialized)"; return; } *lineno = js_PCToLineNumber(cx, script, js_GetCurrentBytecodePC(cx)); *filename = ScriptFilename(script); } -const char * -Probes::FunctionClassname(const JSFunction *fun) -{ - return (fun && !FUN_INTERPRETED(fun) && !(fun->flags & JSFUN_TRCINFO) && FUN_CLASP(fun)) - ? (char *)FUN_CLASP(fun)->name - : nullName; -} - -const char * -Probes::ScriptFilename(JSScript *script) -{ - return (script && script->filename) ? (char *)script->filename : nullName; -} - -int -Probes::FunctionLineNumber(JSContext *cx, const JSFunction *fun) -{ - if (fun && FUN_INTERPRETED(fun)) - return (int) JS_GetScriptBaseLineNumber(cx, FUN_SCRIPT(fun)); - - return 0; -} - -#ifdef INCLUDE_MOZILLA_DTRACE -/* - * These functions call the DTrace macros for the JavaScript USDT probes. - * Originally this code was inlined in the JavaScript code; however since - * a number of operations are called, these have been placed into functions - * to reduce any negative compiler optimization effect that the addition of - * a number of usually unused lines of code would cause. - */ -void -Probes::enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script) -{ - JSAutoByteString funNameBytes; - JAVASCRIPT_FUNCTION_ENTRY(ScriptFilename(script), FunctionClassname(fun), - FunctionName(cx, fun, &funNameBytes)); -} - -void -Probes::handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script) -{ - JSAutoByteString funNameBytes; - JAVASCRIPT_FUNCTION_RETURN(ScriptFilename(script), FunctionClassname(fun), - FunctionName(cx, fun, &funNameBytes)); -} - -#endif - -bool -Probes::startProfiling() -{ -#if defined(MOZ_SHARK) && defined(__APPLE__) - if (Shark::Start()) - return true; -#endif - return false; -} - -void -Probes::stopProfiling() -{ -#if defined(MOZ_SHARK) && defined(__APPLE__) - Shark::Stop(); -#endif -} - - -#ifdef MOZ_ETW /* * ETW (Event Tracing for Windows) * * These are here rather than in the .h file to avoid having to include * windows.h in a header. */ bool Probes::ETWCallTrackingActive(JSContext *cx)
--- a/js/src/jsprobes.h +++ b/js/src/jsprobes.h @@ -9,20 +9,24 @@ * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * - * Copyright (C) 2007 Sun Microsystems, Inc. All Rights Reserved. + * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released + * June 12, 2009. + * + * The Initial Developer of the Original Code is + * the Mozilla Corporation. * * Contributor(s): - * Brendan Eich <brendan@mozilla.org> + * Steve Fink <sfink@mozilla.org> * * Alternatively, the contents of this file may be used under the terms of * either of the GNU General Public License Version 2 or later (the "GPL"), * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your @@ -39,178 +43,214 @@ #ifdef INCLUDE_MOZILLA_DTRACE #include "javascript-trace.h" #endif #include "jspubtd.h" #include "jsprvtd.h" namespace js { -class Probes { - static bool ProfilingActive; - static bool controlProfilers(JSContext *cx, bool toState); - - static const char nullName[]; - static const char anonymousName[]; - - static const char *FunctionName(JSContext *cx, const JSFunction *fun, JSAutoByteString* bytes) - { - if (!fun) - return nullName; - JSAtom *atom = const_cast<JSAtom*>(fun->atom); - if (!atom) - return anonymousName; - return bytes->encode(cx, atom) ? bytes->ptr() : nullName; - } +namespace Probes { - static const char *ScriptFilename(const JSScript *script) { - if (! script) - return "(null)"; - if (! script->filename) - return "(anonymous)"; - return script->filename; - } +/* + * Static probes + * + * The probe points defined in this file are scattered around the SpiderMonkey + * source tree. The presence of Probes::someEvent() means that someEvent is + * about to happen or has happened. To the extent possible, probes should be + * inserted in all paths associated with a given event, regardless of the + * active runmode (interpreter/traceJIT/methodJIT/ionJIT). + * + * When a probe fires, it is handled by any probe handling backends that have + * been compiled in. By default, most probes do nothing or at least do nothing + * expensive, so the presence of the probe should have negligible effect on + * running time. (Probes in slow paths may do something by default, as long as + * there is no noticeable slowdown.) + * + * For some probes, the mere existence of the probe is too expensive even if it + * does nothing when called. For example, just having consistent information + * available for a function call entry/exit probe causes the JITs to + * de-optimize function calls. In those cases, the JITs may query at compile + * time whether a probe is desired, and omit the probe invocation if not. If a + * probe is runtime-disabled at compilation time, it is not guaranteed to fire + * within a compiled function if it is later enabled. + * + * Not all backends handle all of the probes listed here. + */ - static const char *ObjectClassname(JSObject *obj) { - if (! obj) - return "(null object)"; - Class *clasp = obj->getClass(); - if (! clasp) - return "(null)"; - const char *class_name = clasp->name; - if (! class_name) - return "(null class name)"; - return class_name; - } +/* + * Internal use only: remember whether "profiling", whatever that means, is + * currently active. Used for state management. + */ +extern bool ProfilingActive; + +extern const char nullName[]; +extern const char anonymousName[]; - static void current_location(JSContext *cx, int* lineno, char const **filename); +/* JSRuntime created, with currently valid fields */ +bool createRuntime(JSRuntime *rt); + +/* JSRuntime about to be destroyed */ +bool destroyRuntime(JSRuntime *rt); + +/* Total JS engine shutdown */ +bool shutdown(); - static const char *FunctionClassname(const JSFunction *fun); - static const char *ScriptFilename(JSScript *script); - static int FunctionLineNumber(JSContext *cx, const JSFunction *fun); +/* + * Test whether we are tracking JS function call enter/exit. The JITs use this + * to decide whether they can optimize in a way that would prevent probes from + * firing. + */ +bool callTrackingActive(JSContext *); + +/* Entering a JS function */ +bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1); - static void enterJSFunImpl(JSContext *cx, JSFunction *fun, JSScript *script); - static void handleFunctionReturn(JSContext *cx, JSFunction *fun, JSScript *script); - static void finalizeObjectImpl(JSObject *obj); - public: - static bool createRuntime(JSRuntime *rt); - static bool destroyRuntime(JSRuntime *rt); - static bool shutdown(); +/* About to leave a JS function */ +bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0); + +/* Executing a script */ +bool startExecution(JSContext *cx, JSScript *script); + +/* Script has completed execution */ +bool stopExecution(JSContext *cx, JSScript *script); + +/* Heap has been resized */ +bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); - /* - * Pause/resume whatever profiling mechanism is currently compiled - * in, if applicable. This will not affect things like dtrace. - * - * Do not mix calls to these APIs with calls to the individual - * profilers' pase/resume functions, because only overall state is - * tracked, not the state of each profiler. - * - * Return the previous state. - */ - static bool pauseProfilers(JSContext *cx) { - bool prevState = ProfilingActive; - controlProfilers(cx, false); - return prevState; - } - static bool resumeProfilers(JSContext *cx) { - bool prevState = ProfilingActive; - controlProfilers(cx, true); - return prevState; - } +/* + * Object has been created. |obj| must exist (its class and size are read) + */ +bool createObject(JSContext *cx, JSObject *obj); + +/* Object has been resized */ +bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); + +/* + * Object is about to be finalized. |obj| must still exist (its class is + * read) + */ +bool finalizeObject(JSObject *obj); - static bool callTrackingActive(JSContext *); +/* + * String has been created. + * + * |string|'s content is not (yet) valid. |length| is the length of the string + * and does not imply anything about the amount of storage consumed to store + * the string. (It may be a short string, an external string, or a rope, and + * the encoding is not taken into consideration.) + */ +bool createString(JSContext *cx, JSString *string, size_t length); - static bool enterJSFun(JSContext *, JSFunction *, JSScript *, int counter = 1); - static bool exitJSFun(JSContext *, JSFunction *, JSScript *, int counter = 0); - - static bool startExecution(JSContext *cx, JSScript *script); - static bool stopExecution(JSContext *cx, JSScript *script); +/* + * String is about to be finalized + * + * |string| must still have a valid length. + */ +bool finalizeString(JSString *string); - static bool resizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); +/* Script is about to be compiled */ +bool compileScriptBegin(JSContext *cx, const char *filename, int lineno); - /* |obj| must exist (its class and size are computed) */ - static bool createObject(JSContext *cx, JSObject *obj); +/* Script has just finished compilation */ +bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno); - static bool resizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); +/* About to make a call from JS into native code */ +bool calloutBegin(JSContext *cx, JSFunction *fun); - /* |obj| must still exist (its class is accessed) */ - static bool finalizeObject(JSObject *obj); +/* Native code called by JS has terminated */ +bool calloutEnd(JSContext *cx, JSFunction *fun); + +/* Unimplemented */ +bool acquireMemory(JSContext *cx, void *address, size_t nbytes); +bool releaseMemory(JSContext *cx, void *address, size_t nbytes); - /* - * |string| does not need to contain any content yet; only its - * pointer value is used. |length| is the length of the string and - * does not imply anything about the amount of storage consumed to - * store the string. (It may be a short string, an external - * string, or a rope, and the encoding is not taken into - * consideration.) - */ - static bool createString(JSContext *cx, JSString *string, size_t length); +/* + * Garbage collection probes + * + * GC timing is tricky and at the time of this writing is changing frequently. + * GCStart(NULL)/GCEnd(NULL) are intended to bracket the entire garbage + * collection (either global or single-compartment), but a separate thread may + * continue doing work after GCEnd. + * + * Multiple compartments' GC will be interleaved during a global collection + * (eg, compartment 1 starts, compartment 2 starts, compartment 1 ends, ...) + */ +bool GCStart(JSCompartment *compartment); +bool GCEnd(JSCompartment *compartment); - /* - * |string| must still have a valid length. - */ - static bool finalizeString(JSString *string); +bool GCStartMarkPhase(JSCompartment *compartment); +bool GCEndMarkPhase(JSCompartment *compartment); - static bool compileScriptBegin(JSContext *cx, const char *filename, int lineno); - static bool compileScriptEnd(JSContext *cx, JSScript *script, const char *filename, int lineno); +bool GCStartSweepPhase(JSCompartment *compartment); +bool GCEndSweepPhase(JSCompartment *compartment); - static bool calloutBegin(JSContext *cx, JSFunction *fun); - static bool calloutEnd(JSContext *cx, JSFunction *fun); - - static bool acquireMemory(JSContext *cx, void *address, size_t nbytes); - static bool releaseMemory(JSContext *cx, void *address, size_t nbytes); - - static bool GCStart(JSCompartment *compartment); - static bool GCEnd(JSCompartment *compartment); - static bool GCStartMarkPhase(JSCompartment *compartment); +/* + * Various APIs for inserting custom probe points. These might be used to mark + * when something starts and stops, or for various other purposes the user has + * in mind. These are useful to export to JS so that JS code can mark + * application-meaningful events and phases of execution. + * + * Not all backends support these. + */ +bool CustomMark(JSString *string); +bool CustomMark(const char *string); +bool CustomMark(int marker); - static bool GCEndMarkPhase(JSCompartment *compartment); - static bool GCStartSweepPhase(JSCompartment *compartment); - static bool GCEndSweepPhase(JSCompartment *compartment); +/* + * Internal: DTrace-specific functions to be called during Probes::enterJSFun + * and Probes::exitJSFun. These will not be inlined, but the argument + * marshalling required for these probe points is expensive enough that it + * shouldn't really matter. + */ +void DTraceEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script); +void DTraceExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script); - static bool CustomMark(JSString *string); - static bool CustomMark(const char *string); - static bool CustomMark(int marker); - - static bool startProfiling(); - static void stopProfiling(); - +/* + * Internal: ETW-specific probe functions + */ #ifdef MOZ_ETW - // ETW Handlers - static bool ETWCreateRuntime(JSRuntime *rt); - static bool ETWDestroyRuntime(JSRuntime *rt); - static bool ETWShutdown(); - static bool ETWCallTrackingActive(JSContext *cx); - static bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); - static bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); - static bool ETWCreateObject(JSContext *cx, JSObject *obj); - static bool ETWFinalizeObject(JSObject *obj); - static bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); - static bool ETWCreateString(JSContext *cx, JSString *string, size_t length); - static bool ETWFinalizeString(JSString *string); - static bool ETWCompileScriptBegin(const char *filename, int lineno); - static bool ETWCompileScriptEnd(const char *filename, int lineno); - static bool ETWCalloutBegin(JSContext *cx, JSFunction *fun); - static bool ETWCalloutEnd(JSContext *cx, JSFunction *fun); - static bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes); - static bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes); - static bool ETWGCStart(JSCompartment *compartment); - static bool ETWGCEnd(JSCompartment *compartment); - static bool ETWGCStartMarkPhase(JSCompartment *compartment); - static bool ETWGCEndMarkPhase(JSCompartment *compartment); - static bool ETWGCStartSweepPhase(JSCompartment *compartment); - static bool ETWGCEndSweepPhase(JSCompartment *compartment); - static bool ETWCustomMark(JSString *string); - static bool ETWCustomMark(const char *string); - static bool ETWCustomMark(int marker); - static bool ETWStartExecution(JSContext *cx, JSScript *script); - static bool ETWStopExecution(JSContext *cx, JSScript *script); - static bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); +// ETW Handlers +bool ETWCreateRuntime(JSRuntime *rt); +bool ETWDestroyRuntime(JSRuntime *rt); +bool ETWShutdown(); +bool ETWCallTrackingActive(JSContext *cx); +bool ETWEnterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); +bool ETWExitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter); +bool ETWCreateObject(JSContext *cx, JSObject *obj); +bool ETWFinalizeObject(JSObject *obj); +bool ETWResizeObject(JSContext *cx, JSObject *obj, size_t oldSize, size_t newSize); +bool ETWCreateString(JSContext *cx, JSString *string, size_t length); +bool ETWFinalizeString(JSString *string); +bool ETWCompileScriptBegin(const char *filename, int lineno); +bool ETWCompileScriptEnd(const char *filename, int lineno); +bool ETWCalloutBegin(JSContext *cx, JSFunction *fun); +bool ETWCalloutEnd(JSContext *cx, JSFunction *fun); +bool ETWAcquireMemory(JSContext *cx, void *address, size_t nbytes); +bool ETWReleaseMemory(JSContext *cx, void *address, size_t nbytes); +bool ETWGCStart(JSCompartment *compartment); +bool ETWGCEnd(JSCompartment *compartment); +bool ETWGCStartMarkPhase(JSCompartment *compartment); +bool ETWGCEndMarkPhase(JSCompartment *compartment); +bool ETWGCStartSweepPhase(JSCompartment *compartment); +bool ETWGCEndSweepPhase(JSCompartment *compartment); +bool ETWCustomMark(JSString *string); +bool ETWCustomMark(const char *string); +bool ETWCustomMark(int marker); +bool ETWStartExecution(JSContext *cx, JSScript *script); +bool ETWStopExecution(JSContext *cx, JSScript *script); +bool ETWResizeHeap(JSCompartment *compartment, size_t oldSize, size_t newSize); #endif -}; + +} /* namespace Probes */ + +/* + * Probe handlers are implemented inline for minimal performance impact, + * especially important when no backends are enabled. + */ inline bool Probes::createRuntime(JSRuntime *rt) { bool ok = true; #ifdef MOZ_ETW if (!ETWCreateRuntime(rt)) ok = false; @@ -253,40 +293,23 @@ Probes::callTrackingActive(JSContext *cx #endif #ifdef MOZ_ETW if (ProfilingActive && ETWCallTrackingActive(cx)) return true; #endif return false; } -extern inline JS_FRIEND_API(JSBool) -js_PauseProfilers(JSContext *cx, uintN argc, jsval *vp) -{ - Probes::pauseProfilers(cx); - return JS_TRUE; -} - -extern inline JS_FRIEND_API(JSBool) -js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp) -{ - Probes::resumeProfilers(cx); - return JS_TRUE; -} - -extern JS_FRIEND_API(JSBool) -js_ResumeProfilers(JSContext *cx, uintN argc, jsval *vp); - inline bool Probes::enterJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter) { bool ok = true; #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_FUNCTION_ENTRY_ENABLED()) - enterJSFunImpl(cx, fun, script); + DTraceEnterJSFun(cx, fun, script); #endif #ifdef MOZ_TRACE_JSCALLS cx->doFunctionCallback(fun, script, counter); #endif #ifdef MOZ_ETW if (ProfilingActive && !ETWEnterJSFun(cx, fun, script, counter)) ok = false; #endif @@ -296,17 +319,17 @@ Probes::enterJSFun(JSContext *cx, JSFunc inline bool Probes::exitJSFun(JSContext *cx, JSFunction *fun, JSScript *script, int counter) { bool ok = true; #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_FUNCTION_RETURN_ENABLED()) - handleFunctionReturn(cx, fun, script); + DTraceExitJSFun(cx, fun, script); #endif #ifdef MOZ_TRACE_JSCALLS if (counter > 0) counter = -counter; cx->doFunctionCallback(fun, script, counter); #endif #ifdef MOZ_ETW if (ProfilingActive && !ETWExitJSFun(cx, fun, script, counter)) @@ -324,16 +347,30 @@ Probes::resizeHeap(JSCompartment *compar #ifdef MOZ_ETW if (ProfilingActive && !ETWResizeHeap(compartment, oldSize, newSize)) ok = false; #endif return ok; } +#ifdef INCLUDE_MOZILLA_DTRACE +static const char *ObjectClassname(JSObject *obj) { + if (! obj) + return "(null object)"; + Class *clasp = obj->getClass(); + if (! clasp) + return "(null)"; + const char *class_name = clasp->name; + if (! class_name) + return "(null class name)"; + return class_name; +} +#endif + inline bool Probes::createObject(JSContext *cx, JSObject *obj) { bool ok = true; #ifdef INCLUDE_MOZILLA_DTRACE if (JAVASCRIPT_OBJECT_CREATE_ENABLED()) JAVASCRIPT_OBJECT_CREATE(ObjectClassname(obj), (uintptr_t)obj); @@ -652,10 +689,15 @@ struct AutoFunctionCallProbe { } ~AutoFunctionCallProbe() { Probes::exitJSFun(cx, fun, script); } }; } /* namespace js */ - + +/* + * Internal functions for controlling various profilers. The profiler-specific + * implementations of these are mostly in jsdbgapi.cpp. + */ + #endif /* _JSPROBES_H */
--- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -466,17 +466,17 @@ qname_toString(JSContext *cx, uintN argc static JSFunctionSpec qname_methods[] = { JS_FN(js_toString_str, qname_toString, 0,0), JS_FS_END }; static bool InitXMLQName(JSContext *cx, JSObject *obj, JSLinearString *uri, JSLinearString *prefix, - JSLinearString *localName) + JSAtom *localName) { JS_ASSERT(obj->isQName()); JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal())); JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal())); /* Per ECMA-357, 13.3.5, these properties must be "own". */ if (!JS_DefineProperties(cx, obj, qname_props)) @@ -488,29 +488,29 @@ InitXMLQName(JSContext *cx, JSObject *ob obj->setNamePrefix(prefix); if (localName) obj->setQNameLocalName(localName); return true; } static JSObject * NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, - JSLinearString *localName) + JSAtom *localName) { JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass); if (!obj) return NULL; if (!InitXMLQName(cx, obj, uri, prefix, localName)) return NULL; return obj; } static JSObject * NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix, - JSLinearString *localName) + JSAtom *localName) { /* * AttributeName is an internal anonymous class which instances are not * exposed to scripts. */ JSObject *parent = GetGlobalForScopeChain(cx); JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AttributeNameClass, NULL, parent); if (!obj) @@ -714,19 +714,20 @@ Namespace(JSContext *cx, uintN argc, Val * if argc is 1 and argv[0] is JSVAL_VOID. */ static JSBool QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval) { jsval nameval, nsval; JSBool isQName, isNamespace; JSObject *qn; - JSLinearString *uri, *prefix, *name; + JSLinearString *uri, *prefix; JSObject *obj2; + JSAtom *name; if (argc <= 0) { nameval = JSVAL_VOID; isQName = JS_FALSE; } else { nameval = argv[argc > 1]; isQName = !JSVAL_IS_PRIMITIVE(nameval) && JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass; @@ -761,23 +762,18 @@ QNameHelper(JSContext *cx, JSObject *obj nameval = qn->getQNameLocalNameVal(); } if (argc == 0) { name = cx->runtime->emptyString; } else if (argc < 0) { name = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; } else { - JSString *str = js_ValueToString(cx, Valueify(nameval)); - if (!str) - return JS_FALSE; - name = str->ensureLinear(cx); - if (!name) - return JS_FALSE; - argv[argc > 1] = STRING_TO_JSVAL(name); + if (!js_ValueToAtom(cx, Valueify(nameval), &name)) + return false; } if (argc > 1 && !JSVAL_IS_VOID(argv[0])) { nsval = argv[0]; } else if (IS_STAR(name)) { nsval = JSVAL_NULL; } else { if (!js_GetDefaultXMLNamespace(cx, &nsval)) @@ -1161,30 +1157,32 @@ HAS_NS_AFTER_XML(const jschar *chars) static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace"; static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; static JSObject * ParseNodeToQName(Parser *parser, JSParseNode *pn, JSXMLArray *inScopeNSes, JSBool isAttributeName) { JSContext *cx = parser->context; - JSLinearString *str, *uri, *prefix, *localName; + JSLinearString *uri, *prefix; size_t length, offset; const jschar *start, *limit, *colon; uint32 n; JSObject *ns; JSLinearString *nsprefix; JS_ASSERT(pn->pn_arity == PN_NULLARY); - str = pn->pn_atom; + JSAtom *str = pn->pn_atom; start = str->chars(); length = str->length(); JS_ASSERT(length != 0 && *start != '@'); JS_ASSERT(length != 1 || *start != '*'); + JSAtom *localName; + uri = cx->runtime->emptyString; limit = start + length; colon = js_strchr_limit(start, ':', limit); if (colon) { offset = colon - start; prefix = js_NewDependentString(cx, str, 0, offset); if (!prefix) return NULL; @@ -1220,17 +1218,17 @@ ParseNodeToQName(Parser *parser, JSParse JSAutoByteString bytes; if (js_ValueToPrintable(parser->context, v, &bytes)) { ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn, JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr()); } return NULL; } - localName = js_NewStringCopyN(parser->context, colon + 1, length - (offset + 1)); + localName = js_AtomizeChars(parser->context, colon + 1, length - (offset + 1)); if (!localName) return NULL; } else { if (isAttributeName) { /* * An unprefixed attribute is not in any namespace, so set prefix * as well as uri to the empty string. */ @@ -2759,24 +2757,24 @@ ToXMLString(JSContext *cx, jsval v, uint JS::Anchor<JSObject *> anch(obj); JSXML *xml = reinterpret_cast<JSXML *>(obj->getPrivate()); return XMLToXMLString(cx, xml, NULL, toSourceFlag | 0); } static JSObject * ToAttributeName(JSContext *cx, jsval v) { - JSLinearString *name, *uri, *prefix; + JSLinearString *uri, *prefix; JSObject *obj; Class *clasp; JSObject *qn; + JSAtom *name; if (JSVAL_IS_STRING(v)) { - name = JSVAL_TO_STRING(v)->ensureLinear(cx); - if (!name) + if (!js_ValueToAtom(cx, Valueify(v), &name)) return NULL; uri = prefix = cx->runtime->emptyString; } else { if (JSVAL_IS_PRIMITIVE(v)) { js_ReportValueError(cx, JSMSG_BAD_XML_ATTR_NAME, JSDVG_IGNORE_STACK, Valueify(v), NULL); return NULL; } @@ -2790,21 +2788,17 @@ ToAttributeName(JSContext *cx, jsval v) qn = obj; uri = qn->getNameURI(); prefix = qn->getNamePrefix(); name = qn->getQNameLocalName(); } else { if (clasp == &js_AnyNameClass) { name = cx->runtime->atomState.starAtom; } else { - JSString *str = js_ValueToString(cx, Valueify(v)); - if (!str) - return NULL; - name = str->ensureLinear(cx); - if (!name) + if (!js_ValueToAtom(cx, Valueify(v), &name)) return NULL; } uri = prefix = cx->runtime->emptyString; } } qn = NewXMLAttributeName(cx, uri, prefix, name); if (!qn) @@ -2813,37 +2807,34 @@ ToAttributeName(JSContext *cx, jsval v) } static void ReportBadXMLName(JSContext *cx, const Value &idval) { js_ReportValueError(cx, JSMSG_BAD_XML_NAME, JSDVG_IGNORE_STACK, idval, NULL); } -static JSBool -IsFunctionQName(JSContext *cx, JSObject *qn, jsid *funidp) -{ - JSAtom *atom; - JSLinearString *uri; - - atom = cx->runtime->atomState.functionNamespaceURIAtom; - uri = qn->getNameURI(); - if (uri && (uri == atom || EqualStrings(uri, atom))) - return JS_ValueToId(cx, STRING_TO_JSVAL(qn->getQNameLocalName()), funidp); - *funidp = JSID_VOID; - return JS_TRUE; -} - -JSBool -js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp) -{ - if (obj->getClass() == &js_QNameClass) - return IsFunctionQName(cx, obj, funidp); - *funidp = JSID_VOID; - return JS_TRUE; +static bool +GetLocalNameFromFunctionQName(JSObject *qn, jsid *funidp, JSContext *cx) +{ + JSAtom *atom = cx->runtime->atomState.functionNamespaceURIAtom; + JSLinearString *uri = qn->getNameURI(); + if (uri && (uri == atom || EqualStrings(uri, atom))) { + *funidp = ATOM_TO_JSID(qn->getQNameLocalName()); + return true; + } + return false; +} + +bool +js_GetLocalNameFromFunctionQName(JSObject *obj, jsid *funidp, JSContext *cx) +{ + if (!obj->isQName()) + return false; + return GetLocalNameFromFunctionQName(obj, funidp, cx); } static JSObject * ToXMLName(JSContext *cx, jsval v, jsid *funidp) { JSAtom *atomizedName; JSString *name; JSObject *obj; @@ -2901,18 +2892,18 @@ ToXMLName(JSContext *cx, jsval v, jsid * construct: v = STRING_TO_JSVAL(name); obj = js_ConstructObject(cx, &js_QNameClass, NULL, NULL, 1, Valueify(&v)); if (!obj) return NULL; out: - if (!IsFunctionQName(cx, obj, funidp)) - return NULL; + if (!GetLocalNameFromFunctionQName(obj, funidp, cx)) + *funidp = JSID_VOID; return obj; bad: JSAutoByteString bytes; if (js_ValueToPrintable(cx, StringValue(name), &bytes)) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_XML_NAME, bytes.ptr()); return NULL; } @@ -4981,24 +4972,18 @@ again: /* * 11.2.2.1 Step 3(d) onward. */ JSBool js_GetXMLMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp) { JS_ASSERT(obj->isXML()); - if (JSID_IS_OBJECT(id)) { - jsid funid; - - if (!js_IsFunctionQName(cx, JSID_TO_OBJECT(id), &funid)) - return JS_FALSE; - if (!JSID_IS_VOID(funid)) - id = funid; - } + if (JSID_IS_OBJECT(id)) + js_GetLocalNameFromFunctionQName(JSID_TO_OBJECT(id), &id, cx); /* * As our callers have a bad habit of passing a pointer to an unrooted * local value as vp, we use a proper root here. */ AutoValueRooter tvr(cx); JSBool ok = GetXMLFunction(cx, obj, id, Jsvalify(tvr.addr())); *vp = tvr.value(); @@ -6440,39 +6425,30 @@ xml_setChildren(JSContext *cx, uintN arg *vp = OBJECT_TO_JSVAL(obj); return JS_TRUE; } static JSBool xml_setLocalName(JSContext *cx, uintN argc, jsval *vp) { - jsval name; - JSObject *nameqn; - JSLinearString *namestr; - NON_LIST_XML_METHOD_PROLOG; if (!JSXML_HAS_NAME(xml)) return JS_TRUE; + JSAtom *namestr; if (argc == 0) { namestr = cx->runtime->atomState.typeAtoms[JSTYPE_VOID]; } else { - name = vp[2]; - if (!JSVAL_IS_PRIMITIVE(name) && - JSVAL_TO_OBJECT(name)->getClass() == &js_QNameClass) { - nameqn = JSVAL_TO_OBJECT(name); - namestr = nameqn->getQNameLocalName(); + jsval name = vp[2]; + if (!JSVAL_IS_PRIMITIVE(name) && JSVAL_TO_OBJECT(name)->isQName()) { + namestr = JSVAL_TO_OBJECT(name)->getQNameLocalName(); } else { - if (!JS_ConvertValue(cx, name, JSTYPE_STRING, &vp[2])) - return JS_FALSE; - name = vp[2]; - namestr = JSVAL_TO_STRING(name)->ensureLinear(cx); - if (!namestr) - return JS_FALSE; + if (!js_ValueToAtom(cx, Valueify(name), &namestr)) + return false; } } xml = CHECK_COPY_ON_WRITE(cx, xml, obj); if (!xml) return JS_FALSE; if (namestr) xml->name->setQNameLocalName(namestr); @@ -7147,17 +7123,17 @@ js_InitQNameClass(JSContext *cx, JSObjec { JS_ASSERT(obj->isNative()); GlobalObject *global = obj->asGlobal(); JSObject *qnameProto = global->createBlankPrototype(cx, &js_QNameClass); if (!qnameProto) return NULL; - JSFlatString *empty = cx->runtime->emptyString; + JSAtom *empty = cx->runtime->emptyString; if (!InitXMLQName(cx, qnameProto, empty, empty, empty)) return NULL; qnameProto->syncSpecialEquality(); const uintN QNAME_CTOR_LENGTH = 2; JSFunction *ctor = global->createConstructor(cx, QName, &js_QNameClass, CLASS_ATOM(cx, QName), QNAME_CTOR_LENGTH); if (!ctor) @@ -7458,18 +7434,18 @@ js_FindXMLProperty(JSContext *cx, const if (!nameobj) return JS_FALSE; } else { JS_ASSERT(nameobj->getClass() == &js_AttributeNameClass || nameobj->getClass() == &js_QNameClass); } qn = nameobj; - if (!IsFunctionQName(cx, qn, &funid)) - return JS_FALSE; + if (!GetLocalNameFromFunctionQName(qn, &funid, cx)) + funid = JSID_VOID; obj = &js_GetTopStackFrame(cx)->scopeChain(); do { /* Skip any With object that can wrap XML. */ target = obj; while (target->getClass() == &js_WithClass) { proto = target->getProto(); if (!proto) @@ -7777,20 +7753,20 @@ js_NewXMLSpecialObject(JSContext *cx, JS return js_NewXMLObject(cx, JSXML_CLASS_TEXT); } obj = js_NewXMLObject(cx, xml_class); if (!obj) return NULL; xml = (JSXML *) obj->getPrivate(); if (name) { - JSLinearString *linearName = name->ensureLinear(cx); - if (!linearName) + JSAtom *atomName = js_AtomizeString(cx, name); + if (!atomName) return NULL; - qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, linearName); + qn = NewXMLQName(cx, cx->runtime->emptyString, NULL, atomName); if (!qn) return NULL; xml->name = qn; } xml->xml_value = value; return obj; }
--- a/js/src/jsxml.h +++ b/js/src/jsxml.h @@ -275,21 +275,21 @@ js_InitQNameClass(JSContext *cx, JSObjec extern JSObject * js_InitXMLClass(JSContext *cx, JSObject *obj); extern JSObject * js_InitXMLClasses(JSContext *cx, JSObject *obj); /* - * If obj is QName corresponding to function::name, set *funidp to name's id, - * otherwise set *funidp to void. + * If obj is a QName corresponding to function::name, set *funidp to name's id + * and return true, else return false. */ -JSBool -js_IsFunctionQName(JSContext *cx, JSObject *obj, jsid *funidp); +extern bool +js_GetLocalNameFromFunctionQName(JSObject *obj, jsid *funidp, JSContext *cx); extern JSBool js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp); extern JSBool js_SetDefaultXMLNamespace(JSContext *cx, const js::Value &v); /*
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3955,29 +3955,16 @@ static JSFunctionSpec shell_functions[] JS_FN("clone", Clone, 1,0), JS_FN("getpda", GetPDA, 1,0), JS_FN("getslx", GetSLX, 1,0), JS_FN("toint32", ToInt32, 1,0), JS_FN("evalcx", EvalInContext, 1,0), JS_FN("evalInFrame", EvalInFrame, 2,0), JS_FN("shapeOf", ShapeOf, 1,0), JS_FN("resolver", Resolver, 1,0), - JS_FN("pauseProfilers", js_PauseProfilers, 0,0), - JS_FN("resumeProfilers", js_ResumeProfilers, 0,0), -#ifdef MOZ_CALLGRIND - JS_FN("startCallgrind", js_StartCallgrind, 0,0), - JS_FN("stopCallgrind", js_StopCallgrind, 0,0), - JS_FN("dumpCallgrind", js_DumpCallgrind, 1,0), -#endif -#ifdef MOZ_VTUNE - JS_FN("startVtune", js_StartVtune, 1,0), - JS_FN("stopVtune", js_StopVtune, 0,0), - JS_FN("pauseVtune", js_PauseVtune, 0,0), - JS_FN("resumeVtune", js_ResumeVtune, 0,0), -#endif #ifdef MOZ_TRACEVIS JS_FN("startTraceVis", StartTraceVisNative, 1,0), JS_FN("stopTraceVis", StopTraceVisNative, 0,0), #endif #ifdef DEBUG JS_FN("arrayInfo", js_ArrayInfo, 1,0), #endif #ifdef JS_THREADSAFE @@ -4094,29 +4081,16 @@ static const char *const shell_help_mess " Evaluate s in optional sandbox object o\n" " if (s == '' && !o) return new o with eager standard classes\n" " if (s == 'lazy' && !o) return new o with lazy standard classes", "evalInFrame(n,str,save) Evaluate 'str' in the nth up frame.\n" " If 'save' (default false), save the frame chain", "shapeOf(obj) Get the shape of obj (an implementation detail)", "resolver(src[, proto]) Create object with resolve hook that copies properties\n" " from src. If proto is omitted, use Object.prototype.", -"pauseProfilers() Pause all profilers that can be paused", -"resumeProfilers() Resume profilers if they are paused", -#ifdef MOZ_CALLGRIND -"startCallgrind() Start callgrind instrumentation", -"stopCallgrind() Stop callgrind instrumentation", -"dumpCallgrind([name]) Dump callgrind counters", -#endif -#ifdef MOZ_VTUNE -"startVtune([filename]) Start vtune instrumentation", -"stopVtune() Stop vtune instrumentation", -"pauseVtune() Pause vtune collection", -"resumeVtune() Resume vtune collection", -#endif #ifdef MOZ_TRACEVIS "startTraceVis(filename) Start TraceVis recording (stops any current recording)", "stopTraceVis() Stop TraceVis recording", #endif #ifdef DEBUG "arrayInfo(a1, a2, ...) Report statistics about arrays", #endif #ifdef JS_THREADSAFE @@ -4148,53 +4122,86 @@ static const char *const shell_help_mess " Enables or disables a particularly expensive assertion in stack-walking\n" " code. If your test isn't ridiculously thorough, such that performing this\n" " assertion increases test duration by an order of magnitude, you shouldn't\n" " use this.", "getMaxArgs() Return the maximum number of supported args for a call.", /* Keep these last: see the static assertion below. */ #ifdef MOZ_PROFILING -"startProfiling() Start a profiling session.\n" +"startProfiling([profileName])\n" +" Start a profiling session\n" " Profiler must be running with programatic sampling", -"stopProfiling() Stop a running profiling session\n" +"stopProfiling([profileName])\n" +" Stop a running profiling session", +"pauseProfilers([profileName])\n" +" Pause a running profiling session", +"resumeProfilers([profileName])\n" +" Resume a paused profiling session", +"dumpProfile([outfile[, profileName]])\n" +" Dump out current profile info (only valid for callgrind)", +# ifdef MOZ_CALLGRIND +"startCallgrind() Start Callgrind instrumentation", +"stopCallgrind() Stop Callgrind instrumentation", +"dumpCallgrind([outfile]) Dump current Callgrind counters to file or stdout", +# endif +# ifdef MOZ_VTUNE +"startVtune() Start Vtune instrumentation", +"stopVtune() Stop Vtune instrumentation", +"pauseVtune() Pause Vtune collection", +"resumeVtune() Resume Vtune collection", +# endif #endif }; #ifdef MOZ_PROFILING -#define PROFILING_FUNCTION_COUNT 2 +# define PROFILING_FUNCTION_COUNT 5 +# ifdef MOZ_CALLGRIND +# define CALLGRIND_FUNCTION_COUNT 3 +# else +# define CALLGRIND_FUNCTION_COUNT 0 +# endif +# ifdef MOZ_VTUNE +# define VTUNE_FUNCTION_COUNT 4 +# else +# define VTUNE_FUNCTION_COUNT 0 +# endif +# define EXTERNAL_FUNCTION_COUNT (PROFILING_FUNCTION_COUNT + CALLGRIND_FUNCTION_COUNT + VTUNE_FUNCTION_COUNT) #else -#define PROFILING_FUNCTION_COUNT 0 +# define EXTERNAL_FUNCTION_COUNT 0 #endif /* Help messages must match shell functions. */ -JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - PROFILING_FUNCTION_COUNT == +JS_STATIC_ASSERT(JS_ARRAY_LENGTH(shell_help_messages) - EXTERNAL_FUNCTION_COUNT == JS_ARRAY_LENGTH(shell_functions) - 1 /* JS_FS_END */); #ifdef DEBUG static void CheckHelpMessages() { const char *const *m; const char *lp; /* Messages begin with "function_name(" prefix and don't end with \n. */ - for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - PROFILING_FUNCTION_COUNT; ++m) { + for (m = shell_help_messages; m != JS_ARRAY_END(shell_help_messages) - EXTERNAL_FUNCTION_COUNT; ++m) { lp = strchr(*m, '('); JS_ASSERT(lp); JS_ASSERT(memcmp(shell_functions[m - shell_help_messages].name, *m, lp - *m) == 0); JS_ASSERT((*m)[strlen(*m) - 1] != '\n'); } } #else # define CheckHelpMessages() ((void) 0) #endif #undef PROFILING_FUNCTION_COUNT +#undef CALLGRIND_FUNCTION_COUNT +#undef VTUNE_FUNCTION_COUNT +#undef EXTERNAL_FUNCTION_COUNT static JSBool Help(JSContext *cx, uintN argc, jsval *vp) { uintN i, j; int did_header, did_something; JSType type; JSFunction *fun; @@ -5025,17 +5032,24 @@ NewGlobalObject(JSContext *cx, Compartme static bool BindScriptArgs(JSContext *cx, JSObject *obj, OptionParser *op) { MultiStringRange msr = op->getMultiStringArg("scriptArgs"); JSObject *scriptArgs = JS_NewArrayObject(cx, 0, NULL); if (!scriptArgs) return false; - if (!JS_DefineProperty(cx, obj, "scriptArgs", OBJECT_TO_JSVAL(scriptArgs), NULL, NULL, 0)) + + /* + * Script arguments are bound as a normal |arguments| property on the + * global object. It has no special significance, like |arguments| in + * function scope does -- this identifier is used de-facto across shell + * implementations, see bug 675269. + */ + if (!JS_DefineProperty(cx, obj, "arguments", OBJECT_TO_JSVAL(scriptArgs), NULL, NULL, 0)) return false; for (size_t i = 0; !msr.empty(); msr.popFront(), ++i) { const char *scriptArg = msr.front(); JSString *str = JS_NewStringCopyZ(cx, scriptArg); if (!str || !JS_DefineElement(cx, scriptArgs, i, STRING_TO_JSVAL(str), NULL, NULL, JSPROP_ENUMERATE)) { @@ -5209,17 +5223,17 @@ Shell(JSContext *cx, OptionParser *op, c if (jsdbc) JSDB_TermDebugger(jsdc); #endif /* JSDEBUGGER_C_UI */ JSD_DebuggerOff(jsdc); } #endif /* JSDEBUGGER */ if (enableDisassemblyDumps) - JS_DumpAllProfiles(cx); + JS_DumpCompartmentBytecode(cx); return result; } static void MaybeOverrideOutFileFromEnv(const char* const envVar, FILE* defaultOut, FILE** outFile) @@ -5345,17 +5359,17 @@ main(int argc, char **argv, char **envp) "N indicates \"zealousness\":\n" " 0: no additional GCs\n" " 1: additional GCs at common danger points\n" " 2: GC every F allocations (default: 100)\n" "If C is 1, compartmental GCs are performed; otherwise, full") #endif || !op.addOptionalStringArg("script", "A script to execute (after all options)") || !op.addOptionalMultiStringArg("scriptArgs", - "String arguments to bind as |scriptArgs| in the " + "String arguments to bind as |arguments| in the " "shell's global")) { return EXIT_FAILURE; } switch (op.parseArgs(argc, argv)) { case OptionParser::ParseHelp: return EXIT_SUCCESS; case OptionParser::ParseError:
--- a/js/src/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/src/xpconnect/loader/mozJSComponentLoader.cpp @@ -286,27 +286,16 @@ File(JSContext *cx, uintN argc, jsval *v } static JSFunctionSpec gGlobalFun[] = { {"dump", Dump, 1,0}, {"debug", Debug, 1,0}, {"atob", Atob, 1,0}, {"btoa", Btoa, 1,0}, {"File", File, 1,JSFUN_CONSTRUCTOR}, -#ifdef MOZ_CALLGRIND - {"startCallgrind", js_StartCallgrind, 0,0}, - {"stopCallgrind", js_StopCallgrind, 0,0}, - {"dumpCallgrind", js_DumpCallgrind, 1,0}, -#endif -#ifdef MOZ_VTUNE - {"startVtune", js_StartVtune, 1,0}, - {"stopVtune", js_StopVtune, 0,0}, - {"pauseVtune", js_PauseVtune, 0,0}, - {"resumeVtune", js_ResumeVtune, 0,0}, -#endif #ifdef MOZ_TRACEVIS {"initEthogram", js_InitEthogram, 0,0}, {"shutdownEthogram", js_ShutdownEthogram, 0,0}, #endif {nsnull,nsnull,0,0} }; class JSCLContextHelper @@ -1354,17 +1343,17 @@ mozJSComponentLoader::ImportInto(const n if (i == 0) { logBuffer.AssignLiteral("Installing symbols [ "); } JSAutoByteString bytes(mContext, JSID_TO_STRING(symbolId)); if (!!bytes) logBuffer.Append(bytes.ptr()); logBuffer.AppendLiteral(" "); if (i == symbolCount - 1) { - LOG(("%s] from %s\n", PromiseFlatCString(logBuffer).get(), + LOG(("%s] from %s\n", logBuffer.get(), PromiseFlatCString(aLocation).get())); } #endif } } // Cache this module for later if (newEntry) {
--- a/js/src/xpconnect/shell/xpcshell.cpp +++ b/js/src/xpconnect/shell/xpcshell.cpp @@ -842,21 +842,16 @@ static JSFunctionSpec glob_functions[] = {"clear", Clear, 1,0}, {"options", Options, 0,0}, JS_FN("parent", Parent, 1,0), #ifdef DEBUG {"dumpHeap", DumpHeap, 5,0}, #endif {"sendCommand", SendCommand, 1,0}, {"getChildGlobalObject", GetChildGlobalObject, 0,0}, -#ifdef MOZ_CALLGRIND - {"startCallgrind", js_StartCallgrind, 0,0}, - {"stopCallgrind", js_StopCallgrind, 0,0}, - {"dumpCallgrind", js_DumpCallgrind, 1,0}, -#endif {nsnull,nsnull,0,0} }; JSClass global_class = { "global", 0, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, nsnull };
--- a/layout/base/nsDisplayList.cpp +++ b/layout/base/nsDisplayList.cpp @@ -2786,20 +2786,17 @@ PRBool nsDisplayTransform::UntransformRe gfxRect result(NSAppUnitsToFloatPixels(aUntransformedBounds.x, factor), NSAppUnitsToFloatPixels(aUntransformedBounds.y, factor), NSAppUnitsToFloatPixels(aUntransformedBounds.width, factor), NSAppUnitsToFloatPixels(aUntransformedBounds.height, factor)); /* We want to untransform the matrix, so invert the transformation first! */ result = matrix.Inverse().ProjectRectBounds(result); - *aOutRect = nsRect(NSFloatPixelsToAppUnits(float(result.x), factor), - NSFloatPixelsToAppUnits(float(result.y), factor), - NSFloatPixelsToAppUnits(float(result.width), factor), - NSFloatPixelsToAppUnits(float(result.height), factor)); + *aOutRect = nsLayoutUtils::RoundGfxRectToAppRect(result, factor); return PR_TRUE; } nsDisplaySVGEffects::nsDisplaySVGEffects(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame, nsDisplayList* aList) : nsDisplayWrapList(aBuilder, aFrame, aList), mEffectsFrame(aFrame), mBounds(aFrame->GetVisualOverflowRectRelativeToSelf())
--- a/layout/base/nsPresContext.cpp +++ b/layout/base/nsPresContext.cpp @@ -1054,37 +1054,40 @@ nsPresContext::SetShell(nsIPresShell* aS } if (mLangService) { doc->AddCharSetObserver(this); UpdateCharSet(doc->GetDocumentCharacterSet()); } } } else { - // Destroy image loaders now that the presshell is going away. - // This is important since imageloaders can have pointers to frames and - // we don't want those pointers to outlive the destruction of the frame - // arena. - for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) { - mImageLoaders[i].Enumerate(destroy_loads, nsnull); - mImageLoaders[i].Clear(); - } - if (mTransitionManager) { mTransitionManager->Disconnect(); mTransitionManager = nsnull; } if (mAnimationManager) { mAnimationManager->Disconnect(); mAnimationManager = nsnull; } } } void +nsPresContext::DestroyImageLoaders() +{ + // Destroy image loaders. This is important to do when frames are being + // destroyed because imageloaders can have pointers to frames and we don't + // want those pointers to outlive the destruction of the frame arena. + for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) { + mImageLoaders[i].Enumerate(destroy_loads, nsnull); + mImageLoaders[i].Clear(); + } +} + +void nsPresContext::UpdateCharSet(const nsAFlatCString& aCharSet) { if (mLangService) { NS_IF_RELEASE(mLanguage); mLanguage = mLangService->LookupCharSet(aCharSet.get()).get(); // addrefs // this will be a language group (or script) code rather than a true language code // bug 39570: moved from nsLanguageAtomService::LookupCharSet()
--- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -967,16 +967,18 @@ public: } void NotifyDestroyingFrame(nsIFrame* aFrame) { PropertyTable()->DeleteAllFor(aFrame); } inline void ForgetUpdatePluginGeometryFrame(nsIFrame* aFrame); + void DestroyImageLoaders(); + PRBool GetContainsUpdatePluginGeometryFrame() { return mContainsUpdatePluginGeometryFrame; } void SetContainsUpdatePluginGeometryFrame(PRBool aValue) { mContainsUpdatePluginGeometryFrame = aValue;
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -2981,16 +2981,22 @@ PresShell::FireResizeEvent() nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status); mInResize = PR_FALSE; } } void PresShell::SetIgnoreFrameDestruction(PRBool aIgnore) { + if (mPresContext) { + // We need to destroy the image loaders first, as they won't be + // notified when frames are destroyed once this setting takes effect. + // (See bug 673984) + mPresContext->DestroyImageLoaders(); + } mIgnoreFrameDestruction = aIgnore; } void PresShell::NotifyDestroyingFrame(nsIFrame* aFrame) { NS_TIME_FUNCTION_MIN(1.0); @@ -6657,17 +6663,17 @@ PresShell::HandleEvent(nsIView * nsPoint eventPoint = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame); { PRBool ignoreRootScrollFrame = PR_FALSE; if (aEvent->eventStructType == NS_MOUSE_EVENT) { ignoreRootScrollFrame = static_cast<nsMouseEvent*>(aEvent)->ignoreRootScrollFrame; } nsIFrame* target = nsLayoutUtils::GetFrameForPoint(frame, eventPoint, - PR_TRUE, ignoreRootScrollFrame); + PR_FALSE, ignoreRootScrollFrame); if (target) { frame = target; } } } // if a node is capturing the mouse, check if the event needs to be // retargeted at the capturing content instead. This will be the case when
--- a/layout/base/tests/Makefile.in +++ b/layout/base/tests/Makefile.in @@ -148,16 +148,17 @@ DEFINES += -D_IMPL_NS_LAYOUT bug664087-2-ref.html \ test_bug514127.html \ test_bug518777.html \ test_bug548545.xhtml \ test_bug558663.html \ test_bug559499.html \ test_bug582181-1.html \ test_bug582181-2.html \ + test_bug677878.html \ test_flush_on_paint.html \ test_mozPaintCount.html \ test_scroll_selection_into_view.html \ test_bug583889.html \ bug583889_inner1.html \ bug583889_inner2.html \ test_bug582771.html \ test_bug603550.html \
new file mode 100644 --- /dev/null +++ b/layout/base/tests/test_bug677878.html @@ -0,0 +1,51 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=677878 +--> +<head> + <title>Test for Bug 677878</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> + + <style> + #test1 { + background: green; + height: 100px; + width: 100px; + -moz-transform: scale(20, 20); + -moz-transform-origin: 0 0%; + } + </style> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=677878">Mozilla Bug 677878</a> +<div id="content"> + <div id="test1"> + <div id="test2" onclick="testFinish();"> + test + </div> + </div> + +<pre id="test"> +<script type="application/javascript"> +SimpleTest.waitForExplicitFinish(); +runtests(); + +function runtests() { + function doClick() { + document.getElementById("test2").addEventListener("mousedown", testFinish, true); + synthesizeMouseAtCenter(document.getElementById("test2"), { type: "mousedown" }) + } + setTimeout(doClick, 300); +} + +function testFinish(event){ + ok(true, "We can still interact with the item after it is transformed"); + SimpleTest.finish(); +} +</script> +</pre> +</body> +</html>
--- a/layout/reftests/selection/reftest.list +++ b/layout/reftests/selection/reftest.list @@ -25,8 +25,9 @@ == extend-4a.html extend-4-ref.html == extend-4b.html extend-4-ref.html fails-if(Android) != pseudo-element-of-native-anonymous.html pseudo-element-of-native-anonymous-ref.html # bug 676641 # These tests uses Highlight and HighlightText color keywords, they are not same as text selection color on Mac. fails-if(cocoaWidget) == non-themed-widget.html non-themed-widget-ref.html fails-if(cocoaWidget) == themed-widget.html themed-widget-ref.html == addrange-1.html addrange-ref.html == addrange-2.html addrange-ref.html +== splitText-normalize.html splitText-normalize-ref.html
new file mode 100644 --- /dev/null +++ b/layout/reftests/selection/splitText-normalize-ref.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html class="reftest-wait"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=191864 +--> +<head> + <title>Test for Bug 191864</title> + <script type="application/javascript;version=1.7" src="splitText-normalize.js"></script> +<script type="application/javascript;version=1.7"> +var id; +function checkFinished() { + if (window.frames.length == tests_done) { + clearInterval(id); + document.documentElement.className = ""; + } +} + +function runTest() { + let col1 = document.getElementById('col1'); + let col2 = document.getElementById('col2'); + let col3 = document.getElementById('col3'); + let col4 = document.getElementById('col4'); + for (let i=0; i < tests.length; ++i) { + let t = tests[i]; + col1.appendChild(createFrame(test_ref,t)); + col2.appendChild(createFrame(test_ref,t)); + col3.appendChild(createFrame(test_ref,t)); + col4.appendChild(createFrame(test_ref,t)); + } + id = setInterval(checkFinished,500); +} +</script> +</head> +<body onload="runTest()"> +<span id="col1" style="float:left; height:800px; width:180px;"></span> +<span id="col2" style="float:left; height:800px; width:180px;"></span> +<span id="col3" style="float:left; height:800px; width:180px;"></span> +<span id="col4" style="float:left; height:800px; width:180px;"></span> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/selection/splitText-normalize.html @@ -0,0 +1,40 @@ +<!DOCTYPE HTML> +<html class="reftest-wait"> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=191864 +--> +<head> + <title>Test for Bug 191864</title> + <script type="application/javascript;version=1.7" src="splitText-normalize.js"></script> +<script type="application/javascript;version=1.7"> +var id; +function checkFinished() { + if (window.frames.length == tests_done) { + clearInterval(id); + document.documentElement.className = ""; + } +} + +function runTest() { + let col1 = document.getElementById('col1'); + let col2 = document.getElementById('col2'); + let col3 = document.getElementById('col3'); + let col4 = document.getElementById('col4'); + for (let i=0; i < tests.length; ++i) { + let t = tests[i]; + col1.appendChild(createFrame(test_split,t)); + col2.appendChild(createFrame(test_split_merge,t)); + col3.appendChild(createFrame(test_merge,t)); + col4.appendChild(createFrame(test_split_insert,t)); + } + id = setInterval(checkFinished,500); +} +</script> +</head> +<body onload="runTest()"> +<span id="col1" style="float:left; height:800px; width:180px;"></span> +<span id="col2" style="float:left; height:800px; width:180px;"></span> +<span id="col3" style="float:left; height:800px; width:180px;"></span> +<span id="col4" style="float:left; height:800px; width:180px;"></span> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/selection/splitText-normalize.js @@ -0,0 +1,97 @@ +/** Test for Bug 191864 **/ +var tests_done = 0; +var tests = [ + [ {}, [0,4], "012345678" ], + [ {}, [0,0], "012345678" ], + [ {}, [0,9], "012345678" ], + [ {startOffset:4}, [0,4], "012345678" ], + [ {startOffset:5}, [0,4], "012345678" ], + [ {startOffset:5,endOffset:6}, [0,4], "012345678" ], + [ {endOffset:5}, [0,4], "012345678" ], + [ {endOffset:4}, [0,4], "012345678" ], + [ {endOffset:3}, [0,4], "012345678" ], + [ {startOffset:1,endOffset:3}, [0,4], "012345678" ], + [ {startOffset:7,endOffset:7}, [0,4], "012345678" ], + [ {startOffset:4,endOffset:4}, [0,4], "012345678" ], + [ {endNode:1}, [0,4], "012345678", "" ], + [ {endNode:1}, [0,4], "01234567", "8" ], + [ {endNode:1}, [1,4], "0", "12345678" ], + [ {endNode:2}, [1,4], "0", "12345", "678" ], +] + +function runtest(f,t,nosplit) { + // create content + let doc = f.contentDocument; + for (let i = 2; i < t.length; ++i) { + c = doc.createTextNode(t[i]); + doc.body.appendChild(c); + } + + // setup selection + let sel = t[0] + let startNode = sel.startNode === undefined ? doc.body.firstChild : doc.body.childNodes[sel.startNode]; + let startOffset = sel.startOffset === undefined ? 0 : sel.startOffset; + let endNode = sel.endNode === undefined ? startNode : doc.body.childNodes[sel.endNode]; + let endOffset = sel.endOffset === undefined ? endNode.length : sel.endOffset; + let selection = f.contentWindow.getSelection(); + let r = doc.createRange(); + r.setStart(startNode, startOffset); + r.setEnd(endNode, endOffset); + selection.addRange(r); + + // splitText + let split = t[1] + if (!nosplit) + doc.body.childNodes[split[0]].splitText(split[1]) +} +function test_ref(f,t,nosplit) { + runtest(f,t,true); +} +function test_split(f,t) { + runtest(f,t); +} +function test_split_insert(f,t) { + runtest(f,t); + let doc = f.contentDocument; + doc.body.firstChild; + let split = t[1] + let text1 = doc.body.childNodes[split[0]] + let text2 = doc.body.childNodes[split[0]+1] + if (text2.textContent.length==0) return; + let c = doc.createTextNode(text2.textContent[0]); + doc.body.insertBefore(c,text2); + let r = doc.createRange(); + r.setStart(text2, 0); + r.setEnd(text2, 1); + r.deleteContents(); +} +function test_split_merge(f,t) { + runtest(f,t); + f.contentDocument.body.normalize(); +} +function test_merge(f,t) { + runtest(f,t,true); + f.contentDocument.body.normalize(); +} + +function repaint_selection(win) { + let a = new Array; + let sel = win.getSelection(); + for (let i = 0; i < sel.rangeCount; ++i) { + a[i] = sel.getRangeAt(i); + } + sel.removeAllRanges(); + for (let i = 0; i < a.length; ++i) { + sel.addRange(a[i]); + } +} + +function createFrame(run,t) { + let f = document.createElement('iframe'); + f.setAttribute('height','22'); + f.setAttribute('frameborder','0'); + f.setAttribute('width','200'); + f.src = 'data:text/html,<body style="margin:0;padding:0">'; + f.onload = function () { try { run(f, t); repaint_selection(f.contentWindow);} finally { ++tests_done; } } + return f; +}
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filter-scaled-01.svg @@ -0,0 +1,18 @@ +<svg width="100%" height="100%" preserveAspectRatio="none" + xmlns="http://www.w3.org/2000/svg" + xmlns:xlink="http://www.w3.org/1999/xlink"> + + <title>Testcase for scaled filter with non-integer x and y and objectBoundingBox</title> + <defs> + <filter id="filter" filterUnits="objectBoundingBox" x="0.5" y="0.5" width="0.25" height="0.25"> + <feFlood flood-color="red" width="1" height="1" /> + </filter> + </defs> + + <rect width="100%" height="100%" fill="lime"/> + <g transform="scale(400)"> + <rect width="1" height="1" filter="url(#filter)" /> + </g> + <rect x="200" y="200" width="100" height="100" fill="lime" /> + +</svg>
--- a/layout/reftests/svg/filters/filter-nested-filtering-01.svg +++ b/layout/reftests/svg/filters/filter-nested-filtering-01.svg @@ -1,16 +1,16 @@ <!-- Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> <svg xmlns="http://www.w3.org/2000/svg" version="1.0"> <!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=461863 --> <desc> - This test checks that transforms and filtering a decedent of a + This test checks that transforms and filtering a descendant of a filtered element interact correctly. </desc> <filter id="a"><feGaussianBlur stdDeviation="0.001"/></filter> <filter id="b"><feGaussianBlur stdDeviation="0.001"/></filter> <rect width="100%" height="100%" fill="lime"/> <circle fill="red" cx="200" cy="200" r="98"/> <g id="g" filter="url(#a)" transform="translate(100, 0)"> <circle fill="lime" filter="url(#b)" cx="100" cy="200" r="100"/>
--- a/layout/reftests/svg/objectBoundingBox-and-fePointLight-01-ref.svg +++ b/layout/reftests/svg/objectBoundingBox-and-fePointLight-01-ref.svg @@ -1,24 +1,23 @@ <!-- Any copyright is dedicated to the Public Domain. http://creativecommons.org/publicdomain/zero/1.0/ --> -<svg width="100%" height="100%" - viewBox="0 0 480 360" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <title>Reference for objectBoundingBox with fePointLight</title>