merge fx-team to m-c

This commit is contained in:
Wes Kocher 2014-05-05 20:52:49 -07:00
commit db9e1a87e5
56 changed files with 809 additions and 412 deletions

View File

@ -344,6 +344,14 @@ function showManage() {
document.addEventListener("DOMContentLoaded", function onload() {
document.removeEventListener("DOMContentLoaded", onload, true);
init();
var buttonGetStarted = document.getElementById('buttonGetStarted');
buttonGetStarted.addEventListener('click', getStarted);
var oldsync = document.getElementById('oldsync');
oldsync.addEventListener('click', handleOldSync);
var buttonOpenPrefs = document.getElementById('buttonOpenPrefs')
buttonOpenPrefs.addEventListener('click', openPrefs);
}, true);
function initObservers() {

View File

@ -46,7 +46,7 @@
<div class="graphic graphic-sync-intro"> </div>
<div class="button-row">
<a class="button" href="#" onclick="openPrefs()">&aboutAccountsConfig.manage.label;</a>
<a id="buttonOpenPrefs" class="button" href="#">&aboutAccountsConfig.manage.label;</a>
</div>
</section>
</div>
@ -62,11 +62,11 @@
<div class="description">&aboutAccountsConfig.description;</div>
<div class="button-row">
<a class="button" href="#" onclick="getStarted()">&aboutAccountsConfig.startButton.label;</a>
<a id="buttonGetStarted" class="button" href="#">&aboutAccountsConfig.startButton.label;</a>
</div>
<div class="links">
<a id="oldsync" class="no-underline" href="#" onclick="handleOldSync();">&aboutAccountsConfig.useOldSync.label;</a>
<a id="oldsync" class="no-underline" href="#">&aboutAccountsConfig.useOldSync.label;</a>
</div>
</section>
</div>

View File

@ -863,9 +863,11 @@ var gBrowserInit = {
}
}
// Certain kinds of automigration rely on this notification to complete their
// tasks BEFORE the browser window is shown.
Services.obs.notifyObservers(null, "browser-window-before-show", "");
// Certain kinds of automigration rely on this notification to complete
// their tasks BEFORE the browser window is shown. SessionStore uses it to
// restore tabs into windows AFTER important parts like gMultiProcessBrowser
// have been initialized.
Services.obs.notifyObservers(window, "browser-window-before-show", "");
// Set a sane starting width/height for all resolutions on new profiles.
if (!document.documentElement.hasAttribute("width")) {

View File

@ -1407,19 +1407,6 @@ BrowserGlue.prototype = {
}
}
if (currentUIVersion < 6) {
// convert tabsontop attribute to pref
let toolboxResource = this._rdf.GetResource(BROWSER_DOCURL + "navigator-toolbox");
let tabsOnTopResource = this._rdf.GetResource("tabsontop");
let tabsOnTopAttribute = this._getPersist(toolboxResource, tabsOnTopResource);
if (tabsOnTopAttribute)
Services.prefs.setBoolPref("browser.tabs.onTop", tabsOnTopAttribute == "true");
}
// Migration at version 7 only occurred for users who wanted to try the new
// Downloads Panel feature before its release. Since migration at version
// 9 adds the button by default, this step has been removed.
if (currentUIVersion < 8) {
// Reset homepage pref for users who have it set to google.com/firefox
let uri = Services.prefs.getComplexValue("browser.startup.homepage",
@ -1512,8 +1499,6 @@ BrowserGlue.prototype = {
OS.File.remove(path);
}
// Version 15 was obsoleted in favour of 18.
if (currentUIVersion < 16) {
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let collapsedResource = this._rdf.GetResource("collapsed");

View File

@ -30,7 +30,7 @@ const MAX_CONCURRENT_TAB_RESTORES = 3;
// global notifications observed
const OBSERVING = [
"domwindowopened", "domwindowclosed",
"browser-window-before-show", "domwindowclosed",
"quit-application-requested", "quit-application-granted",
"browser-lastwindow-close-granted",
"quit-application", "browser:purge-session-history",
@ -540,8 +540,8 @@ let SessionStoreInternal = {
*/
observe: function ssi_observe(aSubject, aTopic, aData) {
switch (aTopic) {
case "domwindowopened": // catch new windows
this.onOpen(aSubject);
case "browser-window-before-show": // catch new windows
this.onBeforeBrowserWindowShown(aSubject);
break;
case "domwindowclosed": // catch closed windows
this.onClose(aSubject);
@ -919,71 +919,59 @@ let SessionStoreInternal = {
},
/**
* On window open
* Called right before a new browser window is shown.
* @param aWindow
* Window reference
*/
onOpen: function ssi_onOpen(aWindow) {
let onload = () => {
aWindow.removeEventListener("load", onload);
onBeforeBrowserWindowShown: function (aWindow) {
// Just call onLoad() directly if we're initialized already.
if (this._sessionInitialized) {
this.onLoad(aWindow);
return;
}
let windowType = aWindow.document.documentElement.getAttribute("windowtype");
// The very first window that is opened creates a promise that is then
// re-used by all subsequent windows. The promise will be used to tell
// when we're ready for initialization.
if (!this._promiseReadyForInitialization) {
let deferred = Promise.defer();
// Ignore non-browser windows.
if (windowType != "navigator:browser") {
// Wait for the given window's delayed startup to be finished.
Services.obs.addObserver(function obs(subject, topic) {
if (aWindow == subject) {
Services.obs.removeObserver(obs, topic);
deferred.resolve();
}
}, "browser-delayed-startup-finished", false);
// We are ready for initialization as soon as the session file has been
// read from disk and the initial window's delayed startup has finished.
this._promiseReadyForInitialization =
Promise.all([deferred.promise, gSessionStartup.onceInitialized]);
}
// We can't call this.onLoad since initialization
// hasn't completed, so we'll wait until it is done.
// Even if additional windows are opened and wait
// for initialization as well, the first opened
// window should execute first, and this.onLoad
// will be called with the initialState.
this._promiseReadyForInitialization.then(() => {
if (aWindow.closed) {
return;
}
if (this._sessionInitialized) {
this.onLoad(aWindow);
return;
} else {
let initialState = this.initSession();
this._sessionInitialized = true;
this.onLoad(aWindow, initialState);
// Let everyone know we're done.
this._deferredInitialized.resolve();
}
// The very first window that is opened creates a promise that is then
// re-used by all subsequent windows. The promise will be used to tell
// when we're ready for initialization.
if (!this._promiseReadyForInitialization) {
let deferred = Promise.defer();
// Wait for the given window's delayed startup to be finished.
Services.obs.addObserver(function obs(subject, topic) {
if (aWindow == subject) {
Services.obs.removeObserver(obs, topic);
deferred.resolve();
}
}, "browser-delayed-startup-finished", false);
// We are ready for initialization as soon as the session file has been
// read from disk and the initial window's delayed startup has finished.
this._promiseReadyForInitialization =
Promise.all([deferred.promise, gSessionStartup.onceInitialized]);
}
// We can't call this.onLoad since initialization
// hasn't completed, so we'll wait until it is done.
// Even if additional windows are opened and wait
// for initialization as well, the first opened
// window should execute first, and this.onLoad
// will be called with the initialState.
this._promiseReadyForInitialization.then(() => {
if (aWindow.closed) {
return;
}
if (this._sessionInitialized) {
this.onLoad(aWindow);
} else {
let initialState = this.initSession();
this._sessionInitialized = true;
this.onLoad(aWindow, initialState);
// Let everyone know we're done.
this._deferredInitialized.resolve();
}
}, console.error);
};
aWindow.addEventListener("load", onload);
}, console.error);
},
/**

View File

@ -31,9 +31,7 @@ function test() {
// open a window and add the above closed tab list
let newWin = openDialog(location, "", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo",
test_state.windows[0]._closedTabs.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
@ -71,5 +69,5 @@ function test() {
newWin.close();
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
finish();
}, false);
});
}

View File

@ -52,9 +52,7 @@ function test() {
// open a window and add the above closed tab list
let newWin = openDialog(location, "", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
gPrefService.setIntPref("browser.sessionstore.max_tabs_undo",
test_state.windows[0]._closedTabs.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
@ -83,5 +81,5 @@ function test() {
gPrefService.clearUserPref("browser.sessionstore.max_tabs_undo");
finish();
});
}, false);
});
}

View File

@ -15,9 +15,7 @@ function test() {
// open a window and set a value on it
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
ss.setWindowValue(newWin, uniqueKey1, uniqueValue1);
let newState = { windows: [{ tabs:[{ entries: [] }], extData: {} }] };
@ -44,5 +42,5 @@ function test() {
// clean up
newWin.close();
finish();
}, false);
});
}

View File

@ -7,9 +7,7 @@ function test() {
waitForExplicitFinish();
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
let newState = { windows: [{
tabs: [{ entries: [] }],
_closedTabs: [{
@ -59,5 +57,5 @@ function test() {
}, 0);
}, 0);
}, 0);
}, false);
});
}

View File

@ -80,8 +80,7 @@ function test() {
// open a window and add the above closed window list
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
gPrefService.setIntPref("browser.sessionstore.max_windows_undo",
test_state._closedWindows.length);
ss.setWindowState(newWin, JSON.stringify(test_state), true);
@ -117,5 +116,5 @@ function test() {
newWin.close();
gPrefService.clearUserPref("browser.sessionstore.max_windows_undo");
finish();
}, false);
});
}

View File

@ -8,47 +8,38 @@ function test() {
waitForExplicitFinish();
let newWin = openDialog(location, "_blank", "chrome,all,dialog=no,toolbar=yes");
newWin.addEventListener("load", function() {
newWin.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(newWin).then(() => {
let state1 = ss.getWindowState(newWin);
newWin.close();
executeSoon(function() {
let state1 = ss.getWindowState(newWin);
newWin = openDialog(location, "_blank",
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
promiseWindowLoaded(newWin).then(() => {
let state2 = ss.getWindowState(newWin);
newWin.close();
newWin = openDialog(location, "_blank",
"chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar=no,location,personal,directories,dialog=no");
newWin.addEventListener("load", function() {
newWin.removeEventListener("load", arguments.callee, false);
function testState(state, expected, callback) {
let win = openDialog(location, "_blank", "chrome,all,dialog=no");
promiseWindowLoaded(win).then(() => {
executeSoon(function() {
let state2 = ss.getWindowState(newWin);
newWin.close();
is(win.gURLBar.readOnly, false,
"URL bar should not be read-only before setting the state");
is(win.gURLBar.getAttribute("enablehistory"), "true",
"URL bar autocomplete should be enabled before setting the state");
ss.setWindowState(win, state, true);
is(win.gURLBar.readOnly, expected.readOnly,
"URL bar read-only state should be restored correctly");
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
"URL bar autocomplete state should be restored correctly");
function testState(state, expected, callback) {
let win = openDialog(location, "_blank", "chrome,all,dialog=no");
win.addEventListener("load", function() {
win.removeEventListener("load", arguments.callee, false);
is(win.gURLBar.readOnly, false,
"URL bar should not be read-only before setting the state");
is(win.gURLBar.getAttribute("enablehistory"), "true",
"URL bar autocomplete should be enabled before setting the state");
ss.setWindowState(win, state, true);
is(win.gURLBar.readOnly, expected.readOnly,
"URL bar read-only state should be restored correctly");
is(win.gURLBar.getAttribute("enablehistory"), expected.enablehistory,
"URL bar autocomplete state should be restored correctly");
win.close();
executeSoon(callback);
}, false);
}
testState(state1, {readOnly: false, enablehistory: "true"}, function() {
testState(state2, {readOnly: true, enablehistory: "false"}, finish);
});
win.close();
executeSoon(callback);
});
}, false);
}
testState(state1, {readOnly: false, enablehistory: "true"}, function() {
testState(state2, {readOnly: true, enablehistory: "false"}, finish);
});
});
}, false);
});
}

View File

@ -17,11 +17,10 @@ function test() {
browserWindowsCount(1);
var win = openDialog(location, "", "chrome,all,dialog=no");
win.addEventListener("load", function () {
win.removeEventListener("load", arguments.callee, false);
promiseWindowLoaded(win).then(() => {
browserWindowsCount(2);
win.close();
browserWindowsCount(1);
finish();
}, false);
});
}

View File

@ -10,9 +10,7 @@ function test() {
// open a new window and setup the window state.
newWin = openDialog(getBrowserURL(), "_blank", "chrome,all,dialog=no");
newWin.addEventListener("load", function onLoad(event) {
this.removeEventListener("load", onLoad, false);
whenWindowLoaded(newWin, function () {
let newState = {
windows: [{
tabs: [{
@ -69,5 +67,5 @@ function test() {
}
newWin.addEventListener("tabviewshown", onTabViewShow, false);
waitForFocus(function() { newWin.TabView.toggle(); });
}, false);
});
}

View File

@ -142,8 +142,10 @@ function afterAllTabsLoaded(callback, win) {
browser.__SS_restoreState &&
browser.__SS_restoreState == TAB_STATE_NEEDS_RESTORE);
if (isRestorable && browser.contentDocument.readyState != "complete" ||
browser.webProgress.isLoadingDocument) {
let isLoading = browser.webProgress.isLoadingDocument ||
browser.contentDocument.readyState != "complete";
if (isRestorable && isLoading) {
stillToLoad++;
browser.addEventListener("load", onLoad, true);
}

View File

@ -9,7 +9,7 @@ this.EXPORTED_SYMBOLS = ["Windows8WindowFrameColor"];
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/WindowsRegistry.jsm");
let Registry = Cu.import("resource://gre/modules/WindowsRegistry.jsm").WindowsRegistry;
const Windows8WindowFrameColor = {
_windowFrameColor: null,
@ -18,16 +18,26 @@ const Windows8WindowFrameColor = {
if (this._windowFrameColor)
return this._windowFrameColor;
let windowFrameColor = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"Software\\Microsoft\\Windows\\DWM",
"ColorizationColor");
const HKCU = Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER;
const dwmKey = "Software\\Microsoft\\Windows\\DWM";
let customizationColor = Registry.readRegKey(HKCU, dwmKey,
"ColorizationColor");
// The color returned from the Registry is in decimal form.
let windowFrameColorHex = windowFrameColor.toString(16);
let customizationColorHex = customizationColor.toString(16);
// Zero-pad the number just to make sure that it is 8 digits.
windowFrameColorHex = ("00000000" + windowFrameColorHex).substr(-8);
let windowFrameColorArray = windowFrameColorHex.match(/../g);
let [pixelA, pixelR, pixelG, pixelB] = windowFrameColorArray.map(function(val) parseInt(val, 16));
customizationColorHex = ("00000000" + customizationColorHex).substr(-8);
let customizationColorArray = customizationColorHex.match(/../g);
let [unused, fgR, fgG, fgB] = customizationColorArray.map(function(val) parseInt(val, 16));
let colorizationColorBalance = Registry.readRegKey(HKCU, dwmKey,
"ColorizationColorBalance");
// Window frame base color when Color Intensity is at 0, see bug 1004576.
let frameBaseColor = 217;
let alpha = colorizationColorBalance / 100;
return this._windowFrameColor = [pixelR, pixelG, pixelB];
// Alpha-blend the foreground color with the frame base color.
let r = Math.round(fgR * alpha + frameBaseColor * (1 - alpha));
let g = Math.round(fgG * alpha + frameBaseColor * (1 - alpha));
let b = Math.round(fgB * alpha + frameBaseColor * (1 - alpha));
return this._windowFrameColor = [r, g, b];
},
};

View File

@ -202,6 +202,7 @@ browser.jar:
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
skin/classic/browser/devtools/command-eyedropper@2x.png (../shared/devtools/images/command-eyedropper@2x.png)
skin/classic/browser/devtools/alerticon-warning.png (../shared/devtools/images/alerticon-warning.png)
skin/classic/browser/devtools/alerticon-warning@2x.png (../shared/devtools/images/alerticon-warning@2x.png)
* skin/classic/browser/devtools/ruleview.css (../shared/devtools/ruleview.css)
* skin/classic/browser/devtools/webconsole.css (devtools/webconsole.css)
skin/classic/browser/devtools/webconsole_networkpanel.css (devtools/webconsole_networkpanel.css)

View File

@ -322,7 +322,8 @@ browser.jar:
skin/classic/browser/devtools/command-console@2x.png (../shared/devtools/images/command-console@2x.png)
skin/classic/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
skin/classic/browser/devtools/command-eyedropper@2x.png (../shared/devtools/images/command-eyedropper@2x.png)
skin/classic/browser/devtools/alerticon-warning.png (../shared/devtools/images/alerticon-warning.png)
skin/classic/browser/devtools/alerticon-warning.png (../shared/devtools/images/alerticon-warning.png)
skin/classic/browser/devtools/alerticon-warning@2x.png (../shared/devtools/images/alerticon-warning@2x.png)
* skin/classic/browser/devtools/ruleview.css (../shared/devtools/ruleview.css)
skin/classic/browser/devtools/commandline.css (devtools/commandline.css)
skin/classic/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

View File

@ -52,7 +52,8 @@
}
.ruleview-warning {
background: url("chrome://browser/skin/devtools/alerticon-warning.png");
background-image: url(alerticon-warning.png);
background-size: 13px 12px;
-moz-margin-start: 5px;
display: inline-block;
vertical-align: top;
@ -60,6 +61,13 @@
height: 12px;
}
@media (min-resolution: 2dppx) {
.ruleview-warning {
background-image: url(alerticon-warning@2x.png);
}
}
.ruleview-ruleopen {
-moz-padding-end: 5px;
}

View File

@ -113,8 +113,16 @@ text {
*/
.web-audio-inspector .error {
background-image: url(chrome://browser/skin/devtools/alerticon-warning.png);
background-image: url(alerticon-warning.png);
background-size: 13px 12px;
-moz-appearance: none;
opacity: 0;
transition: opacity .5s ease-out 0s;
}
@media (min-resolution: 2dppx) {
.web-audio-inspector .error {
background-image: url(alerticon-warning@2x.png);
}
}

View File

@ -225,6 +225,7 @@ browser.jar:
* skin/classic/browser/devtools/widgets.css (devtools/widgets.css)
skin/classic/browser/devtools/commandline-icon.png (../shared/devtools/images/commandline-icon.png)
skin/classic/browser/devtools/alerticon-warning.png (../shared/devtools/images/alerticon-warning.png)
skin/classic/browser/devtools/alerticon-warning@2x.png (../shared/devtools/images/alerticon-warning@2x.png)
* skin/classic/browser/devtools/ruleview.css (../shared/devtools/ruleview.css)
skin/classic/browser/devtools/commandline.css (devtools/commandline.css)
skin/classic/browser/devtools/command-paintflashing.png (../shared/devtools/images/command-paintflashing.png)
@ -602,6 +603,7 @@ browser.jar:
skin/classic/aero/browser/devtools/command-eyedropper.png (../shared/devtools/images/command-eyedropper.png)
skin/classic/aero/browser/devtools/command-eyedropper@2x.png (../shared/devtools/images/command-eyedropper@2x.png)
skin/classic/aero/browser/devtools/alerticon-warning.png (../shared/devtools/images/alerticon-warning.png)
skin/classic/aero/browser/devtools/alerticon-warning@2x.png (../shared/devtools/images/alerticon-warning@2x.png)
* skin/classic/aero/browser/devtools/ruleview.css (../shared/devtools/ruleview.css)
skin/classic/aero/browser/devtools/commandline.css (devtools/commandline.css)
skin/classic/aero/browser/devtools/markup-view.css (../shared/devtools/markup-view.css)

View File

@ -8390,6 +8390,12 @@ if test -n "$MOZ_CAPTIVEDETECT"; then
AC_DEFINE(MOZ_CAPTIVEDETECT)
fi
dnl Build second screen and casting features for external devices if required
AC_SUBST(MOZ_DEVICES)
if test -n "$MOZ_DEVICES"; then
AC_DEFINE(MOZ_DEVICES)
fi
dnl ========================================================
if test "$MOZ_DEBUG" -o "$NS_TRACE_MALLOC" -o "$MOZ_DMD"; then
MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS=

View File

@ -9614,44 +9614,46 @@
> testcase/MS
46831a52679
> testsuite/MS
46925a52774
46845a52694
> textbox/SM
46925a52775
> theremin/MS
47455c53304
47455c53305
< toolbar
---
> toolbar/MS
47755a53605
47755a53606
> transfect/DSMG
47774a53625,53626
47774a53626,53627
> transgenderism
> transgene/MS
47951c53803
47951c53804
< triage/M
---
> triage/MG
48869a54722
48869a54723
> unlikeable
49211c55064
49211c55065
< vagina/M
---
> vagina/MS
49368,49369c55221
49368,49369c55222
< velour's
< velours's
---
> velour/MS
49478a55331
49478a55332
> vertices
50148a56002
50148a56003
> weaponize/DSG
50260,50261d56113
50260,50261d56114
< werwolf/M
< werwolves
50728c56580
50728c56581
< women
---
> women/M
50794c56646
50794c56647
< wop/S!
---
> wop/MS!

View File

@ -1,4 +1,4 @@
57468
57469
0/nm
0th/pt
1/n1
@ -53044,6 +53044,7 @@ tetrahedron/SM
tetrameter/MS
text/FMS
textbook/SM
textbox/SM
textile/MS
textual/FY
textural/Y

View File

@ -75,12 +75,14 @@
android:icon="@drawable/icon"
android:name="org.mozilla.gecko.GeckoApplication"
android:hardwareAccelerated="true"
#if MOZILLA_OFFICIAL
#ifdef MOZILLA_OFFICIAL
android:debuggable="false">
#else
android:debuggable="true">
#endif
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true"/>
<!-- If the windowSoftInputMode adjust* flag changes below, the
setSoftInputMode call in BrowserSearch#onStop must also be updated. -->
<activity android:name=".App"
@ -91,10 +93,19 @@
android:windowSoftInputMode="stateUnspecified|adjustResize"
android:launchMode="singleTask"
android:theme="@style/Gecko.App">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.MULTIWINDOW_LAUNCHER"/>
</intent-filter>
<meta-data android:name="com.sec.minimode.icon.portrait.normal"
android:resource="@drawable/icon"/>
<meta-data android:name="com.sec.minimode.icon.landscape.normal"
android:resource="@drawable/icon" />
<intent-filter>
<action android:name="org.mozilla.gecko.ACTION_ALERT_CALLBACK" />
</intent-filter>

View File

@ -164,4 +164,14 @@ public class AppConstants {
#else
false;
#endif
// Official corresponds, roughly, to whether this build is performed on
// Mozilla's continuous integration infrastructure. You should disable
// developer-only functionality when this flag is set.
public static final boolean MOZILLA_OFFICIAL =
#ifdef MOZILLA_OFFICIAL
true;
#else
false;
#endif
}

View File

@ -323,6 +323,16 @@ public class GeckoAppShell
if (type != null)
combinedArgs += " " + type;
// In un-official builds, we want to load Javascript resources fresh
// with each build. In official builds, the startup cache is purged by
// the buildid mechanism, but most un-official builds don't bump the
// buildid, so we purge here instead.
if (!AppConstants.MOZILLA_OFFICIAL) {
Log.w(LOGTAG, "STARTUP PERFORMANCE WARNING: un-official build: purging the " +
"startup (JavaScript) caches.");
combinedArgs += " -purgecaches";
}
DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
combinedArgs += " -width " + metrics.widthPixels + " -height " + metrics.heightPixels;

View File

@ -21,6 +21,7 @@ import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
public final class TabsAccessor {
private static final String LOGTAG = "GeckoTabsAccessor";
@ -49,6 +50,7 @@ public final class TabsAccessor {
private static final String LOCAL_CLIENT_SELECTION = BrowserContract.Clients.GUID + " IS NULL";
private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
private static final Pattern FILTERED_URL_PATTERN = Pattern.compile("^(about|chrome|wyciwyg|file):");
public static class RemoteTab {
public String title;
@ -149,9 +151,9 @@ public final class TabsAccessor {
int position = 0;
for (Tab tab : tabs) {
// Skip this tab if it has a null URL or is in private browsing mode
// Skip this tab if it has a null URL or is in private browsing mode, or is a filtered URL.
String url = tab.getURL();
if (url == null || tab.isPrivate())
if (url == null || tab.isPrivate() || isFilteredURL(url))
continue;
ContentValues values = new ContentValues();
@ -192,4 +194,13 @@ public final class TabsAccessor {
insertLocalTabs(cr, tabs);
updateLocalClient(cr);
}
/**
* Matches the supplied URL string against the set of URLs to filter.
*
* @return true if the supplied URL should be skipped; false otherwise.
*/
private static boolean isFilteredURL(String url) {
return FILTERED_URL_PATTERN.matcher(url).lookingAt();
}
}

View File

@ -509,7 +509,7 @@ ANDROID_GENERATED_RESFILES += [
'res/values/strings.xml',
]
for var in ('MOZ_ANDROID_ANR_REPORTER', 'MOZ_LINKER_EXTRACT'):
for var in ('MOZ_ANDROID_ANR_REPORTER', 'MOZ_LINKER_EXTRACT', 'MOZILLA_OFFICIAL'):
if CONFIG[var]:
DEFINES[var] = 1
@ -518,7 +518,7 @@ for var in ('MOZ_UPDATER', 'MOZ_PKG_SPECIAL'):
DEFINES[var] = CONFIG[var]
for var in ('ANDROID_PACKAGE_NAME', 'ANDROID_CPU_ARCH', 'CPU_ARCH',
'GRE_MILESTONE', 'MOZILLA_OFFICIAL', 'MOZ_APP_BASENAME',
'GRE_MILESTONE', 'MOZ_APP_BASENAME',
'MOZ_APP_DISPLAYNAME', 'MOZ_APP_ID', 'MOZ_APP_NAME',
'MOZ_APP_VENDOR', 'MOZ_APP_VERSION', 'MOZ_CHILD_PROCESS_NAME',
'MOZ_CRASHREPORTER', 'MOZ_UPDATE_CHANNEL', 'OMNIJAR_NAME',

View File

@ -35,6 +35,7 @@ skip-if = android_version == "10" || processor == "x86"
[testDoorHanger]
# disabled on 2.3; bug 986172
skip-if = android_version == "10"
[testFilterOpenTab]
[testFindInPage]
# disabled on Android 2.3; bug 975155
skip-if = android_version == "10"

View File

@ -0,0 +1,125 @@
package org.mozilla.gecko.tests;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import org.mozilla.gecko.PrivateTab;
import org.mozilla.gecko.Tab;
import org.mozilla.gecko.TabsAccessor;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.TabsProvider;
import android.content.ContentProvider;
import android.content.Context;
import android.database.Cursor;
/**
* Tests that local tabs are filtered prior to upload.
* - create a set of tabs and perists them through TabsAccessor.
* - verifies that tabs are filtered by querying.
*/
public class testFilterOpenTab extends ContentProviderTest {
private static final String[] TABS_PROJECTION_COLUMNS = new String[] {
BrowserContract.Tabs.TITLE,
BrowserContract.Tabs.URL,
BrowserContract.Clients.GUID,
BrowserContract.Clients.NAME
};
private static final String LOCAL_TABS_SELECTION = BrowserContract.Tabs.CLIENT_GUID + " IS NULL";
/**
* Factory function that makes new ContentProvider instances.
* <p>
* We want a fresh provider each test, so this should be invoked in
* <code>setUp</code> before each individual test.
*/
protected static Callable<ContentProvider> sTabProviderCallable = new Callable<ContentProvider>() {
@Override
public ContentProvider call() {
return new TabsProvider();
}
};
private Cursor getTabsFromLocalClient() throws Exception {
return mProvider.query(BrowserContract.Tabs.CONTENT_URI,
TABS_PROJECTION_COLUMNS,
LOCAL_TABS_SELECTION,
null,
null);
}
private Tab createTab(int id, String url, boolean external, int parentId, String title) {
return new Tab((Context) getActivity(), id, url, external, parentId, title);
}
private Tab createPrivateTab(int id, String url, boolean external, int parentId, String title) {
return new PrivateTab((Context) getActivity(), id, url, external, parentId, title);
}
@Override
public void setUp() throws Exception {
super.setUp(sTabProviderCallable, BrowserContract.TABS_AUTHORITY, "tabs.db");
mTests.add(new TestInsertLocalTabs());
}
public void testFilterOpenTab() throws Exception {
for (int i = 0; i < mTests.size(); i++) {
Runnable test = mTests.get(i);
setTestName(test.getClass().getSimpleName());
test.run();
}
}
private class TestInsertLocalTabs extends TestCase {
@Override
public void test() throws Exception {
final String TITLE1 = "Google";
final String URL1 = "http://www.google.com/";
final String TITLE2 = "Mozilla Start Page";
final String URL2 = "about:home";
final String TITLE3 = "Chrome Weave URL";
final String URL3 = "chrome://weave/";
final String TITLE4 = "What You Cache Is What You Get";
final String URL4 = "wyciwyg://1/test.com";
final String TITLE5 = "Root Folder";
final String URL5 = "file:///";
// Create a list of local tabs.
List<Tab> tabs = new ArrayList<Tab>(6);
Tab tab1 = createTab(1, URL1, false, 0, TITLE1);
Tab tab2 = createTab(2, URL2, false, 0, TITLE2);
Tab tab3 = createTab(3, URL3, false, 0, TITLE3);
Tab tab4 = createTab(4, URL4, false, 0, TITLE4);
Tab tab5 = createTab(5, URL5, false, 0, TITLE5);
Tab tab6 = createPrivateTab(6, URL1, false, 0, TITLE1);
tabs.add(tab1);
tabs.add(tab2);
tabs.add(tab3);
tabs.add(tab4);
tabs.add(tab5);
tabs.add(tab6);
// Persist the created tabs.
TabsAccessor.persistLocalTabs(mResolver, tabs);
// Get the persisted tab and check if urls are filtered.
Cursor c = getTabsFromLocalClient();
assertCountIsAndClose(c, 1, 1 + " tabs entries found");
}
}
/**
* Assert that the provided cursor has the expected number of rows,
* closing the cursor afterwards.
*/
private void assertCountIsAndClose(Cursor c, int expectedCount, String message) {
try {
mAsserter.is(c.getCount(), expectedCount, message);
} finally {
c.close();
}
}
}

View File

@ -0,0 +1,93 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm"); /*global Services */
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm"); /*global SimpleServiceDiscovery */
const EVENT_SERVICE_FOUND = SimpleServiceDiscovery.EVENT_SERVICE_FOUND;
const EVENT_SERVICE_LOST = SimpleServiceDiscovery.EVENT_SERVICE_LOST;
// We want to keep this page fresh while it is open, so we decrease
// our time between searches when it is opened, and revert to the
// former time between searches when it is closed.
const SEARCH_INTERVAL_IN_MILLISECONDS = 5 * 1000;
function dump(s) {
Services.console.logStringMessage("aboutDevices :: " + s);
}
var Devices = {
_savedSearchInterval: -1,
init: function() {
dump("Initializing.");
Services.obs.addObserver(this, EVENT_SERVICE_FOUND, false);
Services.obs.addObserver(this, EVENT_SERVICE_LOST, false);
let button = document.getElementById("refresh");
button.addEventListener("click", () => {
this.updateDeviceList();
}, false);
this._savedSearchInterval = SimpleServiceDiscovery.search(SEARCH_INTERVAL_IN_MILLISECONDS);
this.updateDeviceList();
},
uninit: function() {
dump("Uninitializing.");
Services.obs.removeObserver(this, EVENT_SERVICE_FOUND);
Services.obs.removeObserver(this, EVENT_SERVICE_LOST);
if (this._savedSearchInterval > 0) {
SimpleServiceDiscovery.search(this._savedSearchInterval);
}
},
_createItemForDevice: function(device) {
let item = document.createElement("div");
let friendlyName = document.createElement("div");
friendlyName.classList.add("name");
friendlyName.textContent = device.friendlyName;
item.appendChild(friendlyName);
let location = document.createElement("div");
location.classList.add("location");
location.textContent = device.location;
item.appendChild(location);
return item;
},
updateDeviceList: function() {
let services = SimpleServiceDiscovery.services;
dump("Updating device list with " + services.length + " services.");
let list = document.getElementById("devices-list");
while (list.firstChild) {
list.removeChild(list.firstChild);
}
for (let service of services) {
let item = this._createItemForDevice(service);
list.appendChild(item);
}
},
observe: function(subject, topic, data) {
if (topic == EVENT_SERVICE_FOUND || topic == EVENT_SERVICE_LOST) {
this.updateDeviceList();
}
},
};
window.addEventListener("load", Devices.init.bind(Devices), false);
window.addEventListener("unload", Devices.uninit.bind(Devices), false);

View File

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
%brandDTD;
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % aboutDTD SYSTEM "chrome://browser/locale/aboutDevices.dtd" >
%aboutDTD;
]>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&aboutDevices.title;</title>
<meta name="viewport" content="width=device-width; user-scalable=0" />
<link rel="icon" type="image/png" sizes="64x64" href="chrome://branding/content/favicon64.png" />
<link rel="stylesheet" href="chrome://browser/skin/aboutBase.css" type="text/css"/>
<link rel="stylesheet" href="chrome://browser/skin/aboutDevices.css" type="text/css"/>
</head>
<body>
<h1>&aboutDevices.header;</h1>
<ul id="devices-list"></ul>
<button id="refresh">&aboutDevices.refresh;</button>
<script type="text/javascript;version=1.8" src="chrome://browser/content/aboutDevices.js"></script>
</body>
</html>

View File

@ -56,6 +56,10 @@ chrome.jar:
content/aboutHealthReport.xhtml (content/aboutHealthReport.xhtml)
* content/aboutHealthReport.js (content/aboutHealthReport.js)
#endif
#ifdef MOZ_DEVICES
content/aboutDevices.xhtml (content/aboutDevices.xhtml)
content/aboutDevices.js (content/aboutDevices.js)
#endif
% content branding %content/branding/

View File

@ -78,6 +78,12 @@ let modules = {
privileged: true
},
#endif
#ifdef MOZ_DEVICES
devices: {
uri: "chrome://browser/content/aboutDevices.xhtml",
privileged: true
},
#endif
}
function AboutRedirector() {}

View File

@ -18,6 +18,9 @@ contract @mozilla.org/network/protocol/about;1?what=healthreport {322ba47e-7047-
#ifdef MOZ_SAFE_BROWSING
contract @mozilla.org/network/protocol/about;1?what=blocked {322ba47e-7047-4f71-aebf-cb7d69325cd9}
#endif
#ifdef MOZ_DEVICES
contract @mozilla.org/network/protocol/about;1?what=devices {322ba47e-7047-4f71-aebf-cb7d69325cd9}
#endif
# DirectoryProvider.js
component {ef0f7a87-c1ee-45a8-8d67-26f586e46a4b} DirectoryProvider.js

View File

@ -69,3 +69,6 @@ MOZ_B2G_CERTDATA=1
# Enable the "synthetic APKs" implementation of Open Web Apps.
MOZ_ANDROID_SYNTHAPKS=1
# Enable second screen and casting support for external devices.
MOZ_DEVICES=1

View File

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY aboutDevices.title "Devices">
<!ENTITY aboutDevices.header "Your devices">
<!ENTITY aboutDevices.refresh "Refresh">

View File

@ -10,6 +10,9 @@
locale/@AB_CD@/browser/aboutAddons.dtd (%chrome/aboutAddons.dtd)
locale/@AB_CD@/browser/aboutAddons.properties (%chrome/aboutAddons.properties)
locale/@AB_CD@/browser/aboutApps.dtd (%chrome/aboutApps.dtd)
#ifdef MOZ_DEVICES
locale/@AB_CD@/browser/aboutDevices.dtd (%chrome/aboutDevices.dtd)
#endif
locale/@AB_CD@/browser/aboutCertError.dtd (%chrome/aboutCertError.dtd)
locale/@AB_CD@/browser/aboutDownloads.dtd (%chrome/aboutDownloads.dtd)
locale/@AB_CD@/browser/aboutDownloads.properties (%chrome/aboutDownloads.properties)

View File

@ -37,11 +37,17 @@ const SSDP_DISCOVER_PACKET =
const SSDP_DISCOVER_TIMEOUT = 10000;
const EVENT_SERVICE_FOUND = "ssdp-service-found";
const EVENT_SERVICE_LOST = "ssdp-service-lost";
/*
* SimpleServiceDiscovery manages any discovered SSDP services. It uses a UDP
* broadcast to locate available services on the local network.
*/
var SimpleServiceDiscovery = {
get EVENT_SERVICE_FOUND() { return EVENT_SERVICE_FOUND; },
get EVENT_SERVICE_LOST() { return EVENT_SERVICE_LOST; },
_targets: new Map(),
_services: new Map(),
_searchSocket: null,
@ -107,12 +113,15 @@ var SimpleServiceDiscovery = {
// Start a search. Make it continuous by passing an interval (in milliseconds).
// This will stop a current search loop because the timer resets itself.
// Returns the existing search interval.
search: function search(aInterval) {
let existingSearchInterval = this._searchInterval;
if (aInterval > 0) {
this._searchInterval = aInterval || 0;
this._searchRepeat.initWithCallback(this._search.bind(this), this._searchInterval, Ci.nsITimer.TYPE_REPEATING_SLACK);
}
this._search();
return existingSearchInterval;
},
// Stop the current continuous search
@ -210,7 +219,7 @@ var SimpleServiceDiscovery = {
// Clean out any stale services
for (let [key, service] of this._services) {
if (service.lastPing != this._searchTimestamp) {
Services.obs.notifyObservers(null, "ssdp-service-lost", service.location);
Services.obs.notifyObservers(null, EVENT_SERVICE_LOST, service.location);
this._services.delete(service.location);
}
}
@ -273,7 +282,7 @@ var SimpleServiceDiscovery = {
// Only add and notify if we don't already know about this service
if (!this._services.has(aService.location)) {
this._services.set(aService.location, aService);
Services.obs.notifyObservers(null, "ssdp-service-found", aService.location);
Services.obs.notifyObservers(null, EVENT_SERVICE_FOUND, aService.location);
}
// Make sure we remember this service is not stale

View File

@ -0,0 +1,8 @@
* {
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}

View File

@ -11,6 +11,9 @@ chrome.jar:
* skin/aboutAddons.css (aboutAddons.css)
skin/aboutApps.css (aboutApps.css)
* skin/aboutBase.css (aboutBase.css)
#ifdef MOZ_DEVICES
skin/aboutDevices.css (aboutDevices.css)
#endif
* skin/aboutDownloads.css (aboutDownloads.css)
skin/aboutFeedback.css (aboutFeedback.css)
#ifdef MOZ_SERVICES_HEALTHREPORT

View File

@ -67,8 +67,6 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/AsyncShutdown.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonRepository",
@ -464,7 +462,6 @@ var gUpdateEnabled = true;
var gAutoUpdateDefault = true;
var gHotfixID = null;
var gUpdateCheckInProgress = false;
/**
* This is the real manager, kept here rather than in AddonManager to keep its
* contents hidden from API users.
@ -480,6 +477,7 @@ var AddonManagerInternal = {
// Store telemetry details per addon provider
telemetryDetails: {},
// A read-only wrapper around the types dictionary
typesProxy: Proxy.create({
getOwnPropertyDescriptor: function typesProxy_getOwnPropertyDescriptor(aName) {
@ -1134,121 +1132,120 @@ var AddonManagerInternal = {
/**
* Performs a background update check by starting an update for all add-ons
* that can be updated.
* @return Promise{null} resolves when the background update check is complete
* (including all addon installs)
*/
backgroundUpdateCheck: function AMI_backgroundUpdateCheck() {
if (!gStarted)
throw Components.Exception("AddonManager is not initialized",
Cr.NS_ERROR_NOT_INITIALIZED);
if (gUpdateCheckInProgress) {
throw Components.Exception("Background update check already in progress",
Cr.NS_ERROR_UNEXPECTED);
let hotfixID = this.hotfixID;
let checkHotfix = hotfixID &&
Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
if (!this.updateEnabled && !checkHotfix)
return;
Services.obs.notifyObservers(null, "addons-background-update-start", null);
// Start this from one to ensure the whole of this function completes before
// we can send the complete notification. Some parts can in some cases
// complete synchronously before later parts have a chance to increment
// pendingUpdates.
let pendingUpdates = 1;
function notifyComplete() {
if (--pendingUpdates == 0) {
Services.obs.notifyObservers(null,
"addons-background-update-complete",
null);
}
}
gUpdateCheckInProgress = true;
return Task.spawn(function* backgroundUpdateTask() {
let hotfixID = this.hotfixID;
let checkHotfix = hotfixID &&
Services.prefs.getBoolPref(PREF_APP_UPDATE_ENABLED) &&
Services.prefs.getBoolPref(PREF_APP_UPDATE_AUTO);
if (!this.updateEnabled && !checkHotfix)
return;
Services.obs.notifyObservers(null, "addons-background-update-start", null);
if (this.updateEnabled) {
let scope = {};
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
scope.LightweightThemeManager.updateCurrentTheme();
let aAddons = yield new Promise((resolve, reject) => this.getAllAddons(resolve));
if (this.updateEnabled) {
let scope = {};
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm", scope);
scope.LightweightThemeManager.updateCurrentTheme();
pendingUpdates++;
this.getAllAddons(function getAddonsCallback(aAddons) {
// If there is a known hotfix then exclude it from the list of add-ons to update.
var ids = [a.id for each (a in aAddons) if (a.id != hotfixID)];
// Repopulate repository cache first, to ensure compatibility overrides
// are up to date before checking for addon updates.
yield new Promise((resolve, reject) => AddonRepository.backgroundUpdateCheck(ids, resolve));
AddonRepository.backgroundUpdateCheck(
ids, function BUC_backgroundUpdateCheckCallback() {
pendingUpdates += aAddons.length;
aAddons.forEach(function BUC_forEachCallback(aAddon) {
if (aAddon.id == hotfixID) {
notifyComplete();
return;
}
// Keep track of all the async add-on updates happening in parallel
let updates = [];
for (let aAddon of aAddons) {
if (aAddon.id == hotfixID) {
continue;
}
// Check all add-ons for updates so that any compatibility updates will
// be applied
updates.push(new Promise((resolve, reject) => {
// Check all add-ons for updates so that any compatibility updates will
// be applied
aAddon.findUpdates({
onUpdateAvailable: function BUC_onUpdateAvailable(aAddon, aInstall) {
// Start installing updates when the add-on can be updated and
// background updates should be applied.
if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE &&
AddonManager.shouldAutoUpdate(aAddon)) {
// XXX we really should resolve when this install is done,
// not when update-available check completes, no?
aInstall.install();
}
},
onUpdateFinished: resolve
onUpdateFinished: notifyComplete
}, AddonManager.UPDATE_WHEN_PERIODIC_UPDATE);
}));
}
yield Promise.all(updates);
}
if (checkHotfix) {
var hotfixVersion = "";
try {
hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
}
catch (e) { }
let url = null;
if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
else
url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
// Build the URI from a fake add-on data.
url = AddonManager.escapeAddonURI({
id: hotfixID,
version: hotfixVersion,
userDisabled: false,
appDisabled: false
}, url);
Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
let update = null;
try {
let foundUpdates = yield new Promise((resolve, reject) => {
AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
onUpdateCheckComplete: resolve,
onUpdateCheckError: reject
});
});
update = AddonUpdateChecker.getNewestCompatibleUpdate(foundUpdates);
} catch (e) {
// AUC.checkForUpdates already logged the error
}
// Check that we have a hotfix update, and it's newer than the one we already
// have installed (if any)
if (update) {
if (Services.vc.compare(hotfixVersion, update.version) < 0) {
logger.debug("Downloading hotfix version " + update.version);
let aInstall = yield new Promise((resolve, reject) =>
AddonManager.getInstallForURL(update.updateURL, resolve,
"application/x-xpinstall", update.updateHash, null,
null, update.version));
notifyComplete();
});
});
}
if (checkHotfix) {
var hotfixVersion = "";
try {
hotfixVersion = Services.prefs.getCharPref(PREF_EM_HOTFIX_LASTVERSION);
}
catch (e) { }
let url = null;
if (Services.prefs.getPrefType(PREF_EM_HOTFIX_URL) == Ci.nsIPrefBranch.PREF_STRING)
url = Services.prefs.getCharPref(PREF_EM_HOTFIX_URL);
else
url = Services.prefs.getCharPref(PREF_EM_UPDATE_BACKGROUND_URL);
// Build the URI from a fake add-on data.
url = AddonManager.escapeAddonURI({
id: hotfixID,
version: hotfixVersion,
userDisabled: false,
appDisabled: false
}, url);
pendingUpdates++;
Components.utils.import("resource://gre/modules/addons/AddonUpdateChecker.jsm");
AddonUpdateChecker.checkForUpdates(hotfixID, null, url, {
onUpdateCheckComplete: function BUC_onUpdateCheckComplete(aUpdates) {
let update = AddonUpdateChecker.getNewestCompatibleUpdate(aUpdates);
if (!update) {
notifyComplete();
return;
}
// If the available version isn't newer than the last installed
// version then ignore it.
if (Services.vc.compare(hotfixVersion, update.version) >= 0) {
notifyComplete();
return;
}
logger.debug("Downloading hotfix version " + update.version);
AddonManager.getInstallForURL(update.updateURL,
function BUC_getInstallForURL(aInstall) {
aInstall.addListener({
onDownloadEnded: function BUC_onDownloadEnded(aInstall) {
try {
@ -1286,15 +1283,17 @@ var AddonManagerInternal = {
});
aInstall.install();
}
}
}
gUpdateCheckInProgress = false;
Services.obs.notifyObservers(null,
"addons-background-update-complete",
null);
}.bind(this));
notifyComplete();
}, "application/x-xpinstall", update.updateHash, null,
null, update.version);
},
onUpdateCheckError: notifyComplete
});
}
notifyComplete();
},
/**

View File

@ -3610,13 +3610,7 @@ this.XPIProvider = {
* The AddonInstall to remove
*/
removeActiveInstall: function XPI_removeActiveInstall(aInstall) {
let where = this.installs.indexOf(aInstall);
if (where == -1) {
logger.warn("removeActiveInstall: could not find active install for "
+ aInstall.sourceURI.spec);
return;
}
this.installs.splice(where, 1);
this.installs = this.installs.filter(function installFilter(i) i != aInstall);
},
/**

View File

@ -75,7 +75,7 @@ function install_test_addons(aCallback) {
// Switch to the test update URL
Services.prefs.setCharPref(PREF_UPDATEURL, TESTROOT + "browser_bug557956.rdf");
executeSoon(aCallback);
aCallback();
}
}
};

View File

@ -41,7 +41,7 @@ add_test(function () {
gInstall = new MockInstall(undefined, undefined, addon);
gInstall.addTestListener({
onNewInstall: function () {
executeSoon(run_next_test);
run_next_test();
}
});
gProvider.addInstall(gInstall);
@ -65,7 +65,7 @@ add_test(function () {
}
}
ok(false, "Item with correct name was not found");
executeSoon(run_next_test);
run_next_test();
}
});
gInstall.install();

View File

@ -98,27 +98,9 @@ function test_confirmation(aWindow, aExpectedURLs) {
aWindow.document.documentElement.cancelDialog();
}
add_task(function* test_install_from_file() {
gManagerWindow = yield open_manager("addons://list/extension");
function test() {
waitForExplicitFinish();
open_manager("addons://list/extension", function(aWindow) {
gManagerWindow = aWindow;
run_next_test();
});
}
function end_test() {
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
MockFilePicker.cleanup();
close_manager(gManagerWindow, function() {
finish();
});
}
add_test(function() {
var filePaths = [
get_addon_file_url("browser_bug567127_1.xpi"),
get_addon_file_url("browser_bug567127_2.xpi")
@ -128,9 +110,24 @@ add_test(function() {
Services.obs.addObserver(gInstallNotificationObserver,
"addon-install-started", false);
new WindowOpenListener(INSTALL_URI, function(aWindow) {
test_confirmation(aWindow, filePaths.map(function(aPath) aPath.spec));
}, run_next_test);
// Set handler that executes the core test after the window opens,
// and resolves the promise when the window closes
let pInstallURIClosed = new Promise((resolve, reject) => {
new WindowOpenListener(INSTALL_URI, function(aWindow) {
try {
test_confirmation(aWindow, filePaths.map(function(aPath) aPath.spec));
} catch(e) {
reject(e);
}
}, resolve);
});
gManagerWindow.gViewController.doCommand("cmd_installFromFile");
yield pInstallURIClosed;
is(gSawInstallNotification, true, "Should have seen addon-install-started notification.");
MockFilePicker.cleanup();
yield close_manager(gManagerWindow);
});

View File

@ -21,18 +21,17 @@ var gTestInstallListener = {
onInstallEnded: function(aInstall) {
check_hidden(false);
executeSoon(run_next_test);
run_next_test();
},
onInstallCancelled: function(aInstall) {
ok(gExpectedCancel, "Should expect install cancel");
check_hidden(false);
executeSoon(run_next_test);
run_next_test();
},
onInstallFailed: function(aInstall) {
ok(false, "Did not expect onInstallFailed");
executeSoon(run_next_test);
}
};

View File

@ -40,7 +40,7 @@ function install_locale(aCallback) {
gInstall.addTestListener({
onInstallEnded: function(aInstall) {
gInstall.removeTestListener(this);
executeSoon(aCallback);
aCallback();
}
});
gInstall.install();

View File

@ -97,7 +97,7 @@ add_test(function() {
},
onInstallEnded: function() {
check_list(gItem);
executeSoon(run_next_test);
run_next_test();
}
});
@ -136,7 +136,7 @@ add_test(function() {
onInstallEnded: function() {
check_list(null);
extension.cancel();
executeSoon(run_next_test);
run_next_test();
}
});

View File

@ -15,56 +15,51 @@ const PREF_APP_UPDATE_ENABLED = "app.update.enabled";
const HOTFIX_ID = "hotfix@tests.mozilla.org";
/*
* Register an addon install listener and return a promise that:
* resolves with the AddonInstall object if the install succeeds
* rejects with the AddonInstall if the install fails
*/
function promiseInstallListener() {
return new Promise((resolve, reject) => {
let listener = {
onInstallEnded: ai => {
AddonManager.removeInstallListener(listener);
resolve(ai);
},
onDownloadCancelled: ai => {
AddonManager.removeInstallListener(listener);
reject(ai);
}
};
AddonManager.addInstallListener(listener);
});
var gNextTest;
var SuccessfulInstallListener = {
onDownloadCancelled: function(aInstall) {
ok(false, "Should not have seen the download cancelled");
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
AddonManager.removeInstallListener(this);
gNextTest();
},
onInstallEnded: function(aInstall) {
ok(true, "Should have seen the install complete");
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
AddonManager.removeInstallListener(this);
aInstall.addon.uninstall();
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
gNextTest();
}
}
function promiseSuccessfulInstall() {
return promiseInstallListener().then(
aInstall => {
ok(true, "Should have seen the install complete");
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
aInstall.addon.uninstall();
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
},
aInstall => {
ok(false, "Should not have seen the download cancelled");
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
});
var FailedInstallListener = {
onDownloadCancelled: function(aInstall) {
ok(true, "Should have seen the download cancelled");
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
AddonManager.removeInstallListener(this);
gNextTest();
},
onInstallEnded: function(aInstall) {
ok(false, "Should not have seen the install complete");
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
AddonManager.removeInstallListener(this);
aInstall.addon.uninstall();
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
gNextTest();
}
}
function promiseFailedInstall() {
return promiseInstallListener().then(
aInstall => {
ok(false, "Should not have seen the install complete");
is(aInstall.addon.id, HOTFIX_ID, "Should have installed the right add-on");
aInstall.addon.uninstall();
Services.prefs.clearUserPref(PREF_EM_HOTFIX_LASTVERSION);
},
aInstall => {
ok(true, "Should have seen the download cancelled");
is(aInstall.addon.id, HOTFIX_ID, "Should have seen the right add-on");
});
}
function test() {
waitForExplicitFinish();
add_task(function setup() {
Services.prefs.setBoolPref(PREF_APP_UPDATE_ENABLED, true);
Services.prefs.setBoolPref(PREF_INSTALL_REQUIREBUILTINCERTS, false);
Services.prefs.setBoolPref(PREF_UPDATE_REQUIREBUILTINCERTS, false);
@ -83,85 +78,109 @@ add_task(function setup() {
var prefs = Services.prefs.getChildList(PREF_EM_HOTFIX_CERTS);
prefs.forEach(Services.prefs.clearUserPref);
});
});
add_task(function* check_no_cert_checks() {
run_next_test();
}
function end_test() {
finish();
}
add_test(function check_no_cert_checks() {
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
yield Promise.all([
promiseSuccessfulInstall(),
AddonManagerPrivate.backgroundUpdateCheck()
]);
AddonManager.addInstallListener(SuccessfulInstallListener);
gNextTest = run_next_test;
AddonManagerPrivate.backgroundUpdateCheck();
});
add_task(function* check_wrong_cert_fingerprint() {
add_test(function check_wrong_cert_fingerprint() {
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo");
yield Promise.all([
promiseFailedInstall(),
AddonManagerPrivate.backgroundUpdateCheck()
]);
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
AddonManager.addInstallListener(FailedInstallListener);
gNextTest = function() {
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
run_next_test();
};
AddonManagerPrivate.backgroundUpdateCheck();
});
add_task(function* check_right_cert_fingerprint() {
add_test(function check_right_cert_fingerprint() {
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
yield Promise.all([
promiseSuccessfulInstall(),
AddonManagerPrivate.backgroundUpdateCheck()
]);
AddonManager.addInstallListener(SuccessfulInstallListener);
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
gNextTest = function() {
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
run_next_test();
};
AddonManagerPrivate.backgroundUpdateCheck();
});
add_task(function* check_multi_cert_fingerprint_1() {
add_test(function check_multi_cert_fingerprint_1() {
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "foo");
yield Promise.all([
promiseSuccessfulInstall(),
AddonManagerPrivate.backgroundUpdateCheck()
]);
AddonManager.addInstallListener(SuccessfulInstallListener);
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
gNextTest = function() {
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
run_next_test();
};
AddonManagerPrivate.backgroundUpdateCheck();
});
add_task(function* check_multi_cert_fingerprint_2() {
add_test(function check_multi_cert_fingerprint_2() {
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "foo");
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
yield Promise.all([
promiseSuccessfulInstall(),
AddonManagerPrivate.backgroundUpdateCheck()
]);
AddonManager.addInstallListener(SuccessfulInstallListener);
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
gNextTest = function() {
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "2.sha1Fingerprint");
run_next_test();
};
AddonManagerPrivate.backgroundUpdateCheck();
});
add_task(function* check_no_cert_no_checks() {
add_test(function check_no_cert_no_checks() {
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, false);
Services.prefs.setCharPref(PREF_EM_HOTFIX_URL, TESTROOT + "unsigned_hotfix.rdf");
yield Promise.all([
promiseSuccessfulInstall(),
AddonManagerPrivate.backgroundUpdateCheck()
]);
AddonManager.addInstallListener(SuccessfulInstallListener);
gNextTest = run_next_test;
AddonManagerPrivate.backgroundUpdateCheck();
});
add_task(function* check_no_cert_cert_fingerprint_check() {
add_test(function check_no_cert_cert_fingerprint_check() {
Services.prefs.setBoolPref(PREF_EM_CERT_CHECKATTRIBUTES, true);
Services.prefs.setCharPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint", "3E:B9:4E:07:12:FE:3C:01:41:46:13:46:FC:84:52:1A:8C:BE:1D:A2");
yield Promise.all([
promiseFailedInstall(),
AddonManagerPrivate.backgroundUpdateCheck()
]);
AddonManager.addInstallListener(FailedInstallListener);
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
gNextTest = function() {
Services.prefs.clearUserPref(PREF_EM_HOTFIX_CERTS + "1.sha1Fingerprint");
run_next_test();
};
AddonManagerPrivate.backgroundUpdateCheck();
});

View File

@ -184,7 +184,7 @@ add_test(function() {
is_element_hidden(item._installStatus, "Install progress widget should be hidden");
if (badgeUpdated)
executeSoon(run_next_test);
run_next_test();
else
installCompleted = true;
}

View File

@ -566,7 +566,7 @@ add_test(function() {
is(installBtn.hidden, true, "Install button should be hidden after install ended");
check_filtered_results(QUERY, "relevancescore", false);
executeSoon(run_next_test);
run_next_test();
}
}

View File

@ -30,7 +30,7 @@ function install_test_addon(aCallback) {
onInstallEnded: function() {
AddonManager.getAddonByID("addon1@tests.mozilla.org", function(addon) {
gTestAddon = addon;
executeSoon(aCallback);
aCallback();
});
}
};

View File

@ -336,6 +336,7 @@ function open_manager(aView, aCallback, aLoadCallback, aLongerTimeout) {
}
});
// The promise resolves with the manager window, so it is passed to the callback
return log_callback(p, aCallback);
}
@ -347,13 +348,19 @@ function close_manager(aManagerWindow, aCallback, aLongerTimeout) {
is(aManagerWindow.location, MANAGER_URI, "Should be closing window with correct URI");
aManagerWindow.addEventListener("unload", function() {
info("Manager window unloaded");
this.removeEventListener("unload", arguments.callee, false);
resolve();
try {
dump("Manager window unload handler");
this.removeEventListener("unload", arguments.callee, false);
resolve();
} catch(e) {
reject(e);
}
}, false);
});
info("Telling manager window to close");
aManagerWindow.close();
info("Manager window close() call returned");
return log_callback(p, aCallback);
}

View File

@ -1775,9 +1775,43 @@ RENDER_AGAIN:
DrawThemeBackground(theme, hdc, MENU_POPUPSEPARATOR, /* state */ 0, &sepRect, &clipRect);
}
else if (aWidgetType == NS_THEME_MENUARROW)
{
// We're dpi aware and as such on systems that have dpi > 96 set, the
// theme library expects us to do proper positioning and scaling of glyphs.
// For NS_THEME_MENUARROW, layout may hand us a widget rect larger than the
// glyph rect we request in GetMinimumWidgetSize. To prevent distortion we
// have to position and scale what we draw.
SIZE glyphSize;
GetThemePartSize(theme, hdc, part, state, nullptr, TS_TRUE, &glyphSize);
int32_t widgetHeight = widgetRect.bottom - widgetRect.top;
RECT renderRect = widgetRect;
// We request (glyph width * 2, glyph height) in GetMinimumWidgetSize. In
// Firefox some menu items provide the full height of the item to us, in
// others our widget rect is the exact dims of our arrow glyph. Adjust the
// vertical position by the added space, if any exists.
renderRect.top += ((widgetHeight - glyphSize.cy) / 2);
renderRect.bottom = renderRect.top + glyphSize.cy;
// I'm using the width of the arrow glyph for the arrow-side padding.
// AFAICT there doesn't appear to be a theme constant we can query
// for this value. Generally this looks correct, and has the added
// benefit of being a dpi adjusted value.
if (!IsFrameRTL(aFrame)) {
renderRect.right = widgetRect.right - glyphSize.cx;
renderRect.left = renderRect.right - glyphSize.cx;
} else {
renderRect.left = glyphSize.cx;
renderRect.right = renderRect.left + glyphSize.cx;
}
DrawThemeBGRTLAware(theme, hdc, part, state, &renderRect, &clipRect,
IsFrameRTL(aFrame));
}
// The following widgets need to be RTL-aware
else if (aWidgetType == NS_THEME_MENUARROW ||
aWidgetType == NS_THEME_RESIZER ||
else if (aWidgetType == NS_THEME_RESIZER ||
aWidgetType == NS_THEME_DROPDOWN_BUTTON)
{
DrawThemeBGRTLAware(theme, hdc, part, state,
@ -2239,11 +2273,6 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* a
case NS_THEME_MENUITEMTEXT:
return NS_OK;
case NS_THEME_MENUARROW:
aResult->width = 26;
aResult->height = 16;
return NS_OK;
case NS_THEME_PROGRESSBAR:
case NS_THEME_PROGRESSBAR_VERTICAL:
// Best-fit size for progress meters is too large for most
@ -2403,6 +2432,14 @@ nsNativeThemeWin::GetMinimumWidgetSize(nsRenderingContext* aContext, nsIFrame* a
aResult->width += gutterSize.cx;
break;
}
case NS_THEME_MENUARROW:
{
// Use the width of the arrow glyph as padding. See the drawing
// code for details.
aResult->width *= 2;
break;
}
}
::ReleaseDC(nullptr, hdc);