Merge m-c to b-s.
Merge m-c to b-s.
--- a/.hgtags
+++ b/.hgtags
@@ -62,8 +62,9 @@ b70744835d94e54eec97b8fd186c96da5708a506
a71bd564ebf5bf4f93d13e84114f759c263130b0 MOBILE_MERGE_DONE
a71bd564ebf5bf4f93d13e84114f759c263130b0 MOBILE_MERGE_DONE_20110406
a95d426422816513477e5863add1b00ac7041dcb AURORA_BASE_20110412
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
9eae975b3d6fb7748fe5a3c0113d449b1c7cc0b2 AURORA_BASE_20110524
138f593553b66c9f815e8f57870c19d6347f7702 UPDATE_PACKAGING_R14
462c726144bc1fb45b61e774f64ac5d61b4e047c UPDATE_PACKAGING_R14
5eb553dd2ceae5f88d80f27afc5ef3935c5d43b0 AURORA_BASE_20110705
+41b84b87c816403e1b74963d8094cff0406c989e AURORA_BASE_20110816
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/Makefile.in
+++ b/browser/Makefile.in
@@ -40,23 +40,23 @@ topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
include $(topsrcdir)/config/config.mk
PARALLEL_DIRS = \
base \
components \
- devtools \
fuel \
locales \
themes \
$(NULL)
DIRS = \
+ devtools \
app \
$(NULL)
ifeq ($(OS_ARCH),WINNT)
ifdef MOZ_INSTALLER
DIRS += installer/windows
endif
endif
--- 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/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -510,17 +510,17 @@ GroupItem.prototype = Utils.extend(new I
// Make the computed bounds' "padding" and expand button margin actually be
// themeable --OR-- compute this from actual bounds. Bug 586546
box.inset(6, 6);
// make some room for the expand button if we're stacked
let isStacked = (options && options.forceStacked) || this.isStacked();
if (isStacked)
- box.height -= 33; // 33px room for the expand button
+ box.height -= this.$expander.height() + 9; // the button height plus padding
return box;
},
// ----------
// Function: setBounds
// Sets the bounds with the given <Rect>, animating unless "immediately" is false.
//
@@ -1232,17 +1232,16 @@ GroupItem.prototype = Utils.extend(new I
},
// ----------
// Function: showExpandControl
// Show the control which expands a stacked groupItem into a quick-look view.
showExpandControl: function GroupItem_showExpandControl() {
let parentBB = this.getBounds();
let childBB = this.getChild(0).getBounds();
- let padding = 7;
this.$expander
.show()
.css({
left: parentBB.width/2 - this.$expander.width()/2
});
},
// ----------
@@ -1358,25 +1357,27 @@ GroupItem.prototype = Utils.extend(new I
if (GroupItems._arrangePaused) {
GroupItems.pushArrange(this, options);
return false;
}
let shouldStack = this.shouldStack(childrenToArrange.length + (options.addTab ? 1 : 0));
let shouldStackArrange = (shouldStack && !this.expanded);
- let box = this.getContentBounds({forceStacked: shouldStackArrange});
-
+ let box;
+
// if we should stack and we're not expanded
if (shouldStackArrange) {
this.showExpandControl();
+ box = this.getContentBounds({forceStacked: true});
this._stackArrange(childrenToArrange, box, options);
return false;
} else {
this.hideExpandControl();
+ box = this.getContentBounds({forceStacked: false});
// a dropIndex is returned
return this._gridArrange(childrenToArrange, box, options);
}
},
// ----------
// Function: _stackArrange
// Arranges the children in a stack.
--- a/browser/base/content/tabview/storage.js
+++ b/browser/base/content/tabview/storage.js
@@ -180,16 +180,33 @@ let Storage = {
} catch (e) {
// getWindowValue will fail if the property doesn't exist
Utils.log("Error in readGroupItemData: "+e, data);
}
return existingData;
},
// ----------
+ // Function: readWindowBusyState
+ // Returns the current busyState for the given window.
+ readWindowBusyState: function Storage_readWindowBusyState(win) {
+ let state;
+
+ try {
+ let data = this._sessionStore.getWindowState(win);
+ if (data)
+ state = JSON.parse(data);
+ } catch (e) {
+ Utils.log("Error while parsing window state");
+ }
+
+ return (state && state.windows[0].busy);
+ },
+
+ // ----------
// Function: saveGroupItemsData
// Saves the global data for the <GroupItems> singleton for the given window.
saveGroupItemsData: function Storage_saveGroupItemsData(win, data) {
this.saveData(win, this.GROUPS_DATA_IDENTIFIER, data);
},
// ----------
// Function: readGroupItemsData
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -118,19 +118,19 @@ let UI = {
// Keeps track of info related to private browsing, including:
// transitionMode - whether we're entering or exiting PB
// wasInTabView - whether TabView was visible before we went into PB
_privateBrowsing: {
transitionMode: "",
wasInTabView: false
},
- // Variable: _storageBusyCount
- // Used to keep track of how many calls to storageBusy vs storageReady.
- _storageBusyCount: 0,
+ // Variable: _storageBusy
+ // Tells whether the storage is currently busy or not.
+ _storageBusy: false,
// Variable: isDOMWindowClosing
// Tells wether the parent window is about to close
isDOMWindowClosing: false,
// Variable: _browserKeys
// Used to keep track of allowed browser keys.
_browserKeys: null,
@@ -164,16 +164,20 @@ let UI = {
// initialize the direction of the page
this._initPageDirection();
// ___ thumbnail storage
ThumbnailStorage.init();
// ___ storage
Storage.init();
+
+ if (Storage.readWindowBusyState(gWindow))
+ this.storageBusy();
+
let data = Storage.readUIData(gWindow);
this._storageSanity(data);
this._pageBounds = data.pageBounds;
// ___ currentTab
this._currentTab = gBrowser.selectedTab;
// ___ exit button
@@ -607,39 +611,42 @@ let UI = {
},
#endif
// ----------
// Function: storageBusy
// Pauses the storage activity that conflicts with sessionstore updates and
// private browsing mode switches. Calls can be nested.
storageBusy: function UI_storageBusy() {
- if (!this._storageBusyCount) {
- TabItems.pauseReconnecting();
- GroupItems.pauseAutoclose();
- }
-
- this._storageBusyCount++;
+ if (this._storageBusy)
+ return;
+
+ this._storageBusy = true;
+
+ TabItems.pauseReconnecting();
+ GroupItems.pauseAutoclose();
},
// ----------
// Function: storageReady
// Resumes the activity paused by storageBusy, and updates for any new group
// information in sessionstore. Calls can be nested.
storageReady: function UI_storageReady() {
- this._storageBusyCount--;
- if (!this._storageBusyCount) {
- let hasGroupItemsData = GroupItems.load();
- if (!hasGroupItemsData)
- this.reset();
-
- TabItems.resumeReconnecting();
- GroupItems._updateTabBar();
- GroupItems.resumeAutoclose();
- }
+ if (!this._storageBusy)
+ return;
+
+ this._storageBusy = false;
+
+ let hasGroupItemsData = GroupItems.load();
+ if (!hasGroupItemsData)
+ this.reset();
+
+ TabItems.resumeReconnecting();
+ GroupItems._updateTabBar();
+ GroupItems.resumeAutoclose();
},
// ----------
// Function: _addTabActionHandlers
// Adds handlers to handle tab actions.
_addTabActionHandlers: function UI__addTabActionHandlers() {
var self = this;
@@ -725,17 +732,17 @@ let UI = {
if (self.isTabViewVisible()) {
// just closed the selected tab in the TabView interface.
if (self._currentTab == tab)
self._closedSelectedTabInTabView = true;
} else {
// If we're currently in the process of entering private browsing,
// we don't want to go to the Tab View UI.
- if (self._storageBusyCount)
+ if (self._storageBusy)
return;
// if not closing the last tab
if (gBrowser.tabs.length > 1) {
// Don't return to TabView if there are any app tabs
for (let a = 0; a < gBrowser._numPinnedTabs; a++) {
if (!gBrowser.tabs[a].closing)
return;
--- 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/base/content/test/tabview/browser_tabview_bug597248.js
+++ b/browser/base/content/test/tabview/browser_tabview_bug597248.js
@@ -32,36 +32,30 @@ function setupTwo(win) {
let numTabsToSave = tabItems.length;
// force all canvases to update, and hook in imageData save detection
tabItems.forEach(function(tabItem) {
contentWindow.TabItems.update(tabItem.tab);
tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
item.removeSubscriber("savedCachedImageData", onSaved);
- --numTabsToSave;
+
+ if (!--numTabsToSave)
+ restoreWindow();
});
});
// after the window is closed, restore it.
- let xulWindowDestory = function() {
- Services.obs.removeObserver(
- xulWindowDestory, "xul-window-destroyed", false);
-
- // "xul-window-destroyed" is just fired just before a XUL window is
- // destroyed so restore window and test it after a delay
+ let restoreWindow = function() {
executeSoon(function() {
restoredWin = undoCloseWindow();
restoredWin.addEventListener("load", function onLoad(event) {
restoredWin.removeEventListener("load", onLoad, false);
registerCleanupFunction(function() restoredWin.close());
-
- // ensure that closed tabs have been saved
- is(numTabsToSave, 0, "All tabs were saved when window was closed.");
is(restoredWin.gBrowser.tabs.length, 3, "The total number of tabs is 3");
// setup tab variables and listen to the tabs load progress
newTabOne = restoredWin.gBrowser.tabs[0];
newTabTwo = restoredWin.gBrowser.tabs[1];
newTabThree = restoredWin.gBrowser.tabs[2];
restoredWin.gBrowser.addTabsProgressListener(gTabsProgressListener);
@@ -98,17 +92,16 @@ function setupTwo(win) {
});
}
restoredWin.addEventListener(
"tabviewframeinitialized", onTabViewFrameInitialized, false);
}, false);
});
};
- Services.obs.addObserver(xulWindowDestory, "xul-window-destroyed", false);
win.close();
}
let gTabsProgressListener = {
onStateChange: function(browser, webProgress, request, stateFlags, status) {
// ensure about:blank doesn't trigger the code
if ((stateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
--- a/browser/base/content/test/tabview/browser_tabview_privatebrowsing.js
+++ b/browser/base/content/test/tabview/browser_tabview_privatebrowsing.js
@@ -44,21 +44,25 @@ function onTabViewLoadedAndShown() {
// collect the group titles
let count = contentWindow.GroupItems.groupItems.length;
for (let a = 0; a < count; a++) {
let gi = contentWindow.GroupItems.groupItems[a];
groupTitles[a] = gi.getTitle();
}
+ contentWindow.gPrefBranch.setBoolPref("animate_zoom", false);
+
// Create a second tab
gBrowser.addTab("about:robots");
is(gBrowser.tabs.length, 2, "we now have 2 tabs");
+
registerCleanupFunction(function() {
gBrowser.removeTab(gBrowser.tabs[1]);
+ contentWindow.gPrefBranch.clearUserPref("animate_zoom");
});
afterAllTabsLoaded(function() {
// Get normal tab urls
for (let a = 0; a < gBrowser.tabs.length; a++)
normalURLs.push(gBrowser.tabs[a].linkedBrowser.currentURI.spec);
// verify that we're all set up for our test
--- a/browser/base/content/test/tabview/head.js
+++ b/browser/base/content/test/tabview/head.js
@@ -356,16 +356,19 @@ function restoreTab(callback, index, win
}
// ----------
function togglePrivateBrowsing(callback) {
let topic = "private-browsing-transition-complete";
Services.obs.addObserver(function observe() {
Services.obs.removeObserver(observe, topic);
- afterAllTabsLoaded(callback);
+
+ // use executeSoon() to let Panorama load its group data from the session
+ // before we call afterAllTabsLoaded()
+ executeSoon(function () afterAllTabsLoaded(callback));
}, topic, false);
let pb = Cc["@mozilla.org/privatebrowsing;1"].
getService(Ci.nsIPrivateBrowsingService);
pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
}
--- 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
@@ -757,17 +780,17 @@ SessionStoreService.prototype = {
if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" ||
this._loadState == STATE_QUITTING)
return;
// assign it a unique identifier (timestamp)
aWindow.__SSi = "window" + Date.now();
// and create its data object
- this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [] };
+ this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
if (!this._isWindowLoaded(aWindow))
this._windows[aWindow.__SSi]._restoring = true;
if (!aWindow.toolbar.visible)
this._windows[aWindow.__SSi].isPopup = true;
// perform additional initialization when the first window is loading
if (this._loadState == STATE_STOPPED) {
this._loadState = STATE_RUNNING;
@@ -941,16 +964,19 @@ SessionStoreService.prototype = {
// Until we decide otherwise elsewhere, this window is part of a series
// of closing windows to quit.
winData._shouldRestore = true;
#endif
// save the window if it has multiple tabs or a single saveable tab
if (winData.tabs.length > 1 ||
(winData.tabs.length == 1 && this._shouldSaveTabState(winData.tabs[0]))) {
+ // we don't want to save the busy state
+ delete winData.busy;
+
this._closedWindows.unshift(winData);
this._capClosedWindows();
}
// clear this window from the list
delete this._windows[aWindow.__SSi];
// save the state without this window to disk
@@ -1237,33 +1263,33 @@ SessionStoreService.prototype = {
},
setTabState: function sss_setTabState(aTab, aState) {
var tabState = JSON.parse(aState);
if (!tabState.entries || !aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
var window = aTab.ownerDocument.defaultView;
- this._sendWindowStateEvent(window, "Busy");
+ this._setWindowStateBusy(window);
this.restoreHistoryPrecursor(window, [aTab], [tabState], 0, 0, 0);
},
duplicateTab: function sss_duplicateTab(aWindow, aTab, aDelta) {
if (!aTab.ownerDocument || !aTab.ownerDocument.defaultView.__SSi ||
!aWindow.getBrowser)
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
var tabState = this._collectTabData(aTab, true);
var sourceWindow = aTab.ownerDocument.defaultView;
this._updateTextAndScrollDataForTab(sourceWindow, aTab.linkedBrowser, tabState, true);
tabState.index += aDelta;
tabState.index = Math.max(1, Math.min(tabState.index, tabState.entries.length));
tabState.pinned = false;
- this._sendWindowStateEvent(aWindow, "Busy");
+ this._setWindowStateBusy(aWindow);
let newTab = aTab == aWindow.gBrowser.selectedTab ?
aWindow.gBrowser.addTab(null, {relatedToCurrent: true, ownerTab: aTab}) :
aWindow.gBrowser.addTab();
this.restoreHistoryPrecursor(aWindow, [newTab], [tabState], 0, 0, 0);
return newTab;
},
@@ -1296,17 +1322,17 @@ SessionStoreService.prototype = {
aIndex = aIndex || 0;
if (!(aIndex in closedTabs))
throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
// fetch the data of closed tab, while removing it from the array
let closedTab = closedTabs.splice(aIndex, 1).shift();
let closedTabState = closedTab.state;
- this._sendWindowStateEvent(aWindow, "Busy");
+ this._setWindowStateBusy(aWindow);
// create a new tab
let browser = aWindow.gBrowser;
let tab = browser.addTab();
// restore tab content
this.restoreHistoryPrecursor(aWindow, [tab], [closedTabState], 1, 0, 0);
// restore the tab's position
@@ -2506,17 +2532,17 @@ SessionStoreService.prototype = {
catch (ex) { // invalid state object - don't restore anything
debug(ex);
this._sendRestoreCompletedNotifications();
return;
}
// We're not returning from this before we end up calling restoreHistoryPrecursor
// for this window, so make sure we send the SSWindowStateBusy event.
- this._sendWindowStateEvent(aWindow, "Busy");
+ this._setWindowStateBusy(aWindow);
if (root._closedWindows)
this._closedWindows = root._closedWindows;
var winData;
if (!aState.selectedWindow) {
aState.selectedWindow = 0;
}
@@ -2693,17 +2719,17 @@ SessionStoreService.prototype = {
delete this._statesToRestore[aWindow.__SS_restoreID];
delete aWindow.__SS_restoreID;
delete this._windows[aWindow.__SSi]._restoring;
}
if (aTabs.length == 0) {
// this is normally done in restoreHistory() but as we're returning early
// here we need to take care of it.
- this._sendWindowStateEvent(aWindow, "Ready");
+ this._setWindowStateReady(aWindow);
return;
}
let unhiddenTabs = aTabData.filter(function (aData) !aData.hidden).length;
// if all tabs to be restored are hidden, make the first one visible
if (unhiddenTabs == 0) {
aTabData[0].hidden = false;
@@ -2838,17 +2864,17 @@ SessionStoreService.prototype = {
var _this = this;
while (aTabs.length > 0 && (!aTabData[0]._tabStillLoading || !aTabs[0].parentNode)) {
aTabs.shift(); // this tab got removed before being completely restored
aTabData.shift();
}
if (aTabs.length == 0) {
// At this point we're essentially ready for consumers to read/write data
// via the sessionstore API so we'll send the SSWindowStateReady event.
- this._sendWindowStateEvent(aWindow, "Ready");
+ this._setWindowStateReady(aWindow);
return; // no more tabs to restore
}
var tab = aTabs.shift();
var tabData = aTabData.shift();
var browser = aWindow.gBrowser.getBrowserForTab(tab);
var history = browser.webNavigation.sessionHistory;
@@ -2898,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);
@@ -3034,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) {
@@ -3964,26 +3990,69 @@ 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:
+ function sss__changeWindowStateBusyValue(aWindow, aValue) {
+
+ this._windows[aWindow.__SSi].busy = aValue;
+
+ // Keep the to-be-restored state in sync because that is returned by
+ // getWindowState() as long as the window isn't loaded, yet.
+ if (!this._isWindowLoaded(aWindow)) {
+ let stateToRestore = this._statesToRestore[aWindow.__SS_restoreID].windows[0];
+ stateToRestore.busy = aValue;
+ }
+ },
+
+ /**
+ * Set the given window's state to 'not busy'.
+ * @param aWindow the window
+ */
+ _setWindowStateReady: function sss__setWindowStateReady(aWindow) {
+ this._setWindowStateBusyValue(aWindow, false);
+ this._sendWindowStateEvent(aWindow, "Ready");
+ },
+
+ /**
+ * Set the given window's state to 'busy'.
+ * @param aWindow the window
+ */
+ _setWindowStateBusy: function sss__setWindowStateBusy(aWindow) {
+ this._setWindowStateBusyValue(aWindow, true);
+ this._sendWindowStateEvent(aWindow, "Busy");
},
/**
* Dispatch an SSWindowState_____ event for the given window.
* @param aWindow the window
* @param aType the type of event, SSWindowState will be prepended to this string
*/
_sendWindowStateEvent: function sss__sendWindowStateEvent(aWindow, aType) {
--- a/browser/components/sessionstore/test/browser/Makefile.in
+++ b/browser/components/sessionstore/test/browser/Makefile.in
@@ -144,17 +144,19 @@ 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 \
$(NULL)
endif
--- 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_595601-restore_hidden.js
+++ b/browser/components/sessionstore/test/browser/browser_595601-restore_hidden.js
@@ -1,116 +1,117 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
const TAB_STATE_NEEDS_RESTORE = 1;
const TAB_STATE_RESTORING = 2;
-let stateBackup = ss.getBrowserState();
-
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#4"}]},
{entries:[{url:"http://example.com#5"}], hidden: true},
{entries:[{url:"http://example.com#6"}], hidden: true},
{entries:[{url:"http://example.com#7"}], hidden: true},
{entries:[{url:"http://example.com#8"}], hidden: true}
]}]};
function test() {
waitForExplicitFinish();
registerCleanupFunction(function () {
- Services.prefs.clearUserPref("browser.sessionstore.max_concurrent_tabs");
Services.prefs.clearUserPref("browser.sessionstore.restore_hidden_tabs");
-
- TabsProgressListener.uninit();
-
- ss.setBrowserState(stateBackup);
});
- TabsProgressListener.init();
-
- Services.prefs.setIntPref("browser.sessionstore.max_concurrent_tabs", 3);
-
// First stage: restoreHiddenTabs = true
// Second stage: restoreHiddenTabs = false
test_loadTabs(true, function () {
- test_loadTabs(false, function () {
- waitForFocus(finish);
- });
+ test_loadTabs(false, finish);
});
}
function test_loadTabs(restoreHiddenTabs, callback) {
Services.prefs.setBoolPref("browser.sessionstore.restore_hidden_tabs", restoreHiddenTabs);
let expectedTabs = restoreHiddenTabs ? 8 : 4;
-
let firstProgress = true;
- TabsProgressListener.setCallback(function (needsRestore, isRestoring) {
+ newWindowWithState(state, function (win, needsRestore, isRestoring) {
if (firstProgress) {
firstProgress = false;
is(isRestoring, 3, "restoring 3 tabs concurrently");
} else {
ok(isRestoring < 4, "restoring max. 3 tabs concurrently");
}
- if (gBrowser.tabs.length - needsRestore == expectedTabs) {
- TabsProgressListener.unsetCallback();
- is(gBrowser.visibleTabs.length, 4, "only 4 visible tabs");
+ if (win.gBrowser.tabs.length - needsRestore == expectedTabs) {
+ is(win.gBrowser.visibleTabs.length, 4, "only 4 visible tabs");
+
+ TabsProgressListener.uninit();
callback();
}
});
-
- ss.setBrowserState(JSON.stringify(state));
-}
-
-function countTabs() {
- let needsRestore = 0, isRestoring = 0;
- let windowsEnum = Services.wm.getEnumerator("navigator:browser");
-
- while (windowsEnum.hasMoreElements()) {
- let window = windowsEnum.getNext();
- if (window.closed)
- continue;
-
- for (let i = 0; i < window.gBrowser.tabs.length; i++) {
- let browser = window.gBrowser.tabs[i].linkedBrowser;
- if (browser.__SS_restoreState == TAB_STATE_RESTORING)
- isRestoring++;
- else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
- needsRestore++;
- }
- }
-
- return [needsRestore, isRestoring];
}
let TabsProgressListener = {
- init: function () {
- gBrowser.addTabsProgressListener(this);
+ init: function (win) {
+ this.window = win;
+
+ this.window.gBrowser.addTabsProgressListener(this);
},
uninit: function () {
- this.unsetCallback();
- gBrowser.removeTabsProgressListener(this);
+ this.window.gBrowser.removeTabsProgressListener(this);
+
+ delete this.window;
+ delete this.callback;
},
setCallback: function (callback) {
this.callback = callback;
},
- unsetCallback: function () {
- delete this.callback;
- },
-
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (this.callback && aBrowser.__SS_restoreState == TAB_STATE_RESTORING &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
- this.callback.apply(null, countTabs());
+ this.callback.apply(null, [this.window].concat(this.countTabs()));
+ },
+
+ countTabs: function () {
+ let needsRestore = 0, isRestoring = 0;
+
+ for (let i = 0; i < this.window.gBrowser.tabs.length; i++) {
+ let browser = this.window.gBrowser.tabs[i].linkedBrowser;
+ if (browser.__SS_restoreState == TAB_STATE_RESTORING)
+ isRestoring++;
+ else if (browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE)
+ needsRestore++;
+ }
+
+ return [needsRestore, isRestoring];
}
}
+
+// ----------
+function whenWindowLoaded(win, callback) {
+ win.addEventListener("load", function onLoad() {
+ win.removeEventListener("load", onLoad, false);
+ executeSoon(callback);
+ }, false);
+}
+
+// ----------
+function newWindowWithState(state, callback) {
+ let opts = "chrome,all,dialog=no,height=800,width=800";
+ let win = window.openDialog(getBrowserURL(), "_blank", opts);
+
+ registerCleanupFunction(function () win.close());
+
+ whenWindowLoaded(win, function () {
+ TabsProgressListener.init(win);
+ TabsProgressListener.setCallback(callback);
+
+ ss.setWindowState(win, JSON.stringify(state), true);
+ });
+}
--- 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: [] }));
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser/browser_662812.js
@@ -0,0 +1,34 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test() {
+ waitForExplicitFinish();
+
+ window.addEventListener("SSWindowStateBusy", function onBusy() {
+ window.removeEventListener("SSWindowStateBusy", onBusy, false);
+
+ let state = JSON.parse(ss.getWindowState(window));
+ ok(state.windows[0].busy, "window is busy");
+
+ window.addEventListener("SSWindowStateReady", function onReady() {
+ window.removeEventListener("SSWindowStateReady", onReady, false);
+
+ let state = JSON.parse(ss.getWindowState(window));
+ ok(!state.windows[0].busy, "window is not busy");
+
+ gBrowser.removeTab(gBrowser.tabs[1]);
+ executeSoon(finish);
+ }, false);
+ }, false);
+
+ // create a new tab
+ let tab = gBrowser.addTab("about:mozilla");
+ let browser = tab.linkedBrowser;
+
+ // close and restore it
+ browser.addEventListener("load", function onLoad() {
+ browser.removeEventListener("load", onLoad, true);
+ gBrowser.removeTab(tab);
+ ss.undoCloseTab(window, 0);
+ }, true);
+}
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-8.0a1
+9.0a1
--- 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/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
+++ b/browser/devtools/sourceeditor/test/browser_sourceeditor_initialization.js
@@ -14,19 +14,26 @@ function test()
{
waitForExplicitFinish();
const windowUrl = "data:text/xml,<?xml version='1.0'?>" +
"<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
" title='test for bug 660784' width='600' height='500'><hbox flex='1'/></window>";
const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
- testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+ gBrowser.selectedTab = gBrowser.addTab();
+ gBrowser.selectedBrowser.addEventListener("load", function onTabLoad() {
+ gBrowser.selectedBrowser.removeEventListener("load", onTabLoad, true);
- testWin.addEventListener("load", initEditor, false);
+ testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+ testWin.addEventListener("load", initEditor, false);
+ }, true);
+
+ content.location = "data:text/html,<p>bug 660784 - test the SourceEditor";
+
}
function initEditor()
{
testWin.removeEventListener("load", initEditor, false);
testDoc = testWin.document;
let hbox = testDoc.querySelector("hbox");
@@ -314,17 +321,21 @@ function editorLoaded()
// Done.
editor.destroy();
ok(!editor.parentElement && !editor.editorElement, "destroy() works");
testWin.close();
testWin = testDoc = editor = null;
- finish();
+
+ waitForFocus(function() {
+ gBrowser.removeCurrentTab();
+ finish();
+ }, content);
}
function testBackspaceKey()
{
editor.setText(" a\n b\n c");
editor.setCaretOffset(7);
EventUtils.synthesizeKey("VK_BACK_SPACE", {}, testWin);
is(editor.getText(), "a\n b\n c", "line outdented (Backspace)");
--- 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/gnomestripe/browser/aboutSessionRestore.css
+++ b/browser/themes/gnomestripe/browser/aboutSessionRestore.css
@@ -76,17 +76,17 @@ treechildren::-moz-tree-image(icon),
treechildren::-moz-tree-image(noicon) {
padding-right: 2px;
margin: 0px 2px;
width: 16px;
height: 16px;
}
treechildren::-moz-tree-image(noicon) {
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
treechildren::-moz-tree-image(container, noicon) {
list-style-image: url("chrome://browser/skin/aboutSessionRestore-window-icon.png");
}
treechildren::-moz-tree-image(checked) {
list-style-image: url("chrome://global/skin/checkbox/cbox-check.gif");
}
treechildren::-moz-tree-image(partial) {
--- a/browser/themes/gnomestripe/browser/browser.css
+++ b/browser/themes/gnomestripe/browser/browser.css
@@ -190,17 +190,17 @@ menuitem.bookmark-item {
/* Bookmarks toolbar */
#PlacesToolbarDropIndicator {
list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
}
/* Bookmark items */
.bookmark-item:not([container]) {
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.bookmark-item[container] {
list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
}
.bookmark-item[container][livemark] {
list-style-image: url("chrome://browser/skin/feeds/feedIcon16.png");
@@ -977,17 +977,17 @@ toolbar[iconsize="small"] #feed-button {
#page-proxy-stack {
width: 24px;
height: 20px;
padding: 2px 4px;
}
#page-proxy-favicon:not([src]) {
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
#page-proxy-favicon[pageproxystate="invalid"] {
opacity: 0.3;
}
#urlbar-throbber {
list-style-image: url("chrome://browser/skin/places/searching_16.png");
@@ -1557,17 +1557,17 @@ richlistitem[type~="action"][actiontype=
outline: 1px dotted;
}
.tab-throbber,
.tab-icon-image {
width: 16px;
height: 16px;
-moz-margin-end: 3px;
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.tab-throbber {
list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
}
.tab-throbber[progress] {
list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
@@ -1695,17 +1695,17 @@ richlistitem[type~="action"][actiontype=
}
#alltabs-button[type="menu"] > .toolbarbutton-icon {
display: none;
}
/* All tabs menupopup */
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.alltabs-item[selected="true"] {
font-weight: bold;
}
.alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
list-style-image: url("chrome://global/skin/icons/loading_16.png");
--- a/browser/themes/gnomestripe/browser/places/places.css
+++ b/browser/themes/gnomestripe/browser/places/places.css
@@ -18,17 +18,17 @@
}
/* Trees */
treechildren::-moz-tree-image(title) {
padding-right: 2px;
margin: 0px 2px;
width: 16px;
height: 16px;
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
treechildren::-moz-tree-image(title, livemarkItem) {
list-style-image: url("chrome://browser/skin/places/livemark-item.png");
}
treechildren::-moz-tree-image(title, separator) {
list-style-image: none;
--- a/browser/themes/gnomestripe/browser/preferences/aboutPermissions.css
+++ b/browser/themes/gnomestripe/browser/preferences/aboutPermissions.css
@@ -20,17 +20,17 @@
padding: 4px;
border-bottom: 1px solid ThreeDLightShadow;
}
.site-favicon {
height: 16px;
width: 16px;
-moz-margin-end: 4px;
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
#all-sites-item > .site-container > .site-favicon {
list-style-image: none;
}
/* permissions box */
--- a/browser/themes/gnomestripe/browser/preferences/preferences.css
+++ b/browser/themes/gnomestripe/browser/preferences/preferences.css
@@ -114,17 +114,17 @@ radio[pane=paneSync] {
margin: 0px 3px 6px 3px !important;
}
/* Cookies Manager */
#cookiesChildren::-moz-tree-image(domainCol) {
width: 16px;
height: 16px;
margin: 0px 2px;
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
#paneApplications {
margin-left: 4px;
margin-right: 4px;
padding-left: 0;
padding-right: 0;
}
--- a/browser/themes/gnomestripe/browser/searchbar.css
+++ b/browser/themes/gnomestripe/browser/searchbar.css
@@ -16,17 +16,17 @@
.searchbar-engine-menuitem[selected="true"] > .menu-iconic-text {
font-weight: bold;
}
/* Engine button */
.searchbar-engine-image {
height: 16px;
width: 16px;
- list-style-image: url("moz-icon://stock/gtk-file?size=menu");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
-moz-margin-start: 2px;
}
.searchbar-engine-button {
-moz-appearance: none;
min-width: 0;
margin: 0;
border: 0;
--- a/browser/themes/gnomestripe/browser/tabview/tabview.css
+++ b/browser/themes/gnomestripe/browser/tabview/tabview.css
@@ -213,16 +213,27 @@ html[dir=rtl] .groupItem {
html[dir=rtl] .groupItem.activeGroupItem {
box-shadow:
rgba(0,0,0,0.8) -2px 2px 8px;
}
.groupItem .close {
z-index: 10;
+ top: 0px;
+ right: 0px;
+ width: 22px;
+ height: 22px;
+ background-position: bottom left;
+}
+
+html[dir=rtl] .groupItem .close {
+ right: auto;
+ left: 0px;
+ background-position: bottom right;
}
.phantom {
border: 1px solid rgba(190,190,190,1);
}
.dragRegion {
background: rgba(248,248,248,0.4);
--- a/browser/themes/pinstripe/browser/browser.css
+++ b/browser/themes/pinstripe/browser/browser.css
@@ -1524,17 +1524,17 @@ toolbarbutton.chevron > .toolbarbutton-m
.tabbrowser-tabbox {
margin: 0;
}
.tab-throbber,
.tab-icon-image {
width: 16px;
height: 16px;
- list-style-image: url("chrome://global/skin/tree/item.png");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.tab-throbber {
list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
}
.tab-throbber[progress] {
list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
@@ -2022,17 +2022,17 @@ toolbarbutton.chevron > .toolbarbutton-m
}
#alltabs-button > .toolbarbutton-icon {
-moz-margin-end: 2px;
}
/* All Tabs Menupopup */
.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
- list-style-image: url("chrome://global/skin/tree/item.png");
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
list-style-image: url("chrome://global/skin/icons/loading_16.png") !important;
}
.alltabs-item[tabIsVisible] {
/* box-shadow instead of background-color to work around native styling */
--- a/browser/themes/pinstripe/browser/tabview/tabview.css
+++ b/browser/themes/pinstripe/browser/tabview/tabview.css
@@ -205,16 +205,27 @@ html[dir=rtl] .stack-trayed .tab-title {
html[dir=rtl] .groupItem.activeGroupItem {
box-shadow:
rgba(0,0,0,0.8) -2px 2px 8px;
}
.groupItem .close {
z-index: 10;
+ top: 0px;
+ right: 0px;
+ width: 22px;
+ height: 22px;
+ background-position: bottom left;
+}
+
+html[dir=rtl] .groupItem .close {
+ right: auto;
+ left: 0px;
+ background-position: bottom right;
}
.phantom {
border: 1px solid rgba(255, 255, 255, 0.5);
}
.dragRegion {
background: rgba(235, 235, 235, 0.4);
--- a/browser/themes/winstripe/browser/browser.css
+++ b/browser/themes/winstripe/browser/browser.css
@@ -583,21 +583,21 @@ menuitem.bookmark-item {
.bookmark-item > .menu-iconic-left > .menu-iconic-icon {
-moz-padding-start: 0px;
}
/* ::::: bookmark items ::::: */
.bookmark-item {
- list-style-image: url("chrome://global/skin/icons/folder-item.png");
- -moz-image-region: rect(0px, 16px, 16px, 0px);
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.bookmark-item[container] {
+ list-style-image: url("chrome://global/skin/icons/folder-item.png");
-moz-image-region: rect(0px, 32px, 16px, 16px);
}
.bookmark-item[container][open] {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
.bookmark-item[container][livemark] {
@@ -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);
}
@@ -1371,22 +1379,21 @@ html|*.urlbar-input:-moz-lwtheme:-moz-pl
#page-proxy-stack {
width: 24px;
height: 18px;
padding: 1px 4px;
}
#page-proxy-favicon:not([src]) {
- list-style-image: url("chrome://global/skin/icons/folder-item.png");
- -moz-image-region: rect(0px, 16px, 16px, 0px)
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
#page-proxy-favicon[pageproxystate="invalid"] {
- -moz-image-region: rect(32px, 16px, 48px, 0px);
+ opacity: 0.5;
}
#urlbar-throbber {
list-style-image: url("chrome://browser/skin/places/searching_16.png");
}
/* autocomplete */
@@ -1777,18 +1784,17 @@ richlistitem[type~="action"][actiontype=
background-image: -moz-linear-gradient(hsla(0,0%,100%,.4), hsla(0,0%,75%,.4) 80%),
-moz-radial-gradient(center 3px, circle cover, rgba(255,255,255,1) 3%, rgba(186,221,251,.75) 40%, rgba(127,179,255,.5) 80%, rgba(127,179,255,.25));
}
.tab-throbber,
.tab-icon-image {
width: 16px;
height: 16px;
- list-style-image: url("chrome://global/skin/icons/folder-item.png");
- -moz-image-region: rect(0px, 16px, 16px, 0px);
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
-moz-margin-start: 2px;
-moz-margin-end: 3px;
}
.tab-throbber {
list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
}
@@ -1882,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;
}
@@ -1927,63 +1936,66 @@ 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://global/skin/icons/folder-item.png");
- -moz-image-region: rect(0px, 16px, 16px, 0px);
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
}
.alltabs-item[selected="true"] {
font-weight: bold;
}
.alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
list-style-image: url("chrome://global/skin/icons/loading_16.png");
--- 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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
--- a/browser/themes/winstripe/browser/preferences/preferences.css
+++ b/browser/themes/winstripe/browser/preferences/preferences.css
@@ -113,21 +113,21 @@ radio[pane=paneSync] {
margin: 0 3px 6px !important;
}
/* Cookies Manager */
#cookiesChildren::-moz-tree-image(domainCol) {
width: 16px;
height: 16px;
margin: 0 2px;
- list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
- -moz-image-region: rect(0, 16px, 16px, 0);
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png") !important;
}
#cookiesChildren::-moz-tree-image(domainCol, container) {
+ list-style-image: url("chrome://global/skin/icons/folder-item.png") !important;
-moz-image-region: rect(0, 32px, 16px, 16px);
}
#cookiesChildren::-moz-tree-image(domainCol, container, open) {
-moz-image-region: rect(16px, 32px, 32px, 16px);
}
#cookieInfoBox {
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..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/browser/themes/winstripe/browser/tabview/tabview.css
+++ b/browser/themes/winstripe/browser/tabview/tabview.css
@@ -77,16 +77,17 @@ html[dir=rtl] .favicon {
}
.close {
top: 6px;
right: 6px;
width: 16px;
height: 16px;
background-image: -moz-image-rect(url("chrome://browser/skin/tabview/close.png"), 0, 16, 16, 0);
+ background-repeat: no-repeat;
}
.close:hover {
background-image: -moz-image-rect(url("chrome://browser/skin/tabview/close.png"), 0, 32, 16, 16);
}
.close:hover:active {
background-image: -moz-image-rect(url("chrome://browser/skin/tabview/close.png"), 0, 48, 16, 32);
@@ -231,16 +232,27 @@ html[dir=rtl] .groupItem {
html[dir=rtl] .groupItem.activeGroupItem {
box-shadow:
rgba(0,0,0,0.8) -2px 2px 8px;
}
.groupItem .close {
z-index: 10;
+ top: 0px;
+ right: 0px;
+ width: 22px;
+ height: 22px;
+ background-position: bottom left;
+}
+
+html[dir=rtl] .groupItem .close {
+ right: auto;
+ left: 0px;
+ background-position: bottom right;
}
.dragRegion {
background: rgba(224, 234, 245, 0.4);
}
.overlay {
background-color: rgba(0,0,0,.7) !important;
--- 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/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
# x.x.x.x
# x.x.x+
#
# Referenced by milestone.pl.
# Hopefully I'll be able to automate replacement of *all*
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
-8.0a1
+9.0a1
--- 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/config/milestone.txt
+++ b/js/src/config/milestone.txt
@@ -5,9 +5,9 @@
# x.x.x.x
# x.x.x+
#
# Referenced by milestone.pl.
# Hopefully I'll be able to automate replacement of *all*
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
-8.0a1
+9.0a1
--- 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