Merge mozilla-central to tracemonkey.

This commit is contained in:
Robert Sayre 2010-09-30 23:09:33 -04:00
commit d7d56ebb27
142 changed files with 2703 additions and 1499 deletions

View File

@ -490,8 +490,8 @@ pref("intl.menuitems.insertseparatorbeforeaccesskeys","chrome://global/locale/in
// simple gestures support
pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate");
pref("browser.gesture.swipe.up", "cmd_scrollTop");
pref("browser.gesture.swipe.down", "cmd_scrollBottom");
pref("browser.gesture.swipe.up", "Browser:HideTabView");
pref("browser.gesture.swipe.down", "Browser:ShowTabView");
#ifdef XP_MACOSX
pref("browser.gesture.pinch.latched", true);
pref("browser.gesture.pinch.threshold", 150);

View File

@ -117,6 +117,8 @@
<command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);"/>
<command id="Browser:ShowAllTabs" oncommand="allTabs.open();"/>
<command id="Browser:ToggleTabView" oncommand="TabView.toggle();"/>
<command id="Browser:ShowTabView" oncommand="TabView.show();"/>
<command id="Browser:HideTabView" oncommand="TabView.hide();"/>
<command id="cmd_fullZoomReduce" oncommand="FullZoom.reduce()"/>
<command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
<command id="cmd_fullZoomReset" oncommand="FullZoom.reset()"/>

View File

@ -152,6 +152,7 @@ toolbar[mode="icons"] > #reload-button[displaystop] {
/* Some child nodes want to be ordered based on the locale's direction, while
everything else should be ltr. */
#urlbar-progress:-moz-locale-dir(rtl),
.urlbar-input-box:-moz-locale-dir(rtl) {
direction: rtl;
}

View File

