mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Merge mozilla-central to tracemonkey.
This commit is contained in:
commit
faaf151e3a
@ -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);
|
||||
|
@ -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()"/>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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).
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -59,6 +59,11 @@ public:
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
||||
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
||||
|
||||
nsScriptElement(PRUint32 aFromParser)
|
||||
: nsIScriptElement(aFromParser)
|
||||
{
|
||||
}
|
||||
|
||||
protected:
|
||||
// Internal methods
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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__
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
32
content/test/reftest/bug591981-1.html
Normal file
32
content/test/reftest/bug591981-1.html
Normal 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>
|
32
content/test/reftest/bug591981-2.html
Normal file
32
content/test/reftest/bug591981-2.html
Normal 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>
|
9
content/test/reftest/bug591981-ref.html
Normal file
9
content/test/reftest/bug591981-ref.html
Normal 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>
|
1
content/test/reftest/bug591981-script.js
Normal file
1
content/test/reftest/bug591981-script.js
Normal file
@ -0,0 +1 @@
|
||||
log("external");
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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 },
|
||||
|
@ -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)
|
||||
|
@ -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];
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -75,7 +75,7 @@ child:
|
||||
|
||||
async NotifyVisited(URI uri);
|
||||
|
||||
NotifyRemotePrefObserver(nsCString aDomain);
|
||||
PreferenceUpdate(nsCString pref);
|
||||
|
||||
NotifyAlertsObserver(nsCString topic, nsString data);
|
||||
|
||||
|
@ -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
|
||||
|
@ -97,6 +97,7 @@ class nsGeolocationRequest
|
||||
|
||||
void SendLocation(nsIDOMGeoPosition* location);
|
||||
void MarkCleared();
|
||||
PRBool IsActive() {return !mCleared;}
|
||||
PRBool Allowed() {return mAllowed;}
|
||||
void SetTimeoutTimer();
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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|.
|
||||
*/
|
||||
|
41
dom/tests/unit/test_geolocation_provider.js
Normal file
41
dom/tests/unit/test_geolocation_provider.js
Normal 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);})
|
||||
}
|
||||
|
@ -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" />
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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 \
|
||||
> $@
|
||||
|
4
embedding/android/android_strings.dtd
Normal file
4
embedding/android/android_strings.dtd
Normal 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;">
|
12
embedding/android/strings.xml.in
Normal file
12
embedding/android/strings.xml.in
Normal 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>
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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()) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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++
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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));
|
||||
|
@ -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);
|
||||
|
21
modules/libpref/test/unit_ipc/test_existing_prefs.js
Normal file
21
modules/libpref/test/unit_ipc/test_existing_prefs.js
Normal 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");
|
||||
}
|
||||
}
|
18
modules/libpref/test/unit_ipc/test_initial_prefs.js
Normal file
18
modules/libpref/test/unit_ipc/test_initial_prefs.js
Normal 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");
|
||||
}
|
||||
}
|
16
modules/libpref/test/unit_ipc/test_observed_prefs.js
Normal file
16
modules/libpref/test/unit_ipc/test_observed_prefs.js
Normal 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");
|
||||
}
|
||||
}
|
@ -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 {
|
||||
}
|
||||
}
|
25
modules/libpref/test/unit_ipc/test_update_prefs.js
Normal file
25
modules/libpref/test/unit_ipc/test_update_prefs.js
Normal 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 {
|
||||
}
|
||||
}
|
181
netwerk/cache/nsCacheService.cpp
vendored
181
netwerk/cache/nsCacheService.cpp
vendored
@ -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) {
|
||||
|
2
netwerk/cache/nsDiskCache.h
vendored
2
netwerk/cache/nsDiskCache.h
vendored
@ -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 };
|
||||
|
2
netwerk/cache/nsDiskCacheBlockFile.h
vendored
2
netwerk/cache/nsDiskCacheBlockFile.h
vendored
@ -55,8 +55,8 @@ class nsDiskCacheBlockFile {
|
||||
public:
|
||||
nsDiskCacheBlockFile()
|
||||
: mFD(nsnull)
|
||||
, mBlockSize(0)
|
||||
, mBitMap(nsnull)
|
||||
, mBlockSize(0)
|
||||
, mBitMapDirty(PR_FALSE)
|
||||
{}
|
||||
~nsDiskCacheBlockFile() { (void) Close(PR_TRUE); }
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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))
|
||||
|
||||
|
@ -50,7 +50,6 @@ include $(DEPTH)/config/autoconf.mk
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_STUBS_FILES = \
|
||||
SimpleTest.js \
|
||||
test.css \
|
||||
$(NULL)
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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 {
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user