@ -744,10 +744,12 @@ var gSyncSetup = {
if (stm.step())
daysOfHistory = stm.getInt32(0);
// Support %S for historical reasons (see bug 600141)
document.getElementById("historyCount").value =
PluralForm.get(daysOfHistory,
this._stringBundle.GetStringFromName("historyDaysCount.label"))
.replace("%S", daysOfHistory);
.replace("%S", daysOfHistory)
.replace("#1", daysOfHistory);
// bookmarks
let bookmarks = 0;
@ -759,17 +761,21 @@ var gSyncSetup = {
stm.params.tag = Weave.Svc.Bookmark.tagsFolder;
if (stm.executeStep())
bookmarks = stm.row.bookmarks;
// Support %S for historical reasons (see bug 600141)
document.getElementById("bookmarkCount").value =
PluralForm.get(bookmarks,
this._stringBundle.GetStringFromName("bookmarksCount.label"))
.replace("%S", bookmarks);
.replace("%S", bookmarks)
.replace("#1", bookmarks);
// passwords
let logins = Weave.Svc.Login.getAllLogins({});
// Support %S for historical reasons (see bug 600141)
document.getElementById("passwordCount").value =
PluralForm.get(logins.length,
this._stringBundle.GetStringFromName("passwordsCount.label"))
.replace("%S", logins.length);
.replace("%S", logins.length)
.replace("#1", logins.length);
this._case1Setup = true;
break;
case 2:
@ -794,10 +800,12 @@ var gSyncSetup = {
appendNode(name);
}
if (count > 5) {
// Support %S for historical reasons (see bug 600141)
let label =
PluralForm.get(count - 5,
this._stringBundle.GetStringFromName("additionalClientCount.label"))
.replace("%S", count - 5);
.replace("%S", count - 5)
.replace("#1", count - 5);
appendNode(label);
}
this._case2Setup = true;

View File

@ -853,7 +853,7 @@ SessionStoreService.prototype = {
// If this tab was in the middle of restoring, we want to restore the next
// tab. If the tab hasn't been restored, we want to remove it from the array.
this._resetTabRestoringState(aTab, true);
this._resetTabRestoringState(aTab, true, false);
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
@ -2197,7 +2197,7 @@ SessionStoreService.prototype = {
// If overwriting tabs, we want to remove __SS_restoring from the browser.
if (aOverwriteTabs) {
for (let i = 0; i < tabbrowser.tabs.length; i++)
this._resetTabRestoringState(tabbrowser.tabs[i], false);
this._resetTabRestoringState(tabbrowser.tabs[i], false, false);
}
// We want to set up a counter on the window that indicates how many tabs
@ -2435,7 +2435,10 @@ SessionStoreService.prototype = {
history.PurgeHistory(history.count);
}
history.QueryInterface(Ci.nsISHistoryInternal);
browser.__SS_shistoryListener = new SessionStoreSHistoryListener(this, tab);
history.addSHistoryListener(browser.__SS_shistoryListener);
if (!tabData.entries) {
tabData.entries = [];
}
@ -3518,10 +3521,33 @@ SessionStoreService.prototype = {
this._tabsRestoringCount = 0;
},
_resetTabRestoringState: function sss__resetTabRestoringState(aTab, aRestoreNextTab) {
/**
* Reset the restoring state for a particular tab. This will be called when
* removing a tab, when a tab needs to be reset (it's being overwritten), or
* when reload occurs. This is multipurpose and is meant to provide a single
* path for very similar functionality.
*
* @param aTab
* The tab that will be "reset"
* @param aRestoreNextTab
* If the tab is currently restoring, should we allow a new restore to
* begin
* @param aRestoreThisTab
* In the process of "resetting" this tab, should we also restore it.
*/
_resetTabRestoringState:
function sss__resetTabRestoringState(aTab, aRestoreNextTab, aRestoreThisTab) {
let browser = aTab.linkedBrowser;
if (browser.__SS_restoring) {
// If the session history listener hasn't been detached, make sure we
// remove it and delete the reference.
if (browser.__SS_shistoryListener) {
browser.webNavigation.sessionHistory.
removeSHistoryListener(browser.__SS_shistoryListener);
delete browser.__SS_shistoryListener;
}
delete browser.__SS_restoring;
if (aRestoreNextTab) {
// this._tabsRestoringCount is decremented in restoreNextTab.
@ -3534,13 +3560,32 @@ SessionStoreService.prototype = {
}
}
else if (browser.__SS_needsRestore) {
let window = aTab.ownerDocument.defaultView;
window.__SS_tabsToRestore--;
delete browser.__SS_needsRestore;
if (aTab.hidden)
this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab));
else
this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab));
// First we'll remove the tab from the hidden/visible array of tabs left
// to restore.
let splicedTabs;
if (aTab.hidden) {
splicedTabs =
this._tabsToRestore.hidden.splice(this._tabsToRestore.hidden.indexOf(aTab), 1);
}
else {
splicedTabs =
this._tabsToRestore.visible.splice(this._tabsToRestore.visible.indexOf(aTab), 1);
}
if (aRestoreThisTab && splicedTabs.length) {
// If we want to restore the tab, then we'll do that. This tab still
// needs restore, so we're going to restore this one, not the next one
// as was done above.
this.restoreTab(aTab);
}
else {
// If we don't want to restore the tab, we want to explicitly remove
// __SS_needsRestore and decrement __SS_tabsToRestore. Normally this is
// done in restoreTab.
let window = aTab.ownerDocument.defaultView;
window.__SS_tabsToRestore--;
delete browser.__SS_needsRestore;
}
}
},
@ -3675,19 +3720,51 @@ let XPathHelper = {
let gRestoreTabsProgressListener = {
ss: null,
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
// Ignore state changes on browsers that we've already restored
if (!aBrowser.__SS_restoring)
return;
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
// Ignore state changes on browsers that we've already restored and state
// changes that aren't applicable.
if (aBrowser.__SS_restoring &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) {
delete aBrowser.__SS_restoring;
this.ss.restoreNextTab(true);
// We need to reset the tab before starting the next restore.
// _resetTabRestoringState will make sure we remove the session history
// listener and will call restoreNextTab.
let window = aBrowser.ownerDocument.defaultView;
let tab = window.gBrowser._getTabForContentWindow(aBrowser.contentWindow);
this.ss._resetTabRestoringState(tab, true, false);
}
}
}
// A SessionStoreSHistoryListener will be attached to each browser before it is
// restored. We need to catch reloads that occur before the tab is restored
// because otherwise, docShell will reload an old URI (usually about:blank).
function SessionStoreSHistoryListener(ss, aTab) {
this.tab = aTab;
this.ss = ss;
}
SessionStoreSHistoryListener.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISHistoryListener,
Ci.nsISupportsWeakReference]),
browser: null,
ss: null,
OnHistoryNewEntry: function(aNewURI) { },
OnHistoryGoBack: function(aBackURI) { return true; },
OnHistoryGoForward: function(aForwardURI) { return true; },
OnHistoryGotoIndex: function(aIndex, aGotoURI) { return true; },
OnHistoryPurge: function(aNumEntries) { return true; },
OnHistoryReload: function(aReloadURI, aReloadFlags) {
// On reload, we want to make sure that session history loads the right
// URI. In order to do that, we will call _resetTabRestoringState.
// __SS_needsRestore will still be on this tab's browser, so we will end up
// calling restoreTab and this tab will be loaded.
this.ss._resetTabRestoringState(this.tab, false, true);
// Returning false will stop the load that docshell is attempting.
return false;
}
}
// see nsPrivateBrowsingService.js
String.prototype.hasRootDomain = function hasRootDomain(aDomain)
{

View File

@ -50,7 +50,7 @@ function test() {
let tests = [test_cascade, test_select, test_multiWindowState,
test_setWindowStateNoOverwrite, test_setWindowStateOverwrite,
test_setBrowserStateInterrupted];
test_setBrowserStateInterrupted, test_reload];
function runNextTest() {
// Reset the pref
try {
@ -60,6 +60,7 @@ function runNextTest() {
// set an empty state & run the next test, or finish
if (tests.length) {
ss.setBrowserState(JSON.stringify({ windows: [{ tabs: [{ url: 'about:blank' }], }] }));
info("running next test");
executeSoon(tests.shift());
}
else {
@ -531,6 +532,122 @@ function test_setBrowserStateInterrupted() {
}
function test_reload() {
// Set the pref to 0 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);
// 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_restoring &&
aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
test_reload_progressCallback(aBrowser);
}
}
let state = { windows: [{ tabs: [
{ entries: [{ url: "http://example.org/#1" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#2" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#3" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#4" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#5" }], extData: { "uniq": r() } },
{ entries: [{ url: "http://example.org/#6" }], extData: { "uniq": r() } }
], selected: 1 }] };
let loadCount = 0;
function test_reload_progressCallback(aBrowser) {
loadCount++;
is(aBrowser.currentURI.spec, state.windows[0].tabs[loadCount - 1].entries[0].url,
"test_reload: load " + loadCount + " - browser loaded correct url");
if (loadCount <= state.windows[0].tabs.length) {
// double check that this tab was the right one
let expectedData = state.windows[0].tabs[loadCount - 1].extData.uniq;
let tab;
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
tab = window.gBrowser.tabs[i];
}
is(ss.getTabValue(tab, "uniq"), expectedData,
"test_reload: load " + loadCount + " - correct tab was restored");
if (loadCount == state.windows[0].tabs.length) {
window.gBrowser.removeTabsProgressListener(progressListener);
test_reload2(state);
}
else {
// reload the next tab
window.gBrowser.reloadTab(window.gBrowser.tabs[loadCount]);
}
}
}
window.gBrowser.addTabsProgressListener(progressListener);
ss.setBrowserState(JSON.stringify(state));
}
// This test shouldn't be added to tests. It will be called directly from
// test_reload. This guarantees that we're already in a tested restored state
// and we know what the state should be.
function test_reload2(aState) {
info("starting test_reload2");
let progressListener = {
onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW)
test_reload2_progressCallback(aBrowser);
}
}
// Simulate a left mouse button click with no modifiers, which is what
// Command-R, or clicking reload does.
let fakeEvent = {
button: 0,
metaKey: false,
altKey: false,
ctrlKey: false,
shiftKey: false,
}
let loadCount = 0;
function test_reload2_progressCallback(aBrowser) {
loadCount++;
if (loadCount <= aState.windows[0].tabs.length) {
// double check that this tab was the right one
let expectedData = aState.windows[0].tabs[loadCount - 1].extData.uniq;
let tab;
for (let i = 0; i < window.gBrowser.tabs.length; i++) {
if (!tab && window.gBrowser.tabs[i].linkedBrowser == aBrowser)
tab = window.gBrowser.tabs[i];
}
is(ss.getTabValue(tab, "uniq"), expectedData,
"test_reload2: load " + loadCount + " - correct tab was reloaded");
if (loadCount == aState.windows[0].tabs.length) {
window.gBrowser.removeTabsProgressListener(progressListener);
runNextTest();
}
else {
// reload the next tab
window.gBrowser.selectTabAtIndex(loadCount);
BrowserReloadOrDuplicate(fakeEvent);
}
}
}
window.gBrowser.addTabsProgressListener(progressListener);
BrowserReloadOrDuplicate(fakeEvent);
}
function countTabs() {
let needsRestore = 0,
isRestoring = 0,

View File

@ -33,6 +33,16 @@ xpinstallDisabledMessage=Software installation is currently disabled. Click Enab
xpinstallDisabledButton=Enable
xpinstallDisabledButton.accesskey=n
# LOCALIZATION NOTE (addonDownloading, addonDownloadCancelled, addonDownloadRestart):
# Semi-colon list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# Also see https://bugzilla.mozilla.org/show_bug.cgi?id=570012 for mockups
addonDownloading=Add-on downloading;Add-ons downloading
addonDownloadCancelled=Add-on download cancelled.;Add-on downloads cancelled.
addonDownloadRestart=Restart Download;Restart Downloads
addonDownloadRestart.accessKey=R
addonDownloadCancelTooltip=Cancel
# LOCALIZATION NOTE (addonsInstalled, addonsInstalledNeedsRestart):
# Semi-colon list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals

View File

@ -11,19 +11,23 @@ verifying.label = Verifying…
# LOCALIZATION NOTE (additionalClientCount.label):
# Semi-colon list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
additionalClientCount.label = and %S additional device;and %S additional devices
# #1 is the number of additional clients (was %S for a short while, use #1 instead, even if both work)
additionalClientCount.label = and #1 additional device;and #1 additional devices
# LOCALIZATION NOTE (bookmarksCount.label):
# Semi-colon list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
bookmarksCount.label = %S bookmark;%S bookmarks
# #1 is the number of bookmarks (was %S for a short while, use #1 instead, even if both work)
bookmarksCount.label = #1 bookmark;#1 bookmarks
# LOCALIZATION NOTE (historyDaysCount.label):
# Semi-colon list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
historyDaysCount.label = %S day of history;%S days of history
# #1 is the number of days (was %S for a short while, use #1 instead, even if both work)
historyDaysCount.label = #1 day of history;#1 days of history
# LOCALIZATION NOTE (passwordsCount.label):
# Semi-colon list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
passwordsCount.label = %S password;%S passwords
# #1 is the number of passwords (was %S for a short while, use #1 instead, even if both work)
passwordsCount.label = #1 password;#1 passwords
save.synckey.title = Save Sync Key

View File

@ -169,6 +169,7 @@ browser.jar:
skin/classic/aero/browser/feeds/videoFeedIcon16.png (feeds/videoFeedIcon16-aero.png)
skin/classic/aero/browser/feeds/subscribe.css (feeds/subscribe.css)
skin/classic/aero/browser/feeds/subscribe-ui.css (feeds/subscribe-ui.css)
skin/classic/aero/browser/inspector.css
* skin/classic/aero/browser/places/places.css (places/places-aero.css)
* skin/classic/aero/browser/places/organizer.css (places/organizer-aero.css)
skin/classic/aero/browser/places/bookmark.png (places/bookmark-aero.png)

View File

@ -166,6 +166,7 @@ class DeviceManager:
# TODO: We had an old sleep here but we don't need it
while (found == False and (loopguard < recvGuard)):
temp = ''
if (self.debug >= 4): print "recv'ing..."
# Get our response
@ -193,8 +194,9 @@ class DeviceManager:
# If we violently lose the connection to the device, this loop tends to spin,
# this guard prevents that
loopguard = loopguard + 1
if (temp == ''):
loopguard += 1
# TODO: We had an old sleep here but we don't need it
if (shouldCloseSocket == True):
try:

View File

@ -4656,7 +4656,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
_USE_SYSTEM_NSS=1 )
if test -n "$_USE_SYSTEM_NSS"; then
AM_PATH_NSS(3.12.6, [MOZ_NATIVE_NSS=1], [MOZ_NATIVE_NSS=])
AM_PATH_NSS(3.12.8, [MOZ_NATIVE_NSS=1], [MOZ_NATIVE_NSS=])
fi
if test -n "$MOZ_NATIVE_NSS"; then

View File

@ -45,6 +45,7 @@
#include "nsIScriptLoaderObserver.h"
#include "nsWeakPtr.h"
#include "nsIParser.h"
#include "nsContentCreatorFunctions.h"
#define NS_ISCRIPTELEMENT_IID \
{ 0x6d625b30, 0xfac4, 0x11de, \
@ -57,14 +58,15 @@ class nsIScriptElement : public nsIScriptLoaderObserver {
public:
NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISCRIPTELEMENT_IID)
nsIScriptElement()
nsIScriptElement(PRUint32 aFromParser)
: mLineNumber(0),
mIsEvaluated(PR_FALSE),
mAlreadyStarted(PR_FALSE),
mMalformed(PR_FALSE),
mDoneAddingChildren(PR_TRUE),
mFrozen(PR_FALSE),
mDefer(PR_FALSE),
mAsync(PR_FALSE),
mParserCreated((PRUint8)aFromParser),
mCreatorParser(nsnull)
{
}
@ -117,6 +119,15 @@ public:
return mAsync;
}
/**
* Returns a constant defined in nsContentCreatorFunctions.h. Non-zero
* values mean parser-created and zero means not parser-created.
*/
PRUint32 GetParserCreated()
{
return mParserCreated;
}
void SetScriptLineNumber(PRUint32 aLineNumber)
{
mLineNumber = aLineNumber;
@ -137,7 +148,15 @@ public:
void PreventExecution()
{
mIsEvaluated = PR_TRUE;
mAlreadyStarted = PR_TRUE;
}
void LoseParserInsertedness()
{
mFrozen = PR_FALSE;
mUri = nsnull;
mCreatorParser = nsnull;
mParserCreated = NS_NOT_FROM_PARSER;
}
void SetCreatorParser(nsIParser* aParser)
@ -185,7 +204,7 @@ protected:
/**
* The "already started" flag per HTML5.
*/
PRPackedBool mIsEvaluated;
PRPackedBool mAlreadyStarted;
/**
* The script didn't have an end tag.
@ -212,6 +231,11 @@ protected:
*/
PRPackedBool mAsync;
/**
* Whether this element was parser-created.
*/
PRUint8 mParserCreated;
/**
* The effective src (or null if no src).
*/

View File

@ -216,43 +216,65 @@ CSPRep.fromString = function(aStr, self) {
}
}
}
// REPORT URI ///////////////////////////////////////////////////////
if (dirname === UD.REPORT_URI) {
// might be space-separated list of URIs
var uriStrings = dirvalue.split(/\s+/);
var okUriStrings = [];
// Verify that each report URI is in the same etld + 1
// if "self" is defined, and just that it's valid otherwise.
for (let i in uriStrings) {
var uri = null;
try {
var uri = gIoService.newURI(uriStrings[i],null,null);
// Relative URIs are okay, but to ensure we send the reports to the
// right spot, the relative URIs are expanded here during parsing.
// The resulting CSPRep instance will have only absolute URIs.
uri = gIoService.newURI(uriStrings[i],null,selfUri);
// if there's no host, don't do the ETLD+ check. This will throw
// NS_ERROR_FAILURE if the URI doesn't have a host, causing a parse
// failure.
uri.host;
// Verify that each report URI is in the same etld + 1 and that the
// scheme and port match "self" if "self" is defined, and just that
// it's valid otherwise.
if (self) {
if (gETLDService.getBaseDomain(uri) ===
if (gETLDService.getBaseDomain(uri) !==
gETLDService.getBaseDomain(selfUri)) {
okUriStrings.push(uriStrings[i]);
} else {
CSPWarning("can't use report URI from non-matching eTLD+1: "
+ gETLDService.getBaseDomain(uri));
continue;
}
if (!uri.schemeIs(selfUri.scheme)) {
CSPWarning("can't use report URI with different scheme from "
+ "originating document: " + uri.asciiSpec);
continue;
}
if (uri.port && uri.port !== selfUri.port) {
CSPWarning("can't use report URI with different port from "
+ "originating document: " + uri.asciiSpec);
continue;
}
}
} catch(e) {
switch (e.result) {
case Components.results.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS:
case Components.results.NS_ERROR_HOST_IS_IP_ADDRESS:
if (uri.host === selfUri.host) {
okUriStrings.push(uriStrings[i]);
} else {
CSPWarning("page on " + selfUri.host + " cannot send reports to " + uri.host);
if (uri.host !== selfUri.host) {
CSPWarning("page on " + selfUri.host
+ " cannot send reports to " + uri.host);
continue;
}
break;
default:
CSPWarning("couldn't parse report URI: " + uriStrings[i]);
break;
continue;
}
}
// all verification passed: same ETLD+1, scheme, and port.
okUriStrings.push(uri.asciiSpec);
}
aCSPR._directives[UD.REPORT_URI] = okUriStrings.join(' ');
continue directive;

View File

@ -271,6 +271,9 @@ ContentSecurityPolicy.prototype = {
+ (blockedUri['asciiSpec'] ? " by " + blockedUri.asciiSpec : ""));
// For each URI in the report list, send out a report.
// We make the assumption that all of the URIs are absolute URIs; this
// should be taken care of in CSPRep.fromString (where it converts any
// relative URIs into absolute ones based on "self").
for (let i in uris) {
if (uris[i] === "")
continue;

View File

@ -68,16 +68,34 @@ MessageWakeupService.prototype =
},
receiveMessage: function(aMessage) {
var data = this.messagesData[aMessage.name];
delete this.messagesData[aMessage.name];
var service = Cc[data.cid][data.method](Ci[data.iid]).
let data = this.messagesData[aMessage.name];
// TODO: When bug 593407 is ready, stop doing the wrappedJSObject hack
// and use this line instead:
// QueryInterface(Ci.nsIFrameMessageListener);
let service = Cc[data.cid][data.method](Ci[data.iid]).
wrappedJSObject;
// TODO: When bug 593407 is ready, stop doing the wrappedJSObject hack
// and use the line below instead
// QueryInterface(Ci.nsIFrameMessageListener);
this.messageManager.addMessageListener(aMessage.name, service);
this.messageManager.removeMessageListener(aMessage.name, this);
service.receiveMessage(aMessage);
// The receiveMessage() call itself may spin an event loop, and we
// do not want to swap listeners in that - it would cause the current
// message to be answered by two listeners. So, we call that first,
// then queue the swap for the next event loop
let ret = service.receiveMessage(aMessage);
if (data.timer) {
// Handle the case of two such messages happening in quick succession
data.timer.cancel();
data.timer = null;
}
data.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
let self = this;
data.timer.initWithCallback(function() {
self.messageManager.addMessageListener(aMessage.name, service);
self.messageManager.removeMessageListener(aMessage.name, self);
delete self.messagesData[aMessage.name];
}, 0, Ci.nsITimer.TYPE_ONE_SHOT);
return ret;
},
observe: function TM_observe(aSubject, aTopic, aData) {

View File

@ -366,8 +366,8 @@ nsContentSink::ScriptAvailable(nsresult aResult,
PRUint32 count = mScriptElements.Count();
// aElement will not be in mScriptElements if a <script> was added
// using the DOM during loading, or if the script was inline and thus
// never blocked.
// using the DOM during loading or if DoneAddingChildren did not return
// NS_ERROR_HTMLPARSER_BLOCK.
NS_ASSERTION(count == 0 ||
mScriptElements.IndexOf(aElement) == PRInt32(count - 1) ||
mScriptElements.IndexOf(aElement) == -1,

View File

@ -178,7 +178,7 @@ nsScriptElement::MaybeProcessScript()
NS_ASSERTION(cont->DebugGetSlots()->mMutationObservers.Contains(this),
"You forgot to add self as observer");
if (mIsEvaluated || !mDoneAddingChildren || !cont->IsInDoc() ||
if (mAlreadyStarted || !mDoneAddingChildren || !cont->IsInDoc() ||
mMalformed || !HasScriptContent()) {
return NS_OK;
}
@ -187,13 +187,13 @@ nsScriptElement::MaybeProcessScript()
if (InNonScriptingContainer(cont)) {
// Make sure to flag ourselves as evaluated
mIsEvaluated = PR_TRUE;
mAlreadyStarted = PR_TRUE;
return NS_OK;
}
nsresult scriptresult = NS_OK;
nsRefPtr<nsScriptLoader> loader = cont->GetOwnerDoc()->ScriptLoader();
mIsEvaluated = PR_TRUE;
mAlreadyStarted = PR_TRUE;
scriptresult = loader->ProcessScriptElement(this);
// The only error we don't ignore is NS_ERROR_HTMLPARSER_BLOCK

View File

@ -59,6 +59,11 @@ public:
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
nsScriptElement(PRUint32 aFromParser)
: nsIScriptElement(aFromParser)
{
}
protected:
// Internal methods

View File

@ -75,6 +75,7 @@
#include "nsIChannelPolicy.h"
#include "nsChannelPolicy.h"
#include "nsCRT.h"
#include "nsContentCreatorFunctions.h"
#include "mozilla/FunctionTimer.h"
@ -117,7 +118,6 @@ public:
nsCOMPtr<nsIScriptElement> mElement;
PRPackedBool mLoading; // Are we still waiting for a load to complete?
PRPackedBool mDefer; // Is execution defered?
PRPackedBool mIsInline; // Is the script inline or loaded?
nsString mScriptText; // Holds script for loaded scripts
PRUint32 mJSVersion;
@ -140,7 +140,7 @@ nsScriptLoader::nsScriptLoader(nsIDocument *aDocument)
mBlockerCount(0),
mEnabled(PR_TRUE),
mDeferEnabled(PR_FALSE),
mUnblockOnloadWhenDoneProcessing(PR_FALSE)
mDocumentParsingDone(PR_FALSE)
{
// enable logging for CSP
#ifdef PR_LOGGING
@ -153,8 +153,12 @@ nsScriptLoader::~nsScriptLoader()
{
mObservers.Clear();
for (PRInt32 i = 0; i < mRequests.Count(); i++) {
mRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
if (mParserBlockingRequest) {
mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT);
}
for (PRInt32 i = 0; i < mDeferRequests.Count(); i++) {
mDeferRequests[i]->FireScriptAvailable(NS_ERROR_ABORT);
}
for (PRInt32 i = 0; i < mAsyncRequests.Count(); i++) {
@ -339,6 +343,23 @@ nsScriptLoader::PreloadURIComparator::Equals(const PreloadInfo &aPi,
same;
}
class nsScriptRequestProcessor : public nsRunnable
{
private:
nsRefPtr<nsScriptLoader> mLoader;
nsRefPtr<nsScriptLoadRequest> mRequest;
public:
nsScriptRequestProcessor(nsScriptLoader* aLoader,
nsScriptLoadRequest* aRequest)
: mLoader(aLoader)
, mRequest(aRequest)
{}
NS_IMETHODIMP Run()
{
return mLoader->ProcessRequest(mRequest);
}
};
nsresult
nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
{
@ -515,135 +536,143 @@ nsScriptLoader::ProcessScriptElement(nsIScriptElement *aElement)
nsCOMPtr<nsIContent> eltContent(do_QueryInterface(aElement));
eltContent->SetScriptTypeID(typeID);
PRBool hadPendingRequests = !!GetFirstPendingRequest();
// Step 9. in the HTML5 spec
// Did we preload this request?
nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
nsRefPtr<nsScriptLoadRequest> request;
if (scriptURI) {
// external script
nsTArray<PreloadInfo>::index_type i =
mPreloads.IndexOf(scriptURI.get(), 0, PreloadURIComparator());
if (i != nsTArray<PreloadInfo>::NoIndex) {
// preloaded
// note that a script-inserted script can steal a preload!
request = mPreloads[i].mRequest;
request->mElement = aElement;
request->mJSVersion = version;
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred() &&
!aElement->GetScriptAsync();
// XXX what if the charset attribute of the element and the charset
// of the preload don't match?
mPreloads.RemoveElementAt(i);
rv = CheckContentPolicy(mDocument, aElement, request->mURI, type);
if (NS_FAILED(rv)) {
// Note, we're dropping our last ref to request here.
return rv;
NS_ENSURE_SUCCESS(rv, rv);
} else {
// not preloaded
request = new nsScriptLoadRequest(aElement, version);
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
request->mURI = scriptURI;
request->mIsInline = PR_FALSE;
request->mLoading = PR_TRUE;
rv = StartLoad(request, type);
NS_ENSURE_SUCCESS(rv, rv);
}
request->mJSVersion = version;
PRBool async = !aElement->GetParserCreated() || aElement->GetScriptAsync();
// we now have a request that may or may not be still loading
if (!async && aElement->GetScriptDeferred()) {
// We don't want to run this yet.
// If we come here, the script is a parser-created script and it has
// the defer attribute but not the async attribute. Since a
// a parser-inserted script is being run, we came here by the parser
// running the script, which means the parser is still alive and the
// parse is ongoing.
NS_ASSERTION(mDocument->GetCurrentContentSink(),
"Defer script on a document without an active parser; bug 592366.");
mDeferRequests.AppendObject(request);
return NS_OK;
}
if (async) {
mAsyncRequests.AppendObject(request);
if (!request->mLoading) {
// The script is available already. Run it ASAP when the event
// loop gets a chance to spin.
ProcessPendingRequestsAsync();
}
// Can we run the script now?
// This is true if we're done loading, the script isn't deferred and
// there are either no scripts or stylesheets to wait for, or the
// script is async
PRBool readyToRun =
!request->mLoading && !request->mDefer &&
((!hadPendingRequests && ReadyToExecuteScripts()) ||
aElement->GetScriptAsync());
if (readyToRun && nsContentUtils::IsSafeToRunScript()) {
return NS_OK;
}
if (!request->mLoading) {
// The request has already been loaded. If the script comes from the
// network stream, cheat for performance reasons and avoid a trip
// through the event loop.
if (aElement->GetParserCreated() == NS_FROM_PARSER_NETWORK) {
return ProcessRequest(request);
}
// Not done loading yet. Move into the real requests queue and wait.
if (aElement->GetScriptAsync()) {
mAsyncRequests.AppendObject(request);
}
else {
mRequests.AppendObject(request);
}
if (readyToRun) {
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this,
&nsScriptLoader::ProcessPendingRequests));
}
return request->mDefer || aElement->GetScriptAsync() ?
NS_OK : NS_ERROR_HTMLPARSER_BLOCK;
// Otherwise, we've got a document.written script, make a trip through
// the event loop to hide the preload effects from the scripts on the
// Web page.
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
mParserBlockingRequest = request;
ProcessPendingRequestsAsync();
return NS_ERROR_HTMLPARSER_BLOCK;
}
// The script hasn't loaded yet and is parser-inserted and non-async.
// It'll be executed when it has loaded.
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
mParserBlockingRequest = request;
return NS_ERROR_HTMLPARSER_BLOCK;
}
// Create a request object for this script
request = new nsScriptLoadRequest(aElement, version);
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
// inline script
nsCOMPtr<nsIContentSecurityPolicy> csp;
rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
NS_ENSURE_SUCCESS(rv, rv);
// First check to see if this is an external script
if (scriptURI) {
request->mDefer = mDeferEnabled && aElement->GetScriptDeferred() &&
!aElement->GetScriptAsync();
request->mURI = scriptURI;
request->mIsInline = PR_FALSE;
request->mLoading = PR_TRUE;
rv = StartLoad(request, type);
if (NS_FAILED(rv)) {
return rv;
}
} else {
// in-line script
nsCOMPtr<nsIContentSecurityPolicy> csp;
nsresult rv = mDocument->NodePrincipal()->GetCsp(getter_AddRefs(csp));
if (csp) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****"));
PRBool inlineOK;
// this call will send violation reports when necessary
rv = csp->GetAllowsInlineScript(&inlineOK);
NS_ENSURE_SUCCESS(rv, rv);
if (csp) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("New ScriptLoader i ****with CSP****"));
PRBool inlineOK;
// this call will send violation reports when necessary
rv = csp->GetAllowsInlineScript(&inlineOK);
NS_ENSURE_SUCCESS(rv, rv);
if (!inlineOK) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
return NS_ERROR_FAILURE;
}
}
request->mDefer = PR_FALSE;
request->mLoading = PR_FALSE;
request->mIsInline = PR_TRUE;
request->mURI = mDocument->GetDocumentURI();
request->mLineNo = aElement->GetScriptLineNumber();
// If we've got existing pending requests, add ourselves
// to this list.
if (!hadPendingRequests && ReadyToExecuteScripts() &&
nsContentUtils::IsSafeToRunScript()) {
return ProcessRequest(request);
if (!inlineOK) {
PR_LOG(gCspPRLog, PR_LOG_DEBUG, ("CSP blocked inline scripts (2)"));
return NS_ERROR_FAILURE;
}
}
// Add the request to our requests list
NS_ENSURE_TRUE(aElement->GetScriptAsync() ?
mAsyncRequests.AppendObject(request) :
mRequests.AppendObject(request),
NS_ERROR_OUT_OF_MEMORY);
request = new nsScriptLoadRequest(aElement, version);
NS_ENSURE_TRUE(request, NS_ERROR_OUT_OF_MEMORY);
request->mJSVersion = version;
request->mLoading = PR_FALSE;
request->mIsInline = PR_TRUE;
request->mURI = mDocument->GetDocumentURI();
request->mLineNo = aElement->GetScriptLineNumber();
if (request->mDefer || aElement->GetScriptAsync()) {
if (aElement->GetParserCreated() == NS_NOT_FROM_PARSER) {
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
"A script-inserted script is inserted without an update batch?");
nsContentUtils::AddScriptRunner(new nsScriptRequestProcessor(this,
request));
return NS_OK;
}
// If there weren't any pending requests before, and this one is
// ready to execute, do that as soon as it's safe.
if (!request->mLoading && !hadPendingRequests && ReadyToExecuteScripts()) {
nsContentUtils::AddScriptRunner(NS_NewRunnableMethod(this,
&nsScriptLoader::ProcessPendingRequests));
if (aElement->GetParserCreated() == NS_FROM_PARSER_NETWORK &&
!ReadyToExecuteScripts()) {
NS_ASSERTION(!mParserBlockingRequest,
"There can be only one parser-blocking script at a time");
mParserBlockingRequest = request;
return NS_ERROR_HTMLPARSER_BLOCK;
}
// Added as pending request, now we can send blocking back
return NS_ERROR_HTMLPARSER_BLOCK;
// We now have a document.written inline script or we have an inline script
// from the network but there is no style sheet that is blocking scripts.
// Don't check for style sheets blocking scripts in the document.write
// case to avoid style sheet network activity affecting when
// document.write returns. It's not really necessary to do this if
// there's no document.write currently on the call stack. However,
// this way matches IE more closely than checking if document.write
// is on the call stack.
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Not safe to run a parser-inserted script?");
return ProcessRequest(request);
}
nsresult
nsScriptLoader::ProcessRequest(nsScriptLoadRequest* aRequest)
{
NS_ASSERTION(ReadyToExecuteScripts() && nsContentUtils::IsSafeToRunScript(),
"Caller forgot to check ReadyToExecuteScripts()");
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
"Processing requests when running scripts is unsafe.");
NS_ENSURE_ARG(aRequest);
nsAFlatString* script;
@ -804,22 +833,10 @@ nsScriptLoader::EvaluateScript(nsScriptLoadRequest* aRequest,
return rv;
}
nsScriptLoadRequest*
nsScriptLoader::GetFirstPendingRequest()
{
for (PRInt32 i = 0; i < mRequests.Count(); ++i) {
if (!mRequests[i]->mDefer) {
return mRequests[i];
}
}
return nsnull;
}
void
nsScriptLoader::ProcessPendingRequestsAsync()
{
if (GetFirstPendingRequest() || !mPendingChildLoaders.IsEmpty()) {
if (mParserBlockingRequest || !mPendingChildLoaders.IsEmpty()) {
nsCOMPtr<nsIRunnable> ev = NS_NewRunnableMethod(this,
&nsScriptLoader::ProcessPendingRequests);
@ -830,45 +847,47 @@ nsScriptLoader::ProcessPendingRequestsAsync()
void
nsScriptLoader::ProcessPendingRequests()
{
while (1) {
nsRefPtr<nsScriptLoadRequest> request;
if (ReadyToExecuteScripts()) {
request = GetFirstPendingRequest();
if (request && !request->mLoading) {
mRequests.RemoveObject(request);
}
else {
request = nsnull;
}
}
for (PRInt32 i = 0;
!request && mEnabled && i < mAsyncRequests.Count();
++i) {
if (!mAsyncRequests[i]->mLoading) {
request = mAsyncRequests[i];
mAsyncRequests.RemoveObjectAt(i);
}
}
if (!request)
break;
nsCOMPtr<nsScriptLoadRequest> request;
if (mParserBlockingRequest &&
!mParserBlockingRequest->mLoading &&
ReadyToExecuteScripts()) {
request.swap(mParserBlockingRequest);
// nsContentSink::ScriptAvailable unblocks the parser
ProcessRequest(request);
}
PRInt32 i = 0;
while (mEnabled && i < mAsyncRequests.Count()) {
if (!mAsyncRequests[i]->mLoading) {
request = mAsyncRequests[i];
mAsyncRequests.RemoveObjectAt(i);
ProcessRequest(request);
continue;
}
++i;
}
if (mDocumentParsingDone) {
while (mDeferRequests.Count() && !mDeferRequests[0]->mLoading) {
request = mDeferRequests[0];
mDeferRequests.RemoveObjectAt(0);
ProcessRequest(request);
}
}
while (!mPendingChildLoaders.IsEmpty() && ReadyToExecuteScripts()) {
nsRefPtr<nsScriptLoader> child = mPendingChildLoaders[0];
mPendingChildLoaders.RemoveElementAt(0);
child->RemoveExecuteBlocker();
}
if (mUnblockOnloadWhenDoneProcessing && mDocument &&
!GetFirstPendingRequest() && !mAsyncRequests.Count()) {
if (mDocumentParsingDone && mDocument &&
!mParserBlockingRequest && !mAsyncRequests.Count() &&
!mDeferRequests.Count()) {
// No more pending scripts; time to unblock onload.
// OK to unblock onload synchronously here, since callers must be
// prepared for the world changing anyway.
mUnblockOnloadWhenDoneProcessing = PR_FALSE;
mDocumentParsingDone = PR_FALSE;
mDocument->UnblockOnload(PR_TRUE);
}
}
@ -1033,9 +1052,13 @@ nsScriptLoader::OnStreamComplete(nsIStreamLoader* aLoader,
nsresult rv = PrepareLoadedRequest(request, aLoader, aStatus, aStringLen,
aString);
if (NS_FAILED(rv)) {
if (mRequests.RemoveObject(request) ||
if (mDeferRequests.RemoveObject(request) ||
mAsyncRequests.RemoveObject(request)) {
FireScriptAvailable(rv, request);
} else if (mParserBlockingRequest == request) {
mParserBlockingRequest = nsnull;
// nsContentSink::ScriptAvailable unblocks the parser
FireScriptAvailable(rv, request);
} else {
mPreloads.RemoveElement(request, PreloadRequestComparator());
}
@ -1106,9 +1129,10 @@ nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest,
// inserting the request in the array. However it's an unlikely case
// so if you see this assertion it is likely something else that is
// wrong, especially if you see it more than once.
NS_ASSERTION(mRequests.IndexOf(aRequest) >= 0 ||
NS_ASSERTION(mDeferRequests.IndexOf(aRequest) >= 0 ||
mAsyncRequests.IndexOf(aRequest) >= 0 ||
mPreloads.Contains(aRequest, PreloadRequestComparator()),
mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
mParserBlockingRequest,
"aRequest should be pending!");
// Mark this as loaded
@ -1153,15 +1177,13 @@ nsScriptLoader::ParsingComplete(PRBool aTerminated)
if (mDeferEnabled) {
// Have to check because we apparently get ParsingComplete
// without BeginDeferringScripts in some cases
mUnblockOnloadWhenDoneProcessing = PR_TRUE;
mDocumentParsingDone = PR_TRUE;
}
mDeferEnabled = PR_FALSE;
if (aTerminated) {
mRequests.Clear();
} else {
for (PRUint32 i = 0; i < (PRUint32)mRequests.Count(); ++i) {
mRequests[i]->mDefer = PR_FALSE;
}
mDeferRequests.Clear();
mAsyncRequests.Clear();
mParserBlockingRequest = nsnull;
}
// Have to call this even if aTerminated so we'll correctly unblock
@ -1181,8 +1203,6 @@ nsScriptLoader::PreloadURI(nsIURI *aURI, const nsAString &aCharset,
request->mURI = aURI;
request->mIsInline = PR_FALSE;
request->mLoading = PR_TRUE;
request->mDefer = PR_FALSE; // This is computed later when we go to execute the
// script.
nsresult rv = StartLoad(request, aType);
if (NS_FAILED(rv)) {
return;

View File

@ -60,6 +60,7 @@ class nsScriptLoadRequest;
class nsScriptLoader : public nsIStreamLoaderObserver
{
friend class nsScriptRequestProcessor;
public:
nsScriptLoader(nsIDocument* aDocument);
virtual ~nsScriptLoader();
@ -224,7 +225,7 @@ public:
*/
PRUint32 HasPendingOrCurrentScripts()
{
return mCurrentScript || GetFirstPendingRequest();
return mCurrentScript || mParserBlockingRequest;
}
/**
@ -237,7 +238,7 @@ public:
virtual void PreloadURI(nsIURI *aURI, const nsAString &aCharset,
const nsAString &aType);
protected:
private:
/**
* Helper function to check the content policy for a given request.
*/
@ -294,13 +295,11 @@ protected:
PRUint32 aStringLen,
const PRUint8* aString);
// Returns the first pending (non deferred) request
nsScriptLoadRequest* GetFirstPendingRequest();
nsIDocument* mDocument; // [WEAK]
nsCOMArray<nsIScriptLoaderObserver> mObservers;
nsCOMArray<nsScriptLoadRequest> mRequests;
nsCOMArray<nsScriptLoadRequest> mAsyncRequests;
nsCOMArray<nsScriptLoadRequest> mDeferRequests;
nsCOMPtr<nsScriptLoadRequest> mParserBlockingRequest;
// In mRequests, the additional information here is stored by the element.
struct PreloadInfo {
@ -326,7 +325,7 @@ protected:
PRUint32 mBlockerCount;
PRPackedBool mEnabled;
PRPackedBool mDeferEnabled;
PRPackedBool mUnblockOnloadWhenDoneProcessing;
PRPackedBool mDocumentParsingDone;
};
#endif //__nsScriptLoader_h__

View File

@ -9,6 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=28293
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script>
scriptInsertedExternalExecuted = false;
res = 'A';
SimpleTest.waitForExplicitFinish();
@ -23,11 +24,6 @@ onload = function () {
res+='3';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='h';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='i';done()";
s.defer = true;
@ -37,8 +33,8 @@ onload = function () {
}
function done() {
is(res, "AacBCDEFGeHIJfbd1M2g34hi", "scripts executed in the wrong order");
ok(!fHadExecuted, "Dynamic script executed too late");
is(res, "AacBCDEFGeHIJb1M2g3i", "scripts executed in the wrong order");
ok(scriptInsertedExternalExecuted, "Dynamic script did not block load");
SimpleTest.finish();
}
</script>
@ -59,11 +55,6 @@ res += 'B';
<script>
res += 'C';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='d';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='D';";
document.body.appendChild(s);
@ -87,13 +78,10 @@ res += 'e';
<script>
res += 'I';
s = document.createElement('script');
s.src="file_bug28293.sjs?fHadExecuted=(res.indexOf('f')>=0);";
s.src="file_bug28293.sjs?scriptInsertedExternalExecuted=true;";
document.body.appendChild(s);
res += 'J';
</script>
<script defer>
res += 'f';
</script>
</body>
</html>

View File

@ -8,6 +8,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=28293
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script>
scriptInsertedExternalExecuted = false;
res = 'A';
SimpleTest.waitForExplicitFinish();
@ -22,11 +23,6 @@ onload = function () {
res+='3';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='h';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='i';done()";
s.defer = true;
@ -36,8 +32,8 @@ onload = function () {
}
function done() {
is(res, "AacBCDEFGeHIJfbd1M2g34hi", "scripts executed in the wrong order");
ok(!fHadExecuted, "Dynamic script executed too late");
is(res, "AacBCDEFGeHIJb1M2g3i", "scripts executed in the wrong order");
ok(scriptInsertedExternalExecuted, "Dynamic script did not block load");
SimpleTest.finish();
}
</script>
@ -58,11 +54,6 @@ res += 'B';
<script>
res += 'C';
s = document.createElement('script');
s.src="file_bug28293.sjs?res+='d';";
s.defer = true;
document.body.appendChild(s);
s = document.createElement('script');
s.textContent="res+='D';";
document.body.appendChild(s);
@ -87,14 +78,11 @@ res += 'e';
<![CDATA[
res += 'I';
s = document.createElement('script');
s.src="file_bug28293.sjs?fHadExecuted=(res.indexOf('f')>=0);";
s.src="file_bug28293.sjs?scriptInsertedExternalExecuted=true;";
document.body.appendChild(s);
res += 'J';
]]>
</script>
<script defer="defer">
res += 'f';
</script>
</body>
</html>

View File

@ -46,6 +46,26 @@ const POLICY_PORT = 9000;
const POLICY_URI = "http://localhost:" + POLICY_PORT + "/policy";
const POLICY_URI_RELATIVE = "/policy";
// helper to assert that an array has the given value somewhere.
function do_check_in_array(arr, val, stack) {
if (!stack)
stack = Components.stack.caller;
var text = val + " in [" + arr.join(",") + "]";
for(var i in arr) {
dump(".......... " + i + "> " + arr[i] + "\n");
if(arr[i] == val) {
//succeed
++_passedChecks;
dump("TEST-PASS | " + stack.filename + " | [" + stack.name + " : " +
stack.lineNumber + "] " + text + "\n");
return;
}
}
do_throw(text, stack);
}
// helper to assert that an object or array must have a given key
function do_check_has_key(foo, key, stack) {
if (!stack)
@ -493,6 +513,66 @@ test(function test_FrameAncestor_defaults() {
do_check_false(cspr.permits("http://self.com", SD.FRAME_ANCESTORS));
do_check_false(cspr.permits("http://subd.self.com:34", SD.FRAME_ANCESTORS));
});
test(function test_CSP_ReportURI_parsing() {
var cspr;
var SD = CSPRep.SRC_DIRECTIVES;
var self = "http://self.com:34";
var parsedURIs = [];
var uri_valid_absolute = self + "/report.py";
var uri_invalid_host_absolute = "http://foo.org:34/report.py";
var uri_valid_relative = "/report.py";
var uri_valid_relative_expanded = self + uri_valid_relative;
var uri_valid_relative2 = "foo/bar/report.py";
var uri_valid_relative2_expanded = self + "/" + uri_valid_relative2;
var uri_invalid_relative = "javascript:alert(1)";
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_absolute, self);
parsedURIs = cspr.getReportURIs().split(/\s+/);
do_check_in_array(parsedURIs, uri_valid_absolute);
do_check_eq(parsedURIs.length, 1);
cspr = CSPRep.fromString("allow *; report-uri " + uri_invalid_host_absolute, self);
parsedURIs = cspr.getReportURIs().split(/\s+/);
do_check_in_array(parsedURIs, "");
do_check_eq(parsedURIs.length, 1); // the empty string is in there.
cspr = CSPRep.fromString("allow *; report-uri " + uri_invalid_relative, self);
parsedURIs = cspr.getReportURIs().split(/\s+/);
do_check_in_array(parsedURIs, "");
do_check_eq(parsedURIs.length, 1);
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_relative, self);
parsedURIs = cspr.getReportURIs().split(/\s+/);
do_check_in_array(parsedURIs, uri_valid_relative_expanded);
do_check_eq(parsedURIs.length, 1);
cspr = CSPRep.fromString("allow *; report-uri " + uri_valid_relative2, self);
parsedURIs = cspr.getReportURIs().split(/\s+/);
dump(parsedURIs.length);
do_check_in_array(parsedURIs, uri_valid_relative2_expanded);
do_check_eq(parsedURIs.length, 1);
// combination!
cspr = CSPRep.fromString("allow *; report-uri " +
uri_valid_relative2 + " " +
uri_valid_absolute, self);
parsedURIs = cspr.getReportURIs().split(/\s+/);
do_check_in_array(parsedURIs, uri_valid_relative2_expanded);
do_check_in_array(parsedURIs, uri_valid_absolute);
do_check_eq(parsedURIs.length, 2);
cspr = CSPRep.fromString("allow *; report-uri " +
uri_valid_relative2 + " " +
uri_invalid_host_absolute + " " +
uri_valid_absolute, self);
parsedURIs = cspr.getReportURIs().split(/\s+/);
do_check_in_array(parsedURIs, uri_valid_relative2_expanded);
do_check_in_array(parsedURIs, uri_valid_absolute);
do_check_eq(parsedURIs.length, 2);
});
/*
test(function test_CSPRep_fromPolicyURI_failswhenmixed() {

View File

@ -549,6 +549,19 @@ protected:
PRUint32 mInvalidateCount;
static const PRUint32 kCanvasMaxInvalidateCount = 100;
/**
* Returns true iff the the given operator should affect areas of the
* destination where the source is transparent. Among other things, this
* implies that a fully transparent source would still affect the canvas.
*/
PRBool OperatorAffectsUncoveredAreas(gfxContext::GraphicsOperator op) const
{
return op == gfxContext::OPERATOR_IN ||
op == gfxContext::OPERATOR_OUT ||
op == gfxContext::OPERATOR_DEST_IN ||
op == gfxContext::OPERATOR_DEST_ATOP;
}
/**
* Returns true iff a shadow should be drawn along with a
* drawing operation.
@ -564,6 +577,22 @@ protected:
(state.shadowOffset != gfxPoint(0, 0) || state.shadowBlur != 0);
}
/**
* Checks the current state to determine if an intermediate surface would
* be necessary to complete a drawing operation. Does not check the
* condition pertaining to global alpha and patterns since that does not
* pertain to all drawing operations.
*/
PRBool NeedToUseIntermediateSurface()
{
// certain operators always need an intermediate surface, except
// with quartz since quartz does compositing differently than cairo
return OperatorAffectsUncoveredAreas(mThebes->CurrentOperator());
// XXX there are other unhandled cases but they should be investigated
// first to ensure we aren't using an intermediate surface unecessarily
}
/**
* If the current operator is "source" then clear the destination before we
* draw into it, to simulate the effect of an unbounded source operator.
@ -1932,7 +1961,8 @@ nsCanvasRenderingContext2D::DrawPath(Style style, gfxRect *dirtyRect)
* - globalAlpha != 1 and gradients/patterns are used (need to paint_with_alpha)
* - certain operators are used
*/
PRBool doUseIntermediateSurface = NeedIntermediateSurfaceToHandleGlobalAlpha(style);
PRBool doUseIntermediateSurface = NeedToUseIntermediateSurface() ||
NeedIntermediateSurfaceToHandleGlobalAlpha(style);
PRBool doDrawShadow = NeedToDrawShadow();
@ -2751,7 +2781,7 @@ nsCanvasRenderingContext2D::DrawOrMeasureText(const nsAString& aRawText,
// don't need to take care of these with stroke since Stroke() does that
PRBool doDrawShadow = aOp == TEXT_DRAW_OPERATION_FILL && NeedToDrawShadow();
PRBool doUseIntermediateSurface = aOp == TEXT_DRAW_OPERATION_FILL &&
NeedIntermediateSurfaceToHandleGlobalAlpha(STYLE_FILL);
(NeedToUseIntermediateSurface() || NeedIntermediateSurfaceToHandleGlobalAlpha(STYLE_FILL));
// Clear the surface if we need to simulate unbounded SOURCE operator
ClearSurfaceForUnboundedSource();
@ -3559,11 +3589,25 @@ nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1,
}
}
PRBool doUseIntermediateSurface = NeedToUseIntermediateSurface();
mThebes->SetPattern(pattern);
DirtyAllStyles();
/* Direct2D isn't very good at clipping so use Fill() when we can */
if (CurrentState().globalAlpha == 1.0f && mThebes->CurrentOperator() == gfxContext::OPERATOR_OVER) {
if (doUseIntermediateSurface) {
// draw onto a pushed group
mThebes->PushGroup(gfxASurface::CONTENT_COLOR_ALPHA);
mThebes->Clip(clip);
// don't want operators to be applied twice
mThebes->SetOperator(gfxContext::OPERATOR_SOURCE);
mThebes->Paint();
mThebes->PopGroupToSource();
mThebes->Paint(CurrentState().globalAlpha);
} else if (CurrentState().globalAlpha == 1.0f &&
mThebes->CurrentOperator() == gfxContext::OPERATOR_OVER) {
/* Direct2D isn't very good at clipping so use Fill() when we can */
mThebes->NewPath();
mThebes->Rectangle(clip);
mThebes->Fill();
@ -3575,17 +3619,6 @@ nsCanvasRenderingContext2D::DrawImage(nsIDOMElement *imgElt, float a1,
dirty = mThebes->UserToDevice(clip);
}
#if 1
// XXX cairo bug workaround; force a clip update on mThebes.
// Otherwise, a pixman clip gets left around somewhere, and pixman
// (Render) does source clipping as well -- so we end up
// compositing with an incorrect clip. This only seems to affect
// fallback cases, which happen when we have CSS scaling going on.
// This will blow away the current path, but we already blew it
// away in this function earlier.
mThebes->UpdateSurfaceClip();
#endif
FINISH:
if (NS_SUCCEEDED(rv))
rv = Redraw(dirty);

View File

@ -68,10 +68,18 @@ _TEST_FILES_0 = \
image_green-redirect^headers^ \
test_drawImageIncomplete.html \
test_canvas_font_setter.html \
test_2d.composite.uncovered.image.source-in.html \
test_2d.composite.uncovered.image.destination-in.html \
test_2d.composite.uncovered.image.source-out.html \
test_2d.composite.canvas.destination-atop.html \
test_2d.composite.canvas.destination-in.html \
test_2d.composite.canvas.source-in.html \
test_2d.composite.canvas.source-out.html \
test_2d.composite.image.destination-atop.html \
test_2d.composite.image.destination-in.html \
test_2d.composite.image.source-in.html \
test_2d.composite.image.source-out.html \
test_2d.composite.uncovered.image.destination-atop.html \
test_2d.composite.uncovered.image.destination-in.html \
test_2d.composite.uncovered.image.source-in.html \
test_2d.composite.uncovered.image.source-out.html \
test_mozGetAsFile.html \
$(NULL)
@ -89,18 +97,6 @@ _TEST_FILES_0 = \
# test_2d.composite.clip.lighter.html \
#
# Temporarily disabled tests; unbounded operators changed behaviour, need to reevaluate tests
#
# test_2d.composite.canvas.destination-atop.html \
# test_2d.composite.canvas.destination-in.html \
# test_2d.composite.canvas.source-in.html \
# test_2d.composite.canvas.source-out.html \
# test_2d.composite.image.destination-atop.html \
# test_2d.composite.image.destination-in.html \
# test_2d.composite.image.source-in.html \
# test_2d.composite.image.source-out.html \
#
# Tests that fail on Mac (possibly because spec is underdefined?). Bug 407105
ifneq ($(MOZ_WIDGET_TOOLKIT),cocoa)
# XXX vlad don't test these anywhere, cairo behaviour changed

View File

@ -3301,6 +3301,11 @@ nsEventStateManager::GetCrossProcessTarget()
PRBool
nsEventStateManager::IsTargetCrossProcess(nsGUIEvent *aEvent)
{
// Check to see if there is a focused, editable content in chrome,
// in that case, do not forward IME events to content
nsIContent *focusedContent = GetFocusedContent();
if (focusedContent && focusedContent->IsEditable())
return PR_FALSE;
return TabParent::GetIMETabParent() != nsnull;
}
#endif

View File

@ -363,6 +363,7 @@ NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Script)
nsHTMLScriptElement::nsHTMLScriptElement(already_AddRefed<nsINodeInfo> aNodeInfo,
PRUint32 aFromParser)
: nsGenericHTMLElement(aNodeInfo)
, nsScriptElement(aFromParser)
{
mDoneAddingChildren = !aFromParser;
AddMutationObserver(this);
@ -428,7 +429,7 @@ nsHTMLScriptElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
NS_ENSURE_SUCCESS(rv, rv);
// The clone should be marked evaluated if we are.
it->mIsEvaluated = mIsEvaluated;
it->mAlreadyStarted = mAlreadyStarted;
it->mLineNumber = mLineNumber;
it->mMalformed = mMalformed;
@ -477,11 +478,10 @@ nsHTMLScriptElement::DoneAddingChildren(PRBool aHaveNotified)
{
mDoneAddingChildren = PR_TRUE;
nsresult rv = MaybeProcessScript();
if (!mIsEvaluated) {
// Need to thaw the script uri here to allow another script to cause
if (!mAlreadyStarted) {
// Need to lose parser-insertedness here to allow another script to cause
// execution later.
mFrozen = PR_FALSE;
mUri = nsnull;
LoseParserInsertedness();
}
return rv;
}
@ -555,7 +555,7 @@ nsHTMLScriptElement::MaybeProcessScript()
// We tried to evaluate the script but realized it was an eventhandler
// mEvaluated will already be set at this point
NS_ASSERTION(mIsEvaluated, "should have set mIsEvaluated already");
NS_ASSERTION(mAlreadyStarted, "should have set mIsEvaluated already");
NS_ASSERTION(!mScriptEventHandler, "how could we have an SEH already?");
mScriptEventHandler = new nsHTMLScriptEventHandler(this);

View File

@ -9,7 +9,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<body onload="done();">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=300691">Mozilla Bug 300691</a>
<p id="display"></p>
<div id="content" style="display: none">
@ -17,6 +17,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
</div>
<pre id="test">
<script type="text/javascript">
SimpleTest.waitForExplicitFinish();
// First, setup. We'll be toggling these variables as we go.
var test1Ran = false;
var test2Ran = false;
@ -112,6 +113,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
is(test9Ran, true, "Should be 9!");
</script>
<script type="text/javascript">
function done() {
is(test1Ran, true, "Should have run!");
is(test3Ran, false, "Already executed test3 script once");
is(test4Ran, false,
@ -130,6 +132,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=300691
"src attribute load should have started before the attribute got removed");
is(test15bRan, false,
"src attribute still got executed, so this shouldn't have been");
SimpleTest.finish();
}
</script>
</pre>
</body>

View File

@ -135,6 +135,7 @@ NS_INTERFACE_MAP_END_INHERITING(nsSVGScriptElementBase)
nsSVGScriptElement::nsSVGScriptElement(already_AddRefed<nsINodeInfo> aNodeInfo,
PRUint32 aFromParser)
: nsSVGScriptElementBase(aNodeInfo)
, nsScriptElement(aFromParser)
{
mDoneAddingChildren = !aFromParser;
AddMutationObserver(this);
@ -160,7 +161,7 @@ nsSVGScriptElement::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const
NS_ENSURE_SUCCESS(rv, rv);
// The clone should be marked evaluated if we are.
it->mIsEvaluated = mIsEvaluated;
it->mAlreadyStarted = mAlreadyStarted;
it->mLineNumber = mLineNumber;
it->mMalformed = mMalformed;
@ -278,11 +279,10 @@ nsSVGScriptElement::DoneAddingChildren(PRBool aHaveNotified)
{
mDoneAddingChildren = PR_TRUE;
nsresult rv = MaybeProcessScript();
if (!mIsEvaluated) {
// Need to thaw the script uri here to allow another script to cause
if (!mAlreadyStarted) {
// Need to lose parser-insertedness here to allow another script to cause
// execution later.
mFrozen = PR_FALSE;
mUri = nsnull;
LoseParserInsertedness();
}
return rv;
}

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Script-inserted script</title>
</head>
<body>
<div></div>
<script>
function log(text) {
var p = document.createElement("p");
p.appendChild(document.createTextNode(text));
document.getElementsByTagName("div")[0].appendChild(p);
}
var head = document.getElementsByTagName("head")[0];
var external = document.createElement("script");
external.src = "bug591981-script.js";
head.insertBefore(external, head.firstChild); // what jQuery does
var internal = document.createElement("script");
var data = "log('internal')";
try {
internal.text = data;
} catch(e) {
internal.appendChild(document.createTextNode(data));
}
head.insertBefore(internal, head.firstChild); // what jQuery does
</script>
</body>
</html>

View File

@ -0,0 +1,32 @@
<!DOCTYPE html>
<html>
<head>
<title>Script trying to execute parser-inserted non-executed scripts</title>
</head>
<body>
<div></div>
<script></script>
<script></script>
<script>
function log(text) {
var p = document.createElement("p");
p.appendChild(document.createTextNode(text));
document.getElementsByTagName("div")[0].appendChild(p);
}
var head = document.getElementsByTagName("head")[0];
var external = document.getElementsByTagName("script")[0];
external.src = "bug591981-script.js";
var internal = document.getElementsByTagName("script")[1];
var data = "log('internal')";
try {
internal.text = data;
} catch(e) {
internal.appendChild(document.createTextNode(data));
}
</script>
</body>
</html>

View File

@ -0,0 +1,9 @@
<!DOCTYPE html>
<html>
<head>
<title>Script-inserted script</title>
</head>
<body>
<div><p>internal</p><p>external</p></div>
</body>
</html>

View File

@ -0,0 +1 @@
log("external");

View File

@ -4,3 +4,5 @@
== bug439965.html bug439965-ref.html
== bug427779.xml bug427779-ref.xml
== bug559996.html bug559996-ref.html
== bug591981-1.html bug591981-ref.html
== bug591981-2.html bug591981-ref.html

View File

@ -1,5 +1,5 @@
<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
<window class="reftest-wait" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<richlistbox id="a" datasources="" template="d"/>
<script><![CDATA[
function doe() {
@ -17,8 +17,7 @@ try { b.resultBindingChanged(null); } catch(ex) { }
try { b.getResultForId("empty"); } catch(ex) { }
try { b.getResultForContent(node); } catch(ex) { }
try { b.hasGeneratedContent(null, null); } catch(ex) { }
document.documentElement.removeAttribute("class");
}
setTimeout(doe, 0);
window.addEventListener("load", doe, false);
]]></script>
</window>

View File

@ -57,6 +57,9 @@
#ifdef MOZ_ENABLE_DBUS
#include "nsDBusHandlerApp.h"
#endif
#ifdef ANDROID
#include "nsExternalSharingAppService.h"
#endif
// session history
#include "nsSHEntry.h"
@ -110,6 +113,9 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(PlatformLocalHandlerApp_t)
#ifdef MOZ_ENABLE_DBUS
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDBusHandlerApp)
#endif
#ifdef ANDROID
NS_GENERIC_FACTORY_CONSTRUCTOR(nsExternalSharingAppService)
#endif
// session history
NS_GENERIC_FACTORY_CONSTRUCTOR(nsSHEntry)
@ -134,6 +140,9 @@ NS_DEFINE_NAMED_CID(NS_LOCALHANDLERAPP_CID);
#ifdef MOZ_ENABLE_DBUS
NS_DEFINE_NAMED_CID(NS_DBUSHANDLERAPP_CID);
#endif
#ifdef ANDROID
NS_DEFINE_NAMED_CID(NS_EXTERNALSHARINGAPPSERVICE_CID);
#endif
NS_DEFINE_NAMED_CID(NS_SHENTRY_CID);
NS_DEFINE_NAMED_CID(NS_HISTORYENTRY_CID);
NS_DEFINE_NAMED_CID(NS_SHTRANSACTION_CID);
@ -157,6 +166,9 @@ const mozilla::Module::CIDEntry kDocShellCIDs[] = {
{ &kNS_LOCALHANDLERAPP_CID, false, NULL, PlatformLocalHandlerApp_tConstructor },
#ifdef MOZ_ENABLE_DBUS
{ &kNS_DBUSHANDLERAPP_CID, false, NULL, nsDBusHandlerAppConstructor },
#endif
#ifdef ANDROID
{ &kNS_EXTERNALSHARINGAPPSERVICE_CID, false, NULL, nsExternalSharingAppServiceConstructor },
#endif
{ &kNS_SHENTRY_CID, false, NULL, nsSHEntryConstructor },
{ &kNS_HISTORYENTRY_CID, false, NULL, nsSHEntryConstructor },
@ -198,6 +210,9 @@ const mozilla::Module::ContractIDEntry kDocShellContracts[] = {
{ NS_LOCALHANDLERAPP_CONTRACTID, &kNS_LOCALHANDLERAPP_CID },
#ifdef MOZ_ENABLE_DBUS
{ NS_DBUSHANDLERAPP_CONTRACTID, &kNS_DBUSHANDLERAPP_CID },
#endif
#ifdef ANDROID
{ NS_EXTERNALSHARINGAPPSERVICE_CONTRACTID, &kNS_EXTERNALSHARINGAPPSERVICE_CID },
#endif
{ NS_SHENTRY_CONTRACTID, &kNS_SHENTRY_CID },
{ NS_HISTORYENTRY_CONTRACTID, &kNS_HISTORYENTRY_CID },

View File

@ -79,6 +79,8 @@ TEST_FILES = \
test_writer_starvation.html \
$(NULL)
ifneq (mobile,$(MOZ_BUILD_APP))
BROWSER_TEST_FILES = \
browserHelpers.js \
browser_permissionsPrompt.html \
@ -90,8 +92,10 @@ BROWSER_TEST_FILES = \
head.js \
$(NULL)
libs:: $(BROWSER_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
endif
libs:: $(TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
libs:: $(BROWSER_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)

View File

@ -54,7 +54,6 @@
#include "nsTObserverArray.h"
#include "nsIObserver.h"
#include "nsIPrefService.h"
#include "nsIPrefBranch.h"
#include "nsServiceManagerUtils.h"
#include "nsXULAppAPI.h"
#include "nsWeakReference.h"
@ -78,111 +77,6 @@ using namespace mozilla::places;
namespace mozilla {
namespace dom {
class PrefObserver
{
public:
/**
* Pass |aHoldWeak=true| to force this to hold a weak ref to
* |aObserver|. Otherwise, this holds a strong ref.
*
* XXX/cjones: what do domain and prefRoot mean?
*/
PrefObserver(nsIObserver *aObserver, bool aHoldWeak,
const nsCString& aPrefRoot, const nsCString& aDomain)
: mPrefRoot(aPrefRoot)
, mDomain(aDomain)
{
if (aHoldWeak) {
nsCOMPtr<nsISupportsWeakReference> supportsWeakRef =
do_QueryInterface(aObserver);
if (supportsWeakRef)
mWeakObserver = do_GetWeakReference(aObserver);
} else {
mObserver = aObserver;
}
}
~PrefObserver() {}
/**
* Return true if this observer can no longer receive
* notifications.
*/
bool IsDead() const
{
nsCOMPtr<nsIObserver> observer = GetObserver();
return !observer;
}
/**
* Return true iff a request to remove observers matching
* <aObserver, aDomain, aPrefRoot> entails removal of this.
*/
bool ShouldRemoveFrom(nsIObserver* aObserver,
const nsCString& aPrefRoot,
const nsCString& aDomain) const
{
nsCOMPtr<nsIObserver> observer = GetObserver();
return (observer == aObserver &&
mDomain == aDomain && mPrefRoot == aPrefRoot);
}
/**
* Return true iff this should be notified of changes to |aPref|.
*/
bool Observes(const nsCString& aPref) const
{
nsCAutoString myPref(mPrefRoot);
myPref += mDomain;
return StringBeginsWith(aPref, myPref);
}
/**
* Notify this of a pref change that's relevant to our interests
* (see Observes() above). Return false iff this no longer cares
* to observe any more pref changes.
*/
bool Notify() const
{
nsCOMPtr<nsIObserver> observer = GetObserver();
if (!observer) {
return false;
}
nsCOMPtr<nsIPrefBranch> prefBranch;
nsCOMPtr<nsIPrefService> prefService =
do_GetService(NS_PREFSERVICE_CONTRACTID);
if (prefService) {
prefService->GetBranch(mPrefRoot.get(),
getter_AddRefs(prefBranch));
observer->Observe(prefBranch, "nsPref:changed",
NS_ConvertASCIItoUTF16(mDomain).get());
}
return true;
}
private:
already_AddRefed<nsIObserver> GetObserver() const
{
nsCOMPtr<nsIObserver> observer =
mObserver ? mObserver : do_QueryReferent(mWeakObserver);
return observer.forget();
}
// We only either hold a strong or a weak reference to the
// observer, so only either mObserver or
// GetReferent(mWeakObserver) is ever non-null.
nsCOMPtr<nsIObserver> mObserver;
nsWeakPtr mWeakObserver;
nsCString mPrefRoot;
nsCString mDomain;
// disable these
PrefObserver(const PrefObserver&);
PrefObserver& operator=(const PrefObserver&);
};
class AlertObserver
{
public:
@ -222,7 +116,6 @@ private:
ContentChild* ContentChild::sSingleton;
ContentChild::ContentChild()
: mDead(false)
{
}
@ -359,14 +252,6 @@ ContentChild::ActorDestroy(ActorDestroyReason why)
QuickExit();
#endif
// We might be holding the last ref to some of the observers in
// mPrefObserverArray. Some of them try to unregister themselves
// in their dtors (sketchy). To side-step uaf problems and so
// forth, we set this mDead flag. Then, if during a Clear() a
// being-deleted observer tries to unregister itself, it hits the
// |if (mDead)| special case below and we're safe.
mDead = true;
mPrefObservers.Clear();
mAlertObservers.Clear();
XRE_ShutdownChildProcess();
}
@ -398,47 +283,6 @@ ContentChild::QuickExit()
_exit(0);
}
nsresult
ContentChild::AddRemotePrefObserver(const nsCString& aDomain,
const nsCString& aPrefRoot,
nsIObserver* aObserver,
PRBool aHoldWeak)
{
if (aObserver) {
mPrefObservers.AppendElement(
new PrefObserver(aObserver, aHoldWeak, aPrefRoot, aDomain));
}
return NS_OK;
}
nsresult
ContentChild::RemoveRemotePrefObserver(const nsCString& aDomain,
const nsCString& aPrefRoot,
nsIObserver* aObserver)
{
if (mDead) {
// Silently ignore, we're about to exit. See comment in
// ActorDestroy().
return NS_OK;
}
for (PRUint32 i = 0; i < mPrefObservers.Length();
/*we mutate the array during the loop; ++i iff no mutation*/) {
PrefObserver* observer = mPrefObservers[i];
if (observer->IsDead()) {
mPrefObservers.RemoveElementAt(i);
continue;
} else if (observer->ShouldRemoveFrom(aObserver, aPrefRoot, aDomain)) {
mPrefObservers.RemoveElementAt(i);
return NS_OK;
}
++i;
}
NS_WARNING("RemoveRemotePrefObserver(): no observer was matched!");
return NS_ERROR_UNEXPECTED;
}
nsresult
ContentChild::AddRemoteAlertObserver(const nsString& aData,
nsIObserver* aObserver)
@ -449,28 +293,16 @@ ContentChild::AddRemoteAlertObserver(const nsString& aData,
}
bool
ContentChild::RecvNotifyRemotePrefObserver(const nsCString& aPref)
ContentChild::RecvPreferenceUpdate(const nsCString& aPref)
{
for (PRUint32 i = 0; i < mPrefObservers.Length();
/*we mutate the array during the loop; ++i iff no mutation*/) {
PrefObserver* observer = mPrefObservers[i];
if (observer->Observes(aPref) &&
!observer->Notify()) {
// |observer| had a weak ref that went away, so it no
// longer cares about pref changes
mPrefObservers.RemoveElementAt(i);
continue;
}
++i;
}
nsCOMPtr<nsIPrefServiceInternal> prefs = do_GetService("@mozilla.org/preferences-service;1");
prefs->ReadPrefBuffer(aPref);
return true;
}
bool
ContentChild::RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData)
{
printf("ContentChild::RecvNotifyAlertsObserver %s\n", aType.get() );
for (PRUint32 i = 0; i < mAlertObservers.Length();
/*we mutate the array during the loop; ++i iff no mutation*/) {
AlertObserver* observer = mAlertObservers[i];

View File

@ -98,22 +98,10 @@ public:
virtual bool RecvSetOffline(const PRBool& offline);
virtual bool RecvNotifyVisited(const IPC::URI& aURI);
/**
* Notify |aObserver| of changes to |aPrefRoot|.|aDomain|. If
* |aHoldWeak|, only a weak reference to |aObserver| is held.
*/
nsresult AddRemotePrefObserver(const nsCString& aDomain,
const nsCString& aPrefRoot,
nsIObserver* aObserver, PRBool aHoldWeak);
nsresult RemoveRemotePrefObserver(const nsCString& aDomain,
const nsCString& aPrefRoot,
nsIObserver* aObserver);
// auto remove when alertfinished is received.
nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
virtual bool RecvNotifyRemotePrefObserver(const nsCString& aDomain);
virtual bool RecvPreferenceUpdate(const nsCString& aDomain);
virtual bool RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData);

View File

@ -45,6 +45,7 @@
#include "mozilla/net/NeckoParent.h"
#include "nsIPrefBranch.h"
#include "nsIPrefBranch2.h"
#include "nsIPrefService.h"
#include "nsIPrefLocalizedString.h"
#include "nsIObserverService.h"
#include "nsContentUtils.h"
@ -271,7 +272,10 @@ ContentParent::Observe(nsISupports* aSubject,
if (!strcmp(aTopic, "nsPref:changed")) {
// We know prefs are ASCII here.
NS_LossyConvertUTF16toASCII strData(aData);
if (!SendNotifyRemotePrefObserver(strData))
nsCString prefBuffer;
nsCOMPtr<nsIPrefServiceInternal> prefs = do_GetService("@mozilla.org/preferences-service;1");
prefs->SerializePreference(strData, prefBuffer);
if (!SendPreferenceUpdate(prefBuffer))
return NS_ERROR_NOT_AVAILABLE;
}
else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
@ -512,22 +516,27 @@ ContentParent::RecvAsyncMessage(const nsString& aMsg, const nsString& aJSON)
bool
ContentParent::RecvGeolocationStart()
{
nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
if (!geo) {
return true;
if (mGeolocationWatchID == -1) {
nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
if (!geo) {
return true;
}
geo->WatchPosition(this, nsnull, nsnull, &mGeolocationWatchID);
}
geo->WatchPosition(this, nsnull, nsnull, &mGeolocationWatchID);
return true;
}
bool
ContentParent::RecvGeolocationStop()
{
nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
if (!geo) {
return true;
if (mGeolocationWatchID != -1) {
nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
if (!geo) {
return true;
}
geo->ClearWatch(mGeolocationWatchID);
mGeolocationWatchID = -1;
}
geo->ClearWatch(mGeolocationWatchID);
return true;
}

View File

@ -75,7 +75,7 @@ child:
async NotifyVisited(URI uri);
NotifyRemotePrefObserver(nsCString aDomain);
PreferenceUpdate(nsCString pref);
NotifyAlertsObserver(nsCString topic, nsString data);

View File

@ -741,6 +741,11 @@ nsGeolocationService::StartDevice()
if (!sGeoEnabled)
return NS_ERROR_NOT_AVAILABLE;
// we do not want to keep the geolocation devices online
// indefinitely. Close them down after a reasonable period of
// inactivivity
SetDisconnectTimer();
#ifdef MOZ_IPC
if (XRE_GetProcessType() == GeckoProcessType_Content) {
ContentChild* cpc = ContentChild::GetSingleton();
@ -750,26 +755,18 @@ nsGeolocationService::StartDevice()
#endif
// Start them up!
nsresult rv = NS_ERROR_NOT_AVAILABLE;
for (PRUint32 i = mProviders.Count() - 1; i != PRUint32(-1); --i) {
// If any provder gets started without error, go ahead
// and proceed without error
nsresult temp = mProviders[i]->Startup();
if (NS_SUCCEEDED(temp)) {
rv = NS_OK;
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs)
return NS_ERROR_FAILURE;
mProviders[i]->Watch(this);
}
for (PRUint32 i = 0; i < mProviders.Count(); i++) {
mProviders[i]->Startup();
mProviders[i]->Watch(this);
obs->NotifyObservers(mProviders[i],
"geolocation-device-events",
NS_LITERAL_STRING("starting").get());
}
if (NS_FAILED(rv))
return NS_ERROR_NOT_AVAILABLE;
// we do not want to keep the geolocation devices online
// indefinitely. Close them down after a reasonable period of
// inactivivity
SetDisconnectTimer();
return NS_OK;
}
@ -802,8 +799,15 @@ nsGeolocationService::StopDevice()
}
#endif
for (PRUint32 i = mProviders.Count() - 1; i != PRUint32(-1); --i) {
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (!obs)
return;
for (PRUint32 i = 0; i <mProviders.Count(); i++) {
mProviders[i]->Shutdown();
obs->NotifyObservers(mProviders[i],
"geolocation-device-events",
NS_LITERAL_STRING("shutdown").get());
}
}
@ -944,7 +948,11 @@ nsGeolocation::Shutdown()
PRBool
nsGeolocation::HasActiveCallbacks()
{
return mWatchingCallbacks.Length() != 0;
for (PRUint32 i = 0; i < mWatchingCallbacks.Length(); i++)
if (mWatchingCallbacks[i]->IsActive())
return PR_TRUE;
return PR_FALSE;
}
void

View File

@ -97,6 +97,7 @@ class nsGeolocationRequest
void SendLocation(nsIDOMGeoPosition* location);
void MarkCleared();
PRBool IsActive() {return !mCleared;}
PRBool Allowed() {return mAllowed;}
void SetTimeoutTimer();

View File

@ -385,7 +385,7 @@
duration:0.5
});
$('error_message_3').morph('final', {duration:0.5});
wait(1000,function(){
wait(2000,function(){
assertEqual('17px', $('error_test_ul').getStyle('margin-right'));
assertEqual('40px', $('error_test_ul').getStyle('font-size'));
assertEqual('#ffffff', $('error_message_2').getStyle('background-color').parseColor());
@ -506,4 +506,4 @@
// ]]>
</script>
</body>
</html>
</html>

View File

@ -1,5 +1,6 @@
<html>
<head>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="utils_bug260264.js"></script>
</head>
<body>

View File

@ -1,5 +1,6 @@
<html>
<head>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="utils_bug260264.js"></script>
</head>
<body>

View File

@ -7,6 +7,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=260264
<title>Test for Bug 260264</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script type="application/javascript" src="utils_bug260264.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>

View File

@ -1,10 +1,3 @@
(function() {
// For sendMouseEvent:
document.getElementsByTagName("head").item(0)
.appendChild(document.createElement("script")).src =
"/tests/SimpleTest/EventUtils.js";
})();
/**
* Dispatches |handler| to |element|, as if fired in response to |event|.
*/

View File

@ -0,0 +1,41 @@
const Ci = Components.interfaces;
const Cc = Components.classes;
function successCallback(pos){}
var observer = {
QueryInterface: function(iid) {
if (iid.equals(Components.interfaces.nsISupports) ||
iid.equals(Components.interfaces.nsIObserver))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
},
observe: function(subject, topic, data) {
if (data == "shutdown") {
do_check_true(1)
do_test_finished();
}
else if (data == "starting") {
do_check_true(1)
}
},
};
function run_test()
{
// only kill this test when shutdown is called on the provider.
do_test_pending();
var obs = Cc["@mozilla.org/observer-service;1"].getService();
obs = obs.QueryInterface(Ci.nsIObserverService);
obs.addObserver(observer, "geolocation-device-events", false);
var geolocation = Cc["@mozilla.org/geolocation;1"].getService(Ci.nsIDOMGeoGeolocation);
var watchID = geolocation.watchPosition(successCallback);
do_timeout(1000, function() { geolocation.clearWatch(watchID);})
}

View File

@ -20,7 +20,7 @@
android:label="@MOZ_APP_DISPLAYNAME@"
android:configChanges="keyboard|keyboardHidden|orientation|mcc|mnc"
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:launchMode="singleInstance">
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />

View File

@ -42,6 +42,7 @@ import java.util.*;
import java.util.zip.*;
import java.nio.*;
import java.nio.channels.FileChannel;
import java.util.concurrent.*;
import android.os.*;
import android.app.*;
@ -54,6 +55,7 @@ import android.widget.*;
import android.hardware.*;
import android.util.*;
import android.net.*;
abstract public class GeckoApp
extends Activity
@ -84,7 +86,19 @@ abstract public class GeckoApp
void launch()
{
// unpack files in the components directory
unpackComponents();
try {
unpackComponents();
} catch (FileNotFoundException fnfe) {
showErrorDialog(getString(R.string.error_loading_file));
return;
} catch (IOException ie) {
String msg = ie.getMessage();
if (msg.equalsIgnoreCase("No space left on device"))
showErrorDialog(getString(R.string.no_space_to_start_error));
else
showErrorDialog(getString(R.string.error_loading_file));
return;
}
// and then fire us up
Intent i = getIntent();
String env = i.getStringExtra("env0");
@ -138,9 +152,8 @@ abstract public class GeckoApp
int version = Integer.parseInt(versionStr);
if (version < getMinCPUVersion()) {
showErrorDialog("This device does not meet the " +
"minimum system requirements for " +
getAppName() + ".");
showErrorDialog(
getString(R.string.incompatable_cpu_error));
return;
}
else {
@ -155,8 +168,9 @@ abstract public class GeckoApp
if (!useLaunchButton)
mProgressDialog =
ProgressDialog.show(GeckoApp.this, "", getAppName() +
" is loading", true);
ProgressDialog.show(GeckoApp.this, "",
getString(R.string.splash_screen_label),
true);
// Load our JNI libs; we need to do this before launch() because
// setInitialSize will be called even before Gecko is actually up
// and running.
@ -164,7 +178,7 @@ abstract public class GeckoApp
if (useLaunchButton) {
final Button b = new Button(this);
b.setText("Launch");
b.setText("Launch"); // don't need to localize
b.setOnClickListener(new Button.OnClickListener() {
public void onClick (View v) {
// hide the button so we can't be launched again
@ -334,49 +348,48 @@ abstract public class GeckoApp
abstract public int getMinCPUVersion();
protected void unpackComponents()
throws IOException, FileNotFoundException
{
ZipFile zip;
InputStream listStream;
try {
File componentsDir = new File("/data/data/org.mozilla." + getAppName() +"/components");
componentsDir.mkdir();
zip = new ZipFile(getApplication().getPackageResourcePath());
} catch (Exception e) {
Log.i("GeckoAppJava", e.toString());
return;
}
File componentsDir = new File("/data/data/org.mozilla." + getAppName() +
"/components");
componentsDir.mkdir();
zip = new ZipFile(getApplication().getPackageResourcePath());
byte[] buf = new byte[8192];
unpackFile(zip, buf, null, "application.ini");
unpackFile(zip, buf, null, getContentProcessName());
unpackFile(zip, buf, null, "update.locale");
try {
ZipEntry componentsList = zip.getEntry("components/components.manifest");
if (componentsList == null) {
Log.i("GeckoAppJava", "Can't find components.manifest!");
return;
}
unpackFile(zip, buf, null, "update.locale");
} catch (Exception e) {/* this is non-fatal */}
listStream = new BufferedInputStream(zip.getInputStream(componentsList));
} catch (Exception e) {
Log.i("GeckoAppJava", e.toString());
// copy any .xpi file into an extensions/ directory
Enumeration<? extends ZipEntry> zipEntries = zip.entries();
while (zipEntries.hasMoreElements()) {
ZipEntry entry = zipEntries.nextElement();
if (entry.getName().startsWith("extensions/") && entry.getName().endsWith(".xpi")) {
Log.i("GeckoAppJava", "installing extension : " + entry.getName());
unpackFile(zip, buf, entry, entry.getName());
}
}
ZipEntry componentsList = zip.getEntry("components/components.manifest");
if (componentsList == null) {
Log.i("GeckoAppJava", "Can't find components.manifest!");
return;
}
listStream = new BufferedInputStream(zip.getInputStream(componentsList));
StreamTokenizer tkn = new StreamTokenizer(new InputStreamReader(listStream));
String line = "components/";
int status;
boolean addnext = false;
tkn.eolIsSignificant(true);
do {
try {
status = tkn.nextToken();
} catch (IOException e) {
Log.i("GeckoAppJava", e.toString());
return;
}
status = tkn.nextToken();
switch (status) {
case StreamTokenizer.TT_WORD:
if (tkn.sval.equals("binary-component"))
@ -397,57 +410,42 @@ abstract public class GeckoApp
} while (status != StreamTokenizer.TT_EOF);
}
private void unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry, String name)
private void unpackFile(ZipFile zip, byte[] buf, ZipEntry fileEntry,
String name)
throws IOException, FileNotFoundException
{
if (fileEntry == null)
fileEntry = zip.getEntry(name);
if (fileEntry == null) {
Log.i("GeckoAppJava", "Can't find " + name + " in " + zip.getName());
return;
}
if (fileEntry == null)
throw new FileNotFoundException("Can't find " + name + " in " +
zip.getName());
File outFile = new File("/data/data/org.mozilla." + getAppName() + "/" + name);
File outFile = new File("/data/data/org.mozilla." + getAppName() +
"/" + name);
if (outFile.exists() &&
outFile.lastModified() >= fileEntry.getTime() &&
outFile.length() == fileEntry.getSize())
return;
try {
File dir = outFile.getParentFile();
if (!outFile.exists())
dir.mkdirs();
} catch (Exception e) {
Log.i("GeckoAppJava", e.toString());
return;
}
File dir = outFile.getParentFile();
if (!outFile.exists())
dir.mkdirs();
InputStream fileStream;
try {
fileStream = zip.getInputStream(fileEntry);
} catch (Exception e) {
Log.i("GeckoAppJava", e.toString());
return;
}
OutputStream outStream;
try {
outStream = new FileOutputStream(outFile);
while (fileStream.available() > 0) {
int read = fileStream.read(buf, 0, buf.length);
outStream.write(buf, 0, read);
}
fileStream.close();
outStream.close();
} catch (Exception e) {
Log.i("GeckoAppJava", e.toString());
return;
fileStream = zip.getInputStream(fileEntry);
OutputStream outStream = new FileOutputStream(outFile);
while (fileStream.available() > 0) {
int read = fileStream.read(buf, 0, buf.length);
outStream.write(buf, 0, read);
}
fileStream.close();
outStream.close();
outFile.setLastModified(fileEntry.getTime());
}
public String getEnvString() {
Map<String,String> envMap = System.getenv();
Set<Map.Entry<String,String>> envSet = envMap.entrySet();
@ -543,4 +541,64 @@ abstract public class GeckoApp
if (statusCode == 0)
System.exit(0);
}
static final int FILE_PICKER_REQUEST = 1;
private SynchronousQueue<String> mFilePickerResult = new SynchronousQueue();
public String showFilePicker() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("*/*");
GeckoApp.this.
startActivityForResult(
Intent.createChooser(intent,"choose a file"),
FILE_PICKER_REQUEST);
String filePickerResult = "";
try {
filePickerResult = mFilePickerResult.take();
} catch (InterruptedException e) {
Log.i("GeckoApp", "error: " + e);
}
return filePickerResult;
}
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
String filePickerResult = "";
if (data != null && resultCode == RESULT_OK) {
try {
ContentResolver cr = getContentResolver();
Uri uri = data.getData();
String mimeType = cr.getType(uri);
String fileExt = "." +
mimeType.substring(mimeType.lastIndexOf('/') + 1);
File file =
File.createTempFile("tmp_" +
(int)Math.floor(1000 * Math.random()),
fileExt,
new File("/data/data/org.mozilla." +
getAppName()));
FileOutputStream fos = new FileOutputStream(file);
InputStream is = cr.openInputStream(uri);
byte[] buf = new byte[4096];
int len = is.read(buf);
while (len != -1) {
fos.write(buf, 0, len);
len = is.read(buf);
}
fos.close();
filePickerResult = file.getAbsolutePath();
}catch (Exception e) {
Log.e("GeckoApp", "error : "+ e);
}
}
try {
mFilePickerResult.put(filePickerResult);
} catch (InterruptedException e) {
Log.i("GeckoApp", "error: " + e);
}
}
}

View File

@ -361,11 +361,23 @@ class GeckoAppShell
gRestartScheduled = true;
}
static String[] getHandlersForMimeType(String aMimeType) {
static String[] getHandlersForMimeType(String aMimeType, String aAction) {
Intent intent = getIntentForActionString(aAction);
if (aMimeType != null && aMimeType.length() > 0)
intent.setType(aMimeType);
return getHandlersForIntent(intent);
}
static String[] getHandlersForProtocol(String aScheme, String aAction) {
Intent intent = getIntentForActionString(aAction);
Uri uri = new Uri.Builder().scheme(aScheme).build();
intent.setData(uri);
return getHandlersForIntent(intent);
}
static String[] getHandlersForIntent(Intent intent) {
PackageManager pm =
GeckoApp.surfaceView.getContext().getPackageManager();
Intent intent = new Intent();
intent.setType(aMimeType);
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
int numAttr = 4;
String[] ret = new String[list.size() * numAttr];
@ -378,46 +390,34 @@ class GeckoAppShell
ret[i * numAttr + 1] = "";
ret[i * numAttr + 2] = resolveInfo.activityInfo.applicationInfo.packageName;
ret[i * numAttr + 3] = resolveInfo.activityInfo.name;
}
return ret;
}
static String[] getHandlersForProtocol(String aScheme) {
PackageManager pm =
GeckoApp.surfaceView.getContext().getPackageManager();
Intent intent = new Intent();
Uri uri = new Uri.Builder().scheme(aScheme).build();
intent.setData(uri);
List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
int numAttr = 4;
String[] ret = new String[list.size() * numAttr];
for (int i = 0; i < list.size(); i++) {
ResolveInfo resolveInfo = list.get(i);
ret[i * numAttr] = resolveInfo.loadLabel(pm).toString();
if (resolveInfo.isDefault)
ret[i * numAttr + 1] = "default";
else
ret[i * numAttr + 1] = "";
ret[i * numAttr + 2] = resolveInfo.activityInfo.applicationInfo.packageName;
ret[i * numAttr + 3] = resolveInfo.activityInfo.name;
}
return ret;
static Intent getIntentForActionString(String aAction) {
// Default to the view action if no other action as been specified.
if (aAction != null && aAction.length() > 0)
return new Intent(aAction);
else
return new Intent(Intent.ACTION_VIEW);
}
static String getMimeTypeFromExtension(String aFileExt) {
return android.webkit.MimeTypeMap.getSingleton().getMimeTypeFromExtension(aFileExt);
}
static boolean openUriExternal(String aUriSpec, String aMimeType,
String aPackageName, String aClassName) {
// XXX: It's not clear if we should set the action to view or leave it open
Intent intent = new Intent(Intent.ACTION_VIEW);
if (aMimeType.length() > 0)
static boolean openUriExternal(String aUriSpec, String aMimeType, String aPackageName,
String aClassName, String aAction) {
Intent intent = getIntentForActionString(aAction);
if (aAction.equalsIgnoreCase(Intent.ACTION_SEND)) {
intent.putExtra(Intent.EXTRA_TEXT, aUriSpec);
if (aMimeType != null && aMimeType.length() > 0)
intent.setType(aMimeType);
} else if (aMimeType.length() > 0) {
intent.setDataAndType(Uri.parse(aUriSpec), aMimeType);
else
} else {
intent.setData(Uri.parse(aUriSpec));
}
if (aPackageName.length() > 0 && aClassName.length() > 0)
intent.setClassName(aPackageName, aClassName);
@ -510,4 +510,7 @@ class GeckoAppShell
callObserver(alertName, "alertfinished", alertCookie);
removeObserver(alertName);
}
public static String showFilePicker() {
return GeckoApp.mAppContext.showFilePicker();
}
}

View File

@ -80,6 +80,8 @@ GARBAGE += \
gecko.ap_ \
gecko-unaligned.apk \
gecko-unsigned-unaligned.apk \
res/values/strings.xml \
R.java \
$(NULL)
GARBAGE_DIRS += res libs dist classes
@ -128,6 +130,7 @@ DIST_LINK_FILES = \
browserconfig.properties \
blocklist.xml \
chrome.manifest \
extensions \
$(NULL)
ifdef MOZ_IPC
@ -136,6 +139,9 @@ endif
JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
DEFAULT_BRANDPATH = $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales/en-US/brand.dtd
DEFAULT_STRINGSPATH = android_strings.dtd
include $(topsrcdir)/config/rules.mk
# Override the Java settings with some specific android settings
@ -170,10 +176,10 @@ $(RES_DRAWABLE_HDPI):
$(NSINSTALL) -D res/drawable-hdpi
cp $(topsrcdir)/mobile/app/android/drawable-hdpi/* res/drawable-hdpi/
R.java: $(MOZ_APP_ICON) $(RES_DRAWABLE) $(RES_DRAWABLE_HDPI)
R.java: $(MOZ_APP_ICON) $(RES_DRAWABLE) $(RES_DRAWABLE_HDPI) res/values/strings.xml $(LOCALIZED_STRINGS_XML) AndroidManifest.xml
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -J . --custom-package org.mozilla.gecko
gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_DRAWABLE) $(RES_DRAWABLE_HDPI)
gecko.ap_: AndroidManifest.xml res/drawable/icon.png res/drawable-hdpi/icon.png $(RES_DRAWABLE) $(RES_DRAWABLE_HDPI) res/values/strings.xml $(LOCALIZED_STRINGS_XML)
$(AAPT) package -f -M AndroidManifest.xml -I $(ANDROID_SDK)/android.jar -S res -F $@
libs/armeabi/%: $(DIST)/lib/%
@ -209,3 +215,10 @@ endif
$(MOZ_APP_NAME).apk: gecko-unaligned.apk
$(ZIPALIGN) -f -v 4 gecko-unaligned.apk $@
res/values/strings.xml: FORCE
mkdir -p res/values
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) \
-DBRANDPATH="$(DEFAULT_BRANDPATH)" \
-DSTRINGSPATH="$(DEFAULT_STRINGSPATH)" \
$(srcdir)/strings.xml.in \
> $@

View File

@ -0,0 +1,4 @@
<!ENTITY splash_screen_label "&brandShortName; is loading">
<!ENTITY incompatable_cpu_error "This device does not meet the minimum system requirements for &brandShortName;.">
<!ENTITY no_space_to_start_error "There is not enough space available for &brandShortName; to start.">
<!ENTITY error_loading_file "An error occurred when trying to load files required to run &brandShortName;">

View File

@ -0,0 +1,12 @@
#filter substitution
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
#includesubst @BRANDPATH@
#includesubst @STRINGSPATH@
]>
<resources>
<string name="splash_screen_label">&splash_screen_label;</string>
<string name="incompatable_cpu_error">&incompatable_cpu_error;</string>
<string name="no_space_to_start_error">&no_space_to_start_error;</string>
<string name="error_loading_file">&error_loading_file;</string>
</resources>

View File

@ -2691,8 +2691,9 @@ _cairo_d2d_blend_surface(cairo_d2d_surface_t *dst,
rectSrc.right = (float)(x2 * transform->xx + transform->x0);
rectSrc.bottom = (float)(y2 * transform->yy + transform->y0);
if (rectSrc.left < 0 || rectSrc.top < 0 ||
rectSrc.right > sourceSize.width || rectSrc.bottom > sourceSize.height) {
if (rectSrc.left < 0 || rectSrc.top < 0 || rectSrc.right < 0 || rectSrc.bottom < 0 ||
rectSrc.right > sourceSize.width || rectSrc.bottom > sourceSize.height ||
rectSrc.left > sourceSize.width || rectSrc.top > sourceSize.height) {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
@ -2702,9 +2703,31 @@ _cairo_d2d_blend_surface(cairo_d2d_surface_t *dst,
rectDst.right = (float)x2;
rectDst.bottom = (float)y2;
// Bug 599658 - if the src rect is inverted in either axis D2D is fine with
// this but it does not actually invert the bitmap. This is an easy way
// of doing that.
D2D1_MATRIX_3X2_F matrix = D2D1::IdentityMatrix();
bool needsTransform = false;
if (rectSrc.left > rectSrc.right) {
rectDst.left = -rectDst.left;
rectDst.right = -rectDst.right;
matrix._11 = -1.0;
needsTransform = true;
}
if (rectSrc.top > rectSrc.bottom) {
rectDst.top = -rectDst.top;
rectDst.bottom = -rectDst.bottom;
matrix._22 = -1.0;
needsTransform = true;
}
D2D1_BITMAP_INTERPOLATION_MODE interpMode =
D2D1_BITMAP_INTERPOLATION_MODE_LINEAR;
if (needsTransform) {
dst->rt->SetTransform(matrix);
}
if (filter == CAIRO_FILTER_NEAREST) {
interpMode = D2D1_BITMAP_INTERPOLATION_MODE_NEAREST_NEIGHBOR;
}
@ -2714,6 +2737,9 @@ _cairo_d2d_blend_surface(cairo_d2d_surface_t *dst,
opacity,
interpMode,
rectSrc);
if (needsTransform) {
dst->rt->SetTransform(D2D1::IdentityMatrix());
}
return rv;
}

View File

@ -474,12 +474,11 @@ BasicThebesLayer::Paint(gfxContext* aContext,
// subpixel AA)
state.mRegionToInvalidate.And(state.mRegionToInvalidate, mVisibleRegion);
InheritContextFlags(target, state.mContext);
mXResolution = paintXRes;
mYResolution = paintYRes;
PaintBuffer(state.mContext,
state.mRegionToDraw, state.mRegionToInvalidate,
aCallback, aCallbackData);
mXResolution = paintXRes;
mYResolution = paintYRes;
Mutated();
} else {
// It's possible that state.mRegionToInvalidate is nonempty here,
@ -1433,6 +1432,10 @@ private:
// copy of the descriptor here so that we can call
// DestroySharedSurface() on the descriptor.
SurfaceDescriptor mBackBuffer;
// When we allocate a new front buffer, we keep a temporary record
// of it until reach PaintBuffer(). Then we pre-fill back->front
// and destroy our record.
SurfaceDescriptor mNewFrontBuffer;
nsIntSize mBufferSize;
};
@ -1450,8 +1453,39 @@ BasicShadowableThebesLayer::PaintBuffer(gfxContext* aContext,
NS_ABORT_IF_FALSE(IsSurfaceDescriptorValid(mBackBuffer),
"should have a back buffer by now");
nsIntRegion updatedRegion = aRegionToDraw;
if (IsSurfaceDescriptorValid(mNewFrontBuffer)) {
// We just allocated a new buffer pair. We want to "pre-fill"
// the new front buffer by copying to it what we just painted
// into the back buffer. This starts off our Swap()s from a
// stable base: the first swap will return the same valid region
// as our new back buffer. Thereafter, we only need to
// invalidate what was painted into the back buffer.
nsRefPtr<gfxASurface> frontBuffer =
BasicManager()->OpenDescriptor(mNewFrontBuffer);
nsRefPtr<gfxASurface> backBuffer =
BasicManager()->OpenDescriptor(mBackBuffer);
nsRefPtr<gfxContext> ctx = new gfxContext(frontBuffer);
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
ctx->DrawSurface(backBuffer, backBuffer->GetSize());
BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this),
mValidRegion,
mXResolution,
mYResolution,
mBuffer.BufferRect(),
mNewFrontBuffer);
// Clear temporary record of new front buffer
mNewFrontBuffer = SurfaceDescriptor();
// And pretend that we didn't update anything, in order to
// stabilize the first swap.
updatedRegion.SetEmpty();
}
BasicManager()->PaintedThebesBuffer(BasicManager()->Hold(this),
aRegionToDraw,
updatedRegion,
mBuffer.BufferRect(),
mBuffer.BufferRotation(),
mBackBuffer);
@ -1472,19 +1506,15 @@ BasicShadowableThebesLayer::CreateBuffer(Buffer::ContentType aType,
mBackBuffer = SurfaceDescriptor();
}
SurfaceDescriptor tmpFront;
// XXX error handling
NS_ABORT_IF_FALSE(!IsSurfaceDescriptorValid(mNewFrontBuffer),
"Bad! Did we create a buffer twice without painting?");
if (!BasicManager()->AllocDoubleBuffer(gfxIntSize(aSize.width, aSize.height),
aType,
&tmpFront,
&mNewFrontBuffer,
&mBackBuffer))
NS_RUNTIMEABORT("creating ThebesLayer 'back buffer' failed!");
mBufferSize = aSize;
nsIntRect bufRect = mVisibleRegion.GetBounds();
BasicManager()->CreatedThebesBuffer(BasicManager()->Hold(this),
bufRect,
tmpFront);
return BasicManager()->OpenDescriptor(mBackBuffer);
}
@ -1770,6 +1800,10 @@ public:
MOZ_COUNT_DTOR(BasicShadowThebesLayer);
}
virtual void SetFrontBuffer(const ThebesBuffer& aNewFront,
const nsIntRegion& aValidRegion,
float aXResolution, float aYResolution);
virtual void SetValidRegion(const nsIntRegion& aRegion)
{
mOldValidRegion = mValidRegion;
@ -1830,6 +1864,25 @@ private:
float mOldYResolution;
};
void
BasicShadowThebesLayer::SetFrontBuffer(const ThebesBuffer& aNewFront,
const nsIntRegion& aValidRegion,
float aXResolution, float aYResolution)
{
mValidRegion = mOldValidRegion = aValidRegion;
mXResolution = mOldXResolution = aXResolution;
mYResolution = mOldYResolution = aYResolution;
nsRefPtr<gfxASurface> newFrontBuffer =
BasicManager()->OpenDescriptor(aNewFront.buffer());
nsRefPtr<gfxASurface> unused;
nsIntRect unusedRect;
nsIntPoint unusedRotation;
mFrontBuffer.Swap(newFrontBuffer, aNewFront.rect(), aNewFront.rotation(),
getter_AddRefs(unused), &unusedRect, &unusedRotation);
mFrontBufferDescriptor = aNewFront.buffer();
}
void
BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront,
const nsIntRegion& aUpdatedRegion,
@ -1842,15 +1895,22 @@ BasicShadowThebesLayer::Swap(const ThebesBuffer& aNewFront,
// We have to invalidate the pixels painted into the new buffer.
// They might overlap with our old pixels.
if (mOldXResolution == mXResolution && mOldYResolution == mYResolution) {
aNewBackValidRegion->Sub(mValidRegion, aUpdatedRegion);
aNewBackValidRegion->Sub(mOldValidRegion, aUpdatedRegion);
} else {
// On resolution changes, pretend that our buffer has the new
// resolution, but just has no valid content. This can avoid
// unnecessary buffer reallocs.
//
// FIXME/bug 598866: when we start re-using buffers after
// resolution changes, we're going to need to implement
// front->back copies to avoid thrashing our valid region by
// always nullifying it.
aNewBackValidRegion->SetEmpty();
mOldXResolution = mXResolution;
mOldYResolution = mYResolution;
}
NS_ASSERTION(mXResolution == mOldXResolution && mYResolution == mOldYResolution,
"Uh-oh, buffer allocation thrash forthcoming!");
*aNewXResolution = mXResolution;
*aNewYResolution = mYResolution;

View File

@ -82,10 +82,18 @@ union SurfaceDescriptor {
SurfaceDescriptorX11;
};
struct ThebesBuffer {
SurfaceDescriptor buffer;
nsIntRect rect;
nsIntPoint rotation;
};
struct OpCreateThebesBuffer {
PLayer layer;
nsIntRect bufferRect;
SurfaceDescriptor initialFront;
ThebesBuffer initialFront;
nsIntRegion frontValidRegion;
float xResolution;
float yResolution;
};
struct OpDestroyThebesFrontBuffer { PLayer layer; };
@ -152,11 +160,6 @@ struct OpRemoveChild { PLayer container; PLayer childLayer; };
// Paint (buffer update)
struct ThebesBuffer {
SurfaceDescriptor buffer;
nsIntRect rect;
nsIntPoint rotation;
};
struct OpPaintThebesBuffer {
PLayer layer;
ThebesBuffer newFrontBuffer;

View File

@ -183,12 +183,19 @@ ShadowLayerForwarder::CreatedCanvasLayer(ShadowableLayer* aCanvas)
void
ShadowLayerForwarder::CreatedThebesBuffer(ShadowableLayer* aThebes,
nsIntRect aBufferRect,
const nsIntRegion& aFrontValidRegion,
float aXResolution,
float aYResolution,
const nsIntRect& aBufferRect,
const SurfaceDescriptor& aTempFrontBuffer)
{
mTxn->AddEdit(OpCreateThebesBuffer(NULL, Shadow(aThebes),
aBufferRect,
aTempFrontBuffer));
ThebesBuffer(aTempFrontBuffer,
aBufferRect,
nsIntPoint(0, 0)),
aFrontValidRegion,
aXResolution,
aYResolution));
}
void

View File

@ -143,13 +143,16 @@ public:
*
* It is expected that Created*Buffer() will be followed by a
* Painted*Buffer() in the same transaction, so that
* |aInitialFrontBuffer| is never actually drawn to screen.
* |aInitialFrontBuffer| is never actually drawn to screen. It is
* OK if it is drawn though.
*/
/**
* |aBufferRect| is the screen rect covered by |aInitialFrontBuffer|.
*/
void CreatedThebesBuffer(ShadowableLayer* aThebes,
nsIntRect aBufferRect,
const nsIntRegion& aFrontValidRegion,
float aXResolution, float aYResolution,
const nsIntRect& aBufferRect,
const SurfaceDescriptor& aInitialFrontBuffer);
/**
* For the next two methods, |aSize| is the size of
@ -394,6 +397,16 @@ public:
mAllocator = aParent;
}
/**
* CONSTRUCTION PHASE ONLY
*
* Override the front buffer and its valid region with the specified
* values. This is called when a new buffer has been created.
*/
virtual void SetFrontBuffer(const ThebesBuffer& aNewFront,
const nsIntRegion& aValidRegion,
float aXResolution, float aYResolution) = 0;
virtual void InvalidateRegion(const nsIntRegion& aRegion)
{
NS_RUNTIMEABORT("ShadowThebesLayers can't fill invalidated regions");

View File

@ -195,12 +195,8 @@ ShadowLayersParent::RecvUpdate(const nsTArray<Edit>& cset,
ShadowThebesLayer* thebes = static_cast<ShadowThebesLayer*>(
AsShadowLayer(otb)->AsLayer());
ThebesBuffer unusedBuffer;
nsIntRegion unusedRegion; float unusedXRes, unusedYRes;
thebes->Swap(
ThebesBuffer(otb.initialFront(), otb.bufferRect(), nsIntPoint(0, 0)),
unusedRegion,
&unusedBuffer, &unusedRegion, &unusedXRes, &unusedYRes);
thebes->SetFrontBuffer(otb.initialFront(), otb.frontValidRegion(),
otb.xResolution(), otb.yResolution());
break;
}

View File

@ -247,6 +247,10 @@ CanvasLayerOGL::RenderLayer(int aPreviousDestination,
mCanvasGLContext->GetContextType() == gl()->GetContextType();
if (useGLContext) {
mCanvasGLContext->MakeCurrent();
mCanvasGLContext->fFlush();
gl()->MakeCurrent();
gl()->BindTex2DOffscreen(mCanvasGLContext);
DEBUG_GL_ERROR_CHECK(gl());
program = mOGLManager->GetRGBALayerProgram();

View File

@ -169,9 +169,8 @@ ContainerLayerOGL::RenderLayer(int aPreviousFrameBuffer,
childOffset.x = visibleRect.x;
childOffset.y = visibleRect.y;
// Note that we don't set a new viewport here, even though we're
// about to render to a new FBO -- see the comments in
// LayerManagerOGL::SetupPipeline.
gl()->PushViewportRect();
mOGLManager->SetupPipeline(visibleRect.width, visibleRect.height);
gl()->fScissor(0, 0, visibleRect.width, visibleRect.height);
gl()->fClearColor(0.0, 0.0, 0.0, 0.0);
@ -214,6 +213,12 @@ ContainerLayerOGL::RenderLayer(int aPreviousFrameBuffer,
if (needsFramebuffer) {
// Unbind the current framebuffer and rebind the previous one.
// Restore the viewport
gl()->PopViewportRect();
nsIntRect viewport = gl()->ViewportRect();
mOGLManager->SetupPipeline(viewport.width, viewport.height);
gl()->fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, aPreviousFrameBuffer);
gl()->fDeleteFramebuffers(1, &frameBuffer);

View File

@ -671,20 +671,7 @@ LayerManagerOGL::Render()
void
LayerManagerOGL::SetupPipeline(int aWidth, int aHeight)
{
// Set the viewport correctly. Note that his viewport is used
// throughout the GL layers rendering pipeline, even when we're
// rendering to a FBO with different dimensions than the window.
// This means that we can set the viewMatrix once on every program
// (below). When we render to a FBO (as in ContainerLayerOGL), we
// have to pass a correct child offset so that the coordinate system
// is translated appropriately to start at the origin of the FBO
// (or, put another way, so that the FBO looks to be at the right
// spot in the parent).
//
// Note: this effectively means that we can't really draw to a FBO
// that is bigger than the window dimensions. This is fine for now,
// but might be a problem if we ever start doing GL drawing to
// retained layer FBOs that happen to retain more than is visible.
// Set the viewport correctly.
//
// When we're not double buffering, we use a FBO as our backbuffer.
// We use a normal view transform in that case, meaning that our FBO

View File

@ -314,12 +314,18 @@ public:
}
#ifdef MOZ_LAYERS_HAVE_LOG
virtual const char* Name() const { return "OGL"; }
virtual const char* Name() const { return "OGL"; }
#endif // MOZ_LAYERS_HAVE_LOG
const nsIntSize& GetWigetSize() {
return mWidgetSize;
}
const nsIntSize& GetWigetSize() {
return mWidgetSize;
}
/**
* Setup the viewport and projection matrix for rendering
* to a window of the given dimensions.
*/
void SetupPipeline(int aWidth, int aHeight);
private:
/** Widget associated with this layer manager */
@ -382,15 +388,12 @@ private:
* Render the current layer tree to the active target.
*/
void Render();
/**
* Setup the viewport and projection matrix for rendering
* to a window of the given dimensions.
*/
void SetupPipeline(int aWidth, int aHeight);
/**
* Setup a backbuffer of the given dimensions.
*/
void SetupBackBuffer(int aWidth, int aHeight);
/**
* Copies the content of our backbuffer to the set transaction target.
*/

View File

@ -343,10 +343,7 @@ GLContextProviderCGL::CreateForWindow(nsIWidget *aWidget)
[context setView:childView];
// make the context transparent
GLint opaque = 0;
[context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity];
nsRefPtr<GLContextCGL> glContext = new GLContextCGL(ContextFormat(ContextFormat::BasicRGBA32),
nsRefPtr<GLContextCGL> glContext = new GLContextCGL(ContextFormat(ContextFormat::BasicRGB24),
shareContext,
context);
if (!glContext->Init()) {

View File

@ -2881,8 +2881,10 @@ nsPluginInstanceOwner::SetInstance(nsIPluginInstance *aInstance)
mInstance = aInstance;
PRBool useAsyncPainting = PR_FALSE;
if (mInstance &&
NS_SUCCEEDED(mInstance->UseAsyncPainting(&useAsyncPainting)))
mUsePluginLayers = useAsyncPainting;
mUsePluginLayers &&
NS_SUCCEEDED(mInstance->UseAsyncPainting(&useAsyncPainting)) &&
!useAsyncPainting)
mUsePluginLayers = PR_FALSE;
return NS_OK;
}

View File

@ -11,7 +11,7 @@
div::first-line { color: red }
</style>
</head>
<body onload="document.getElementById('test').className = ''">
<body>
<div>
<span class="some value" style="float: left">
This should be green

View File

@ -7,7 +7,7 @@
.inner { border: 6px sold green; }
</style>
</head>
<body onload="doTest()">
<body>
<span class="outermost" style="border-right: none">
<span class="outer" style="border-right: none">
<span class="inner" style="border-right: none">

View File

@ -11,7 +11,7 @@
#i::after { display: block; content: "Three"; }
</style>
</head>
<body onload="doTest()">
<body>
<div id="i"><script>document.body.offsetWidth</script><div>One</div>Two</div>
</body>
</html>

View File

@ -49,7 +49,11 @@ MODULE = test_libpr0n
XPCSHELL_TESTS = unit
DIRS += mochitest \
browser \
$(NULL)
ifneq (mobile,$(MOZ_BUILD_APP))
DIRS += browser \
$(NULL)
endif
include $(topsrcdir)/config/rules.mk

View File

@ -170,6 +170,8 @@ interface nsIPrefServiceInternal : nsISupports
void readExtensionPrefs(in nsILocalFile aFile);
ACString serializePreferences();
ACString serializePreference(in ACString aPrefName);
void readPrefBuffer(in ACString aBuffer);
};
%{C++

View File

@ -3210,8 +3210,12 @@ pref("gfx.color_management.mode", 0);
#ifdef XP_WIN
pref("layers.accelerate-all", true);
#else
#ifdef XP_MACOSX
pref("layers.accelerate-all", true);
#else
pref("layers.accelerate-all", false);
#endif
#endif
// Whether to allow acceleration on layers at all.
pref("layers.accelerate-none", false);

View File

@ -148,12 +148,7 @@ nsresult nsPrefService::Init()
nsCAutoString prefs;
cpc->SendReadPrefs(&prefs);
PrefParseState ps;
PREF_InitParseState(&ps, PREF_ReaderCallback, NULL);
nsresult rv = PREF_ParseBuf(&ps, prefs.get(), prefs.Length());
PREF_FinalizeParseState(&ps);
return rv;
return ReadPrefBuffer(prefs);
}
#endif
@ -359,6 +354,34 @@ NS_IMETHODIMP nsPrefService::SerializePreferences(nsACString& prefs)
return NS_OK;
}
NS_IMETHODIMP nsPrefService::SerializePreference(const nsACString& aPrefName, nsACString& aBuffer)
{
PrefHashEntry *pref = pref_HashTableLookup(nsDependentCString(aPrefName).get());
if (!pref)
return NS_ERROR_NOT_AVAILABLE;
char* prefArray = nsnull;
pref_saveArgs saveArgs;
saveArgs.prefArray = &prefArray;
saveArgs.saveTypes = SAVE_ALL_AND_DEFAULTS;
pref_savePref(&gHashTable, pref, 0, &saveArgs);
aBuffer = prefArray;
PR_Free(prefArray);
return NS_OK;
}
NS_IMETHODIMP nsPrefService::ReadPrefBuffer(const nsACString& aBuffer)
{
PrefParseState ps;
PREF_InitParseState(&ps, PREF_ReaderCallback, NULL);
nsresult rv = PREF_ParseBuf(&ps, nsDependentCString(aBuffer).get(), aBuffer.Length());
PREF_FinalizeParseState(&ps);
return rv;
}
NS_IMETHODIMP nsPrefService::GetBranch(const char *aPrefRoot, nsIPrefBranch **_retval)
{
nsresult rv;

View File

@ -184,7 +184,6 @@ static nsresult pref_DoCallback(const char* changed_pref);
static nsresult pref_HashPref(const char *key, PrefValue value, PrefType type, PRBool defaultPref);
static inline PrefHashEntry* pref_HashTableLookup(const void *key);
#define PREF_HASHTABLE_INITIAL_SIZE 2048
@ -387,7 +386,6 @@ pref_CompareStrings(const void *v1, const void *v2, void *unused)
return strcmp(s1, s2);
}
PRBool PREF_HasUserPref(const char *pref_name)
{
if (!gHashTable.ops)
@ -678,7 +676,7 @@ static void pref_SetValue(PrefValue* oldValue, PrefValue newValue, PrefType type
gDirty = PR_TRUE;
}
static inline PrefHashEntry* pref_HashTableLookup(const void *key)
PrefHashEntry* pref_HashTableLookup(const void *key)
{
PrefHashEntry* result =
static_cast<PrefHashEntry*>(PL_DHashTableOperate(&gHashTable, key, PL_DHASH_LOOKUP));

View File

@ -52,3 +52,4 @@ PLDHashOperator
pref_savePref(PLDHashTable *table, PLDHashEntryHdr *heh, PRUint32 i, void *arg);
int pref_CompareStrings(const void *v1, const void *v2, void* unused);
PrefHashEntry* pref_HashTableLookup(const void *key);

View File

@ -0,0 +1,21 @@
const Ci = Components.interfaces;
const Cc = Components.classes;
function isParentProcess() {
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
}
function run_test() {
if (isParentProcess() == false) {
do_load_child_test_harness();
var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
pb.setBoolPref("Test.IPC.bool.new", true);
pb.setIntPref("Test.IPC.int.new", 23);
pb.setCharPref("Test.IPC.char.new", "hey");
run_test_in_child("test_observed_prefs.js");
}
}

View File

@ -0,0 +1,18 @@
const Ci = Components.interfaces;
const Cc = Components.classes;
function isParentProcess() {
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
}
function run_test() {
if (isParentProcess() == false) {
var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
pb.setBoolPref("Test.IPC.bool", true);
pb.setIntPref("Test.IPC.int", 23);
pb.setCharPref("Test.IPC.char", "hey");
run_test_in_child("test_existing_prefs.JS");
}
}

View File

@ -0,0 +1,16 @@
const Ci = Components.interfaces;
const Cc = Components.classes;
function isParentProcess() {
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
}
function run_test() {
if (isParentProcess() == false) {
var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
do_check_eq(pb.getBoolPref("Test.IPC.bool.new"), true);
do_check_eq(pb.getIntPref("Test.IPC.int.new"), 23);
do_check_eq(pb.getCharPref("Test.IPC.char.new"), "hey");
}
}

View File

@ -1,58 +0,0 @@
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
function isParentProcess() {
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
}
function verify_existing_prefs() {
const Ci = Components.interfaces;
const Cc = Components.classes;
var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
do_check_eq(pb.getBoolPref("Test.IPC.bool"), true);
do_check_eq(pb.getIntPref("Test.IPC.int"), 23);
do_check_eq(pb.getCharPref("Test.IPC.char"), "hey");
do_test_finished();
_do_main();
}
function verify_observed_prefs() {
const Ci = Components.interfaces;
const Cc = Components.classes;
var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
do_test_pending();
do_check_eq(pb.getBoolPref("Test.IPC.bool.new"), true);
do_check_eq(pb.getIntPref("Test.IPC.int.new"), 23);
do_check_eq(pb.getCharPref("Test.IPC.char.new"), "hey");
do_test_finished();
_do_main();
}
function run_test() {
if (isParentProcess()) {
var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
pb.setBoolPref("Test.IPC.bool", true);
pb.setIntPref("Test.IPC.int", 23);
pb.setCharPref("Test.IPC.char", "hey");
do_load_child_test_harness();
do_test_pending();
sendCommand(verify_existing_prefs.toString(), do_test_finished);
// these prefs are set after the child has been created.
pb.setBoolPref("Test.IPC.bool.new", true);
pb.setIntPref("Test.IPC.int.new", 23);
pb.setCharPref("Test.IPC.char.new", "hey");
do_test_pending();
sendCommand(verify_existing_prefs.toString(), do_test_finished);
}
else {
}
}

View File

@ -0,0 +1,25 @@
const Ci = Components.interfaces;
const Cc = Components.classes;
function isParentProcess() {
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
}
function run_test() {
if (isParentProcess()) {
do_load_child_test_harness();
var pb = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
// these prefs are set after the child has been created.
pb.setBoolPref("Test.IPC.bool.new", true);
pb.setIntPref("Test.IPC.int.new", 23);
pb.setCharPref("Test.IPC.char.new", "hey");
run_test_in_child("test_observed_prefs.js");
}
else {
}
}

View File

@ -129,11 +129,12 @@ static const char * prefList[] = {
MEMORY_CACHE_CAPACITY_PREF
};
// Let our base line be 250MB.
const PRInt32 BASE_LINE = 250 * 1024 * 1024;
const PRInt32 MIN_SIZE = 50 * 1024 * 1024;
const PRInt32 MAX_SIZE = 1024 * 1024 * 1024;
// Cache sizes, in KB
const PRInt32 DEFAULT_CACHE_SIZE = 250 * 1024; // 250 MB
const PRInt32 MIN_CACHE_SIZE = 50 * 1024; // 50 MB
const PRInt32 MAX_CACHE_SIZE = 1024 * 1024; // 1 GB
// Default cache size was 50 MB for many years until FF 4:
const PRInt32 PRE_GECKO_2_0_DEFAULT_CACHE_SIZE = 50 * 1024;
class nsCacheProfilePrefObserver : public nsIObserver
{
@ -171,7 +172,7 @@ public:
PRBool MemoryCacheEnabled();
PRInt32 MemoryCacheCapacity();
static PRUint32 GetSmartCacheSize(void);
static PRUint32 GetSmartCacheSize(const nsAString& cachePath);
private:
bool PermittedToSmartSize(nsIPrefBranch*, PRBool firstRun);
@ -243,13 +244,18 @@ private:
class nsGetSmartSizeEvent: public nsRunnable
{
public:
nsGetSmartSizeEvent(bool firstRun) : mFirstRun(firstRun) , mSmartSize(0) {}
nsGetSmartSizeEvent(bool firstRun, const nsAString& cachePath)
: mFirstRun(firstRun)
, mCachePath(cachePath)
, mSmartSize(0)
{}
// Calculates user's disk space available on a background thread and
// dispatches this value back to the main thread.
NS_IMETHOD Run()
{
mSmartSize = nsCacheProfilePrefObserver::GetSmartCacheSize() / 1024;
mSmartSize =
nsCacheProfilePrefObserver::GetSmartCacheSize(mCachePath);
nsCOMPtr<nsIRunnable> event = new nsSetSmartSizeEvent(mFirstRun,
mSmartSize);
NS_DispatchToMainThread(event);
@ -258,6 +264,7 @@ public:
private:
bool mFirstRun;
nsString mCachePath;
PRInt32 mSmartSize;
};
@ -374,10 +381,12 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
} else if (!strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, topic)) {
// ignore pref changes until we're done switch profiles
if (!mHaveProfile) return NS_OK;
if (!mHaveProfile)
return NS_OK;
nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(subject, &rv);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv))
return rv;
#ifdef NECKO_DISK_CACHE
// which preference changed?
@ -386,7 +395,8 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
if (!mInPrivateBrowsing) {
rv = branch->GetBoolPref(DISK_CACHE_ENABLE_PREF,
&mDiskCacheEnabled);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv))
return rv;
nsCacheService::SetDiskCacheEnabled(DiskCacheEnabled());
}
@ -394,7 +404,8 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
PRInt32 capacity = 0;
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &capacity);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv))
return rv;
mDiskCacheCapacity = PR_MAX(0, capacity);
nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
@ -404,23 +415,30 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
PRBool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv))
return rv;
PRInt32 newCapacity = 0;
if (smartSizeEnabled) {
// Dispatch event to update smart size: just keep using old
// value if this fails at any point
if (!mDiskCacheParentDirectory)
return NS_ERROR_NOT_AVAILABLE; // disk cache disabled anyway
nsAutoString cachePath;
rv = mDiskCacheParentDirectory->GetPath(cachePath);
if (NS_FAILED(rv))
return rv;
// Smart sizing switched on: recalculate the capacity.
nsCOMPtr<nsIRunnable> event = new nsGetSmartSizeEvent(false);
nsCOMPtr<nsIRunnable> event =
new nsGetSmartSizeEvent(false, cachePath);
rv = nsCacheService::DispatchToCacheIOThread(event);
// If the dispatch failed, just use our base line for the size
if (NS_FAILED(rv)) mDiskCacheCapacity = BASE_LINE;
} else {
// Smart sizing switched off: use user specified size
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &newCapacity);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv))
return rv;
mDiskCacheCapacity = PR_MAX(0, newCapacity);
nsCacheService::SetDiskCacheCapacity(mDiskCacheCapacity);
}
#if 0
} else if (!strcmp(DISK_CACHE_DIR_PREF, data.get())) {
// XXX We probaby don't want to respond to this pref except after
@ -463,7 +481,8 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
rv = branch->GetBoolPref(MEMORY_CACHE_ENABLE_PREF,
&mMemoryCacheEnabled);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv))
return rv;
nsCacheService::SetMemoryCache();
} else if (!strcmp(MEMORY_CACHE_CAPACITY_PREF, data.get())) {
@ -495,7 +514,8 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
#if defined(NECKO_DISK_CACHE) || defined(NECKO_OFFLINE_CACHE)
nsCOMPtr<nsIPrefBranch> branch = do_GetService(NS_PREFSERVICE_CONTRACTID, &rv);
if (NS_FAILED(rv)) return rv;
if (NS_FAILED(rv))
return rv;
#endif // !NECKO_DISK_CACHE && !NECKO_OFFLINE_CACHE
#ifdef NECKO_DISK_CACHE
@ -516,8 +536,6 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
return NS_OK;
}
/* Computes our best guess for the default size of the user's disk cache,
* based on the amount of space they have free on their hard drive.
@ -528,61 +546,48 @@ nsCacheProfilePrefObserver::Observe(nsISupports * subject,
* lower bound of 50MB and an upper bound of 1GB.
*
*@param: None.
*@return: The size that the user's disk cache should default to, in bytes.
*@return: The size that the user's disk cache should default to, in kBytes.
*/
PRUint32
nsCacheProfilePrefObserver::GetSmartCacheSize(void) {
// Get a handle to disk where cache lives, so we can check for free space
nsCacheProfilePrefObserver::GetSmartCacheSize(const nsAString& cachePath)
{
// Check for free space on device where cache directory lives
nsresult rv;
nsCOMPtr<nsIFile> profileDirectory;
rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
getter_AddRefs(profileDirectory));
if (NS_FAILED(rv)) {
return BASE_LINE;
}
nsCOMPtr<nsILocalFile> diskHandle = do_QueryInterface(profileDirectory);
nsCOMPtr<nsILocalFile>
cacheDirectory (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
if (NS_FAILED(rv) || !cacheDirectory)
return DEFAULT_CACHE_SIZE;
rv = cacheDirectory->InitWithPath(cachePath);
if (NS_FAILED(rv))
return DEFAULT_CACHE_SIZE;
PRInt64 bytesAvailable;
diskHandle->GetDiskSpaceAvailable(&bytesAvailable);
rv = cacheDirectory->GetDiskSpaceAvailable(&bytesAvailable);
if (NS_FAILED(rv))
return DEFAULT_CACHE_SIZE;
PRInt64 kBytesAvail = bytesAvailable / 1024;
/* 0MB <= Available < 500MB
* Use between 50MB and 200MB
*/
if (bytesAvailable < BASE_LINE * 2) {
return PR_MAX(MIN_SIZE, bytesAvailable * 4 / 10);
}
// 0 MB <= Available < 500 MB: Use between 50MB and 200MB
if (kBytesAvail < DEFAULT_CACHE_SIZE * 2)
return PR_MAX(MIN_CACHE_SIZE, kBytesAvail * 4 / 10);
/* 500MB <= Available < 2500MB
* Use 250MB
*/
if (bytesAvailable < static_cast<PRInt64>(BASE_LINE) * 10) {
return BASE_LINE;
}
// 500MB <= Available < 2.5 GB: Use 250MB
if (kBytesAvail < static_cast<PRInt64>(DEFAULT_CACHE_SIZE) * 10)
return DEFAULT_CACHE_SIZE;
/* 2500MB <= Available < 5000MB
* Use between 250MB and 500MB
*/
if (bytesAvailable < static_cast<PRInt64>(BASE_LINE) * 20) {
return bytesAvailable / 10;
}
// 2.5 GB <= Available < 5 GB: Use between 250MB and 500MB
if (kBytesAvail < static_cast<PRInt64>(DEFAULT_CACHE_SIZE) * 20)
return kBytesAvail / 10;
/* 5000MB <= Available < 50000MB
* Use 625MB
*/
if (bytesAvailable < static_cast<PRInt64>(BASE_LINE) * 200 ) {
return BASE_LINE * 5 / 2;
}
// 5 GB <= Available < 50 GB: Use 625MB
if (kBytesAvail < static_cast<PRInt64>(DEFAULT_CACHE_SIZE) * 200 )
return DEFAULT_CACHE_SIZE * 5 / 2;
/* 50000MB <= Available < 75000MB
* Use 800MB
*/
if (bytesAvailable < static_cast<PRInt64>(BASE_LINE) * 300) {
return BASE_LINE / 5 * 16;
}
// 50 GB <= Available < 75 GB: Use 800MB
if (kBytesAvail < static_cast<PRInt64>(DEFAULT_CACHE_SIZE) * 300)
return DEFAULT_CACHE_SIZE / 5 * 16;
/* We have come within range of the ceiling
* Use 1GB
*/
return MAX_SIZE;
// Use 1 GB
return MAX_CACHE_SIZE;
}
/* Determine if we are permitted to dynamically size the user's disk cache based
@ -594,9 +599,6 @@ nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, PRBool
firstRun)
{
nsresult rv;
// If user has explicitly set cache size to be smaller than previous default
// of 250MB, then smart sizing is off by default. Otherwise, smart sizing is
// on by default.
if (firstRun) {
// check if user has set cache size in the past
PRBool userSet;
@ -604,18 +606,24 @@ nsCacheProfilePrefObserver::PermittedToSmartSize(nsIPrefBranch* branch, PRBool
if (NS_FAILED(rv)) userSet = PR_TRUE;
if (userSet) {
PRInt32 oldCapacity;
// If user explicitly set cache size to be smaller than old default
// of 50 MB, then keep user's value. Otherwise use smart sizing.
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
if (oldCapacity < BASE_LINE / 1024) {
if (oldCapacity < PRE_GECKO_2_0_DEFAULT_CACHE_SIZE) {
branch->SetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
PR_FALSE);
return false;
}
}
// Set manual setting to MAX cache size as starting val for any
// adjustment by user: (bug 559942 comment 65)
branch->SetIntPref(DISK_CACHE_CAPACITY_PREF, MAX_CACHE_SIZE);
}
PRBool smartSizeEnabled;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_ENABLED_PREF,
&smartSizeEnabled);
if (NS_FAILED(rv)) return false;
if (NS_FAILED(rv))
return false;
return !!smartSizeEnabled;
}
@ -681,23 +689,32 @@ nsCacheProfilePrefObserver::ReadPrefs(nsIPrefBranch* branch)
PRBool firstSmartSizeRun;
rv = branch->GetBoolPref(DISK_CACHE_SMART_SIZE_FIRST_RUN_PREF,
&firstSmartSizeRun);
if (NS_FAILED(rv)) firstSmartSizeRun = PR_FALSE;
if (NS_FAILED(rv))
firstSmartSizeRun = PR_FALSE;
if (PermittedToSmartSize(branch, firstSmartSizeRun)) {
// Prevent unnecessary eviction before smart size event returns
// Avoid evictions: use previous cache size until smart size event
// updates mDiskCacheCapacity
if (!firstSmartSizeRun) {
PRInt32 oldSmartSize;
rv = branch->GetIntPref(DISK_CACHE_SMART_SIZE_PREF,
&oldSmartSize);
mDiskCacheCapacity = oldSmartSize;
} else {
rv = branch->SetIntPref(DISK_CACHE_CAPACITY_PREF,
MAX_SIZE / 1024);
if (NS_FAILED(rv)) NS_WARNING("Failed setting capacity pref");
PRInt32 oldCapacity;
rv = branch->GetIntPref(DISK_CACHE_CAPACITY_PREF, &oldCapacity);
if (NS_SUCCEEDED(rv)) {
mDiskCacheCapacity = oldCapacity;
} else {
mDiskCacheCapacity = DEFAULT_CACHE_SIZE;
}
}
nsAutoString cachePath;
rv = mDiskCacheParentDirectory->GetPath(cachePath);
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIRunnable> event =
new nsGetSmartSizeEvent(!!firstSmartSizeRun, cachePath);
nsCacheService::DispatchToCacheIOThread(event);
}
nsCOMPtr<nsIRunnable> event =
new nsGetSmartSizeEvent(!!firstSmartSizeRun);
rv = nsCacheService::DispatchToCacheIOThread(event);
if (NS_FAILED(rv)) mDiskCacheCapacity = BASE_LINE;
}
if (firstSmartSizeRun) {

View File

@ -53,7 +53,7 @@
class nsDiskCache {
public:
enum {
kCurrentVersion = 0x0001000E // format = 16 bits major version/16 bits minor version
kCurrentVersion = 0x0001000F // format = 16 bits major version/16 bits minor version
};
enum { kData, kMetaData };

View File

@ -55,8 +55,8 @@ class nsDiskCacheBlockFile {
public:
nsDiskCacheBlockFile()
: mFD(nsnull)
, mBlockSize(0)
, mBitMap(nsnull)
, mBlockSize(0)
, mBitMapDirty(PR_FALSE)
{}
~nsDiskCacheBlockFile() { (void) Close(PR_TRUE); }

View File

@ -64,7 +64,7 @@ ifeq ($(OS_ARCH),Darwin)
CPPSRCS += nsWifiScannerMac.cpp
CMMSRCS = osx_corewlan.mm
else
ifneq (,$(filter WINCE WINNT,$(OS_ARCH)))
ifneq (,$(filter WINNT,$(OS_ARCH)))
CPPSRCS += nsWifiScannerWin.cpp
else
ifeq ($(OS_ARCH),Linux)

View File

@ -55,505 +55,9 @@
#include "nsComponentManagerUtils.h"
#include "nsIMutableArray.h"
#ifdef WINCE
#include <Iphlpapi.h> // For GetAdaptersInfo()
#include <nuiouser.h> // For NDISUIO stuff
// GetAdaptersInfo
typedef DWORD (*GETADAPTERSINFO)(PIP_ADAPTER_INFO pAdapterInfo, PULONG pOutBufLen);
#endif
// Taken from ndis.h
#define NDIS_STATUS_INVALID_LENGTH ((NDIS_STATUS)0xC0010014L)
#define NDIS_STATUS_BUFFER_TOO_SHORT ((NDIS_STATUS)0xC0010016L)
static const int kStringLength = 512;
// The limits on the size of the buffer used for the OID query.
static const int kInitialBufferSize = 2 << 12; // Good for about 50 APs.
static const int kMaximumBufferSize = 2 << 20; // 2MB
static int oid_buffer_size_ = kStringLength;
PRBool ResizeBuffer(int requested_size, BYTE **buffer)
{
if (requested_size > kMaximumBufferSize) {
free(*buffer);
*buffer = NULL;
return false;
}
BYTE *new_buffer = (BYTE*)realloc(*buffer, requested_size);
if (new_buffer == NULL) {
free(*buffer);
*buffer = NULL;
return false;
}
*buffer = new_buffer;
return true;
}
#ifdef WINCE
int PerformQuery(HANDLE &ndis_handle,
const TCHAR *device_name,
BYTE *buffer,
DWORD buffer_size,
BYTE *&data,
DWORD *bytes_out) {
NS_ASSERTION(buffer, "Buffer is null. OOM?");
if (!buffer)
return ERROR_INSUFFICIENT_BUFFER;
// Form the query parameters.
NDISUIO_QUERY_OID *query = (NDISUIO_QUERY_OID*)(buffer);
query->ptcDeviceName = const_cast<PTCHAR>(device_name);
query->Oid = OID_802_11_BSSID_LIST;
if (!DeviceIoControl(ndis_handle,
IOCTL_NDISUIO_QUERY_OID_VALUE,
query,
sizeof(NDISUIO_QUERY_OID),
query,
buffer_size,
bytes_out,
NULL)) {
return GetLastError();
}
// The start of the NDIS_802_11_BSSID_LIST is at Data[0]
data = &query->Data[0];
return ERROR_SUCCESS;
}
void GetNetworkInterfaces(GETADAPTERSINFO pGetAdaptersInfo, nsStringArray& interfaces){
// Get the list of adapters. First determine the buffer size.
ULONG buffer_size = 0;
// since buffer_size is zero before this, we should get ERROR_BUFFER_OVERFLOW
// after this error the value of buffer_size will reflect the size needed
if (pGetAdaptersInfo(NULL, &buffer_size) != ERROR_BUFFER_OVERFLOW)
return;
// Allocate adapter_info with correct size.
IP_ADAPTER_INFO *adapter_info = (IP_ADAPTER_INFO*)malloc(buffer_size);
if (adapter_info == NULL){
free (adapter_info);
return;
}
if (pGetAdaptersInfo(adapter_info, &buffer_size) != ERROR_SUCCESS){
free (adapter_info);
return;
}
// Walk through the list of adapters.
while (adapter_info) {
// AdapterName e.g. TNETW12511
nsString adapterName;
// AdapterName is in ASCII
adapterName.AppendWithConversion(adapter_info->AdapterName);
interfaces.AppendString(adapterName);
adapter_info = adapter_info->Next;
}
free (adapter_info);
}
nsresult SetupWince(HANDLE& ndis_handle, GETADAPTERSINFO& pGetAdaptersInfo){
// Get the Network Driver Interface Specification (NDIS) handle for wireless
// devices. NDIS defines a standard API for Network Interface Cards (NICs).
// NDIS User Mode I/O (NDISUIO) is an NDIS protocol driver which offers
// support for wireless devices.
ndis_handle = CreateFile(NDISUIO_DEVICE_NAME, GENERIC_READ,
FILE_SHARE_READ, NULL, OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY, NULL);
if (INVALID_HANDLE_VALUE == ndis_handle)
return NS_ERROR_FAILURE;
HINSTANCE hIpDLL = LoadLibraryW(L"Iphlpapi.dll");
if (!hIpDLL)
return NS_ERROR_NOT_AVAILABLE;
pGetAdaptersInfo = (GETADAPTERSINFO) GetProcAddress(hIpDLL, "GetAdaptersInfo");
if (!pGetAdaptersInfo)
return NS_ERROR_FAILURE;
return NS_OK;
}
#else
int PerformQuery(HANDLE adapter_handle,
BYTE *buffer,
DWORD buffer_size,
DWORD *bytes_out) {
DWORD oid = OID_802_11_BSSID_LIST;
if (!DeviceIoControl(adapter_handle,
IOCTL_NDIS_QUERY_GLOBAL_STATS,
&oid,
sizeof(oid),
buffer,
buffer_size,
bytes_out,
NULL)) {
return GetLastError();
}
return ERROR_SUCCESS;
}
HANDLE GetFileHandle(const PRUnichar* device_name) {
// We access a device with DOS path \Device\<device_name> at
// \\.\<device_name>.
nsString formatted_device_name;
formatted_device_name.Assign(L"\\\\.\\");
formatted_device_name.Append(device_name);
return CreateFileW(formatted_device_name.get(),
GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE, // share mode
0, // security attributes
OPEN_EXISTING,
0, // flags and attributes
INVALID_HANDLE_VALUE);
}
bool UndefineDosDevice(const PRUnichar* device_name) {
// We remove only the mapping we use, that is \Device\<device_name>.
nsString target_path;
target_path.Assign(L"\\Device\\");
target_path.Append(device_name);
return DefineDosDeviceW(DDD_RAW_TARGET_PATH | DDD_REMOVE_DEFINITION | DDD_EXACT_MATCH_ON_REMOVE,
device_name,
target_path.get()) == TRUE;
}
bool DefineDosDeviceIfNotExists(const PRUnichar* device_name, bool* dosDeviceDefined) {
// We create a DOS device name for the device at \Device\<device_name>.
nsString target_path;
target_path.Assign(L"\\Device\\");
target_path.Append(device_name);
WCHAR target[kStringLength];
if (QueryDosDeviceW(device_name, target, kStringLength) > 0 && target_path.Equals(target)) {
// Device already exists.
return true;
}
DWORD error = GetLastError();
if (error != ERROR_FILE_NOT_FOUND) {
return false;
}
if (!DefineDosDeviceW(DDD_RAW_TARGET_PATH,
device_name,
target_path.get())) {
return false;
}
*dosDeviceDefined = true;
// Check that the device is really there.
return QueryDosDeviceW(device_name, target, kStringLength) > 0 &&
target_path.Equals(target);
}
void GetNetworkInterfaces(nsStringArray& interfaces)
{
HKEY network_cards_key = NULL;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE,
L"Software\\Microsoft\\Windows NT\\CurrentVersion\\NetworkCards",
0,
KEY_READ,
&network_cards_key) != ERROR_SUCCESS)
{
return;
}
for (int i = 0; ; ++i) {
WCHAR name[kStringLength];
DWORD name_size = kStringLength;
FILETIME time;
if (RegEnumKeyExW(network_cards_key,
i,
name,
&name_size,
NULL,
NULL,
NULL,
&time) != ERROR_SUCCESS)
{
break;
}
HKEY hardware_key = NULL;
if (RegOpenKeyExW(network_cards_key,
name,
0,
KEY_READ,
&hardware_key) != ERROR_SUCCESS)
{
break;
}
PRUnichar service_name[kStringLength];
DWORD service_name_size = kStringLength;
DWORD type = 0;
if (RegQueryValueExW(hardware_key,
L"ServiceName",
NULL,
&type,
(LPBYTE)service_name,
&service_name_size) == ERROR_SUCCESS) {
interfaces.AppendString(nsString(service_name)); // is this allowed?
}
RegCloseKey(hardware_key);
}
RegCloseKey(network_cards_key);
}
PRBool IsRunningOnVista() {
static DWORD os_major_version = 0;
static DWORD platform_id = 0;
if (0 == os_major_version) {
OSVERSIONINFO os_version = {0};
os_version.dwOSVersionInfoSize = sizeof(os_version);
GetVersionEx(&os_version);
os_major_version = os_version.dwMajorVersion;
platform_id = os_version.dwPlatformId;
}
return (6 == os_major_version) && (VER_PLATFORM_WIN32_NT == platform_id);
}
#endif
nsresult
nsWifiMonitor::DoScan()
{
#ifndef WINCE
if (!IsRunningOnVista()) {
#else
HANDLE ndis_handle;
GETADAPTERSINFO pGetAdaptersInfo;
nsresult rc = SetupWince(ndis_handle, pGetAdaptersInfo);
if (rc != NS_OK)
return rc;
#endif
nsCOMArray<nsWifiAccessPoint> lastAccessPoints;
nsCOMArray<nsWifiAccessPoint> accessPoints;
do {
accessPoints.Clear();
nsStringArray interfaces;
#ifdef WINCE
GetNetworkInterfaces(pGetAdaptersInfo, interfaces);
#else
GetNetworkInterfaces(interfaces);
#endif
for (int i = 0; i < interfaces.Count(); i++) {
nsString *s = interfaces.StringAt(i);
const PRUnichar *service_name = s->get();
#ifndef WINCE
bool dosDeviceDefined = false;
if (!DefineDosDeviceIfNotExists(service_name, &dosDeviceDefined))
continue;
// Get the handle to the device. This will fail if the named device is not
// valid.
HANDLE adapter_handle = GetFileHandle(service_name);
if (adapter_handle == INVALID_HANDLE_VALUE)
continue;
#else
BYTE *data; // will store address of NDIS_802_11_BSSID_LIST data
#endif
// Get the data.
BYTE *buffer = (BYTE*)malloc(oid_buffer_size_);
if (buffer == NULL) {
#ifdef WINCE
CloseHandle(ndis_handle);
#endif
return NS_ERROR_OUT_OF_MEMORY;
}
DWORD bytes_out;
int result;
while (true) {
NS_ASSERTION(buffer && oid_buffer_size_ > 0, "buffer must not be null, and the size must be larger than 0");
bytes_out = 0;
#ifdef WINCE
result = PerformQuery(ndis_handle, service_name, buffer, oid_buffer_size_, data, &bytes_out);
#else
result = PerformQuery(adapter_handle, buffer, oid_buffer_size_, &bytes_out);
#endif
if (result == ERROR_GEN_FAILURE || // Returned by some Intel cards.
result == ERROR_INVALID_USER_BUFFER || // Returned on the Samsung Omnia II.
result == ERROR_INSUFFICIENT_BUFFER ||
result == ERROR_MORE_DATA ||
result == NDIS_STATUS_INVALID_LENGTH ||
result == NDIS_STATUS_BUFFER_TOO_SHORT) {
// The buffer we supplied is too small, so increase it. bytes_out should
// provide the required buffer size, but this is not always the case.
if (bytes_out > static_cast<DWORD>(oid_buffer_size_)) {
oid_buffer_size_ = bytes_out;
} else {
oid_buffer_size_ *= 2;
}
if (!ResizeBuffer(oid_buffer_size_, &buffer)) {
oid_buffer_size_ = kInitialBufferSize; // Reset for next time.
continue;
}
} else {
// The buffer is not too small.
break;
}
}
if (result == ERROR_SUCCESS) {
#ifdef WINCE
NDIS_802_11_BSSID_LIST* bssid_list = (NDIS_802_11_BSSID_LIST*)data;
#else
NDIS_802_11_BSSID_LIST* bssid_list = (NDIS_802_11_BSSID_LIST*)buffer;
#endif
// Walk through the BSS IDs.
const uint8 *iterator = (const uint8*)&bssid_list->Bssid[0];
const uint8 *end_of_buffer = (const uint8*)buffer + oid_buffer_size_;
for (int i = 0; i < static_cast<int>(bssid_list->NumberOfItems); ++i) {
const NDIS_WLAN_BSSID *bss_id = (const NDIS_WLAN_BSSID*)iterator;
// Check that the length of this BSS ID is reasonable.
if (bss_id->Length < sizeof(NDIS_WLAN_BSSID) ||
iterator + bss_id->Length > end_of_buffer) {
break;
}
nsWifiAccessPoint* ap = new nsWifiAccessPoint();
if (!ap)
continue;
ap->setMac(bss_id->MacAddress);
ap->setSignal(bss_id->Rssi);
ap->setSSID((char*) bss_id->Ssid.Ssid, bss_id->Ssid.SsidLength);
accessPoints.AppendObject(ap);
// Move to the next BSS ID.
iterator += bss_id->Length;
}
free(buffer);
// Clean up.
#ifndef WINCE
CloseHandle(adapter_handle);
#endif
}
#ifndef WINCE
if (dosDeviceDefined)
UndefineDosDevice(service_name);
#endif
}
PRBool accessPointsChanged = !AccessPointsEqual(accessPoints, lastAccessPoints);
nsCOMArray<nsIWifiListener> currentListeners;
{
nsAutoMonitor mon(mMonitor);
for (PRUint32 i = 0; i < mListeners.Length(); i++) {
if (!mListeners[i].mHasSentData || accessPointsChanged) {
mListeners[i].mHasSentData = PR_TRUE;
currentListeners.AppendObject(mListeners[i].mListener);
}
}
}
ReplaceArray(lastAccessPoints, accessPoints);
if (currentListeners.Count() > 0) {
PRUint32 resultCount = lastAccessPoints.Count();
nsIWifiAccessPoint** result = static_cast<nsIWifiAccessPoint**> (nsMemory::Alloc(sizeof(nsIWifiAccessPoint*) * resultCount));
if (!result) {
#ifdef WINCE
CloseHandle(ndis_handle);
#endif
return NS_ERROR_OUT_OF_MEMORY;
}
for (PRUint32 i = 0; i < resultCount; i++)
result[i] = lastAccessPoints[i];
for (PRInt32 i = 0; i < currentListeners.Count(); i++) {
LOG(("About to send data to the wifi listeners\n"));
nsCOMPtr<nsIWifiListener> proxy;
nsCOMPtr<nsIProxyObjectManager> proxyObjMgr = do_GetService("@mozilla.org/xpcomproxy;1");
proxyObjMgr->GetProxyForObject(NS_PROXY_TO_MAIN_THREAD,
NS_GET_IID(nsIWifiListener),
currentListeners[i],
NS_PROXY_SYNC | NS_PROXY_ALWAYS,
getter_AddRefs(proxy));
if (!proxy) {
LOG(("There is no proxy available. this should never happen\n"));
}
else
{
nsresult rv = proxy->OnChange(result, resultCount);
LOG( ("... sent %d\n", rv));
}
}
nsMemory::Free(result);
}
// wait for some reasonable amount of time. pref?
LOG(("waiting on monitor\n"));
nsAutoMonitor mon(mMonitor);
mon.Wait(PR_SecondsToInterval(60));
}
while (mKeepGoing);
#ifdef WINCE
//Clean up
CloseHandle(ndis_handle);
#else
}
else {
HINSTANCE wlan_library = LoadLibrary("Wlanapi.dll");
if (!wlan_library)
return NS_ERROR_NOT_AVAILABLE;
@ -701,7 +205,6 @@ nsWifiMonitor::DoScan()
mon.Wait(PR_SecondsToInterval(60));
}
while (mKeepGoing);
}
#endif
return NS_OK;
return NS_OK;
}

View File

@ -275,7 +275,9 @@ function extractJarToTmp(jar) {
.getService(Components.interfaces.nsIProperties)
.get("ProfD", Components.interfaces.nsILocalFile);
tmpdir.append("mochikit.tmp");
tmpdir.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
// parseInt is used because octal escape sequences cause deprecation warnings
// in strict mode (which is turned on in debug builds)
tmpdir.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8));
var zReader = Components.classes["@mozilla.org/libjar/zip-reader;1"].
createInstance(Components.interfaces.nsIZipReader);
@ -301,7 +303,9 @@ function extractJarToTmp(jar) {
var dirs = zReader.findEntries(filepath + '*/');
while (dirs.hasMore()) {
var targetDir = buildRelativePath(dirs.getNext(), tmpdir, filepath);
targetDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777);
// parseInt is used because octal escape sequences cause deprecation warnings
// in strict mode (which is turned on in debug builds)
targetDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8));
}
//now do the files

View File

@ -125,7 +125,10 @@ class RemoteOptions(MochitestOptions):
return None
if (options.remoteLogFile == None):
options.remoteLogFile = automation._devicemanager.getDeviceRoot() + '/test.log'
options.remoteLogFile = automation._devicemanager.getDeviceRoot() + '/test.log'
if (options.remoteLogFile.count('/') < 1):
options.remoteLogFile = automation._devicemanager.getDeviceRoot() + '/' + options.remoteLogFile
# Set up our options that we depend on based on the above
productRoot = options.remoteTestRoot + "/" + automation._product
@ -286,6 +289,7 @@ def main():
if (options == None):
sys.exit(1)
auto.setRemoteLog(options.remoteLogFile)
auto.setServerInfo(options.webServer, options.httpPort, options.sslPort)
sys.exit(mochitest.runTests(options))

View File

@ -50,7 +50,6 @@ include $(DEPTH)/config/autoconf.mk
include $(topsrcdir)/config/rules.mk
_STUBS_FILES = \
SimpleTest.js \
test.css \
$(NULL)

View File

@ -5,7 +5,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

View File

@ -4,7 +4,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

View File

@ -6,7 +6,7 @@
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="../MochiKit/Logging.js"></script>
<script type="text/javascript" src="../MochiKit/Color.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
<style type="text/css">.redtext {color: red}</style>
</head>

View File

@ -5,7 +5,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

View File

@ -5,7 +5,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

View File

@ -5,7 +5,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

View File

@ -14,7 +14,7 @@
<script type="text/javascript" src="../MochiKit/Position.js"></script>
<script type="text/javascript" src="../MochiKit/Visual.js"></script>
<script type="text/javascript" src="../MochiKit/DragAndDrop.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
<style type="text/css">
.drop-hover {

View File

@ -5,7 +5,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

View File

@ -4,7 +4,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

View File

@ -5,7 +5,7 @@
<script type="text/javascript" src="../MochiKit/Iter.js"></script>
<script type="text/javascript" src="../MochiKit/DOM.js"></script>
<script type="text/javascript" src="../MochiKit/Style.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>

View File

@ -1,7 +1,7 @@
<html>
<head>
<script type="text/javascript" src="../MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="SimpleTest/test.css">
</head>
<body>

Some files were not shown because too many files have changed in this diff Show More