mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-07 23:43:37 +00:00
Merge mozilla-central into mozilla-inbound
This commit is contained in:
commit
629d8a0ae2
@ -44,6 +44,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
|
||||
"resource://gre/modules/DownloadUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsPanel
|
||||
@ -107,7 +109,7 @@ const DownloadsPanel = {
|
||||
DownloadsOverlayLoader.ensureOverlayLoaded(this.kDownloadsOverlay,
|
||||
function DP_I_callback() {
|
||||
DownloadsViewController.initialize();
|
||||
DownloadsCommon.data.addView(DownloadsView);
|
||||
DownloadsCommon.getData(window).addView(DownloadsView);
|
||||
DownloadsPanel._attachEventListeners();
|
||||
aCallback();
|
||||
});
|
||||
@ -130,7 +132,7 @@ const DownloadsPanel = {
|
||||
this.hidePanel();
|
||||
|
||||
DownloadsViewController.terminate();
|
||||
DownloadsCommon.data.removeView(DownloadsView);
|
||||
DownloadsCommon.getData(window).removeView(DownloadsView);
|
||||
this._unattachEventListeners();
|
||||
|
||||
this._state = this.kStateUninitialized;
|
||||
@ -230,7 +232,7 @@ const DownloadsPanel = {
|
||||
this._state = this.kStateShown;
|
||||
|
||||
// Since at most one popup is open at any given time, we can set globally.
|
||||
DownloadsCommon.indicatorData.attentionSuppressed = true;
|
||||
DownloadsCommon.getIndicatorData(window).attentionSuppressed = true;
|
||||
|
||||
// Ensure that an item is selected when the panel is focused.
|
||||
if (DownloadsView.richListBox.itemCount > 0 &&
|
||||
@ -249,7 +251,7 @@ const DownloadsPanel = {
|
||||
}
|
||||
|
||||
// Since at most one popup is open at any given time, we can set globally.
|
||||
DownloadsCommon.indicatorData.attentionSuppressed = false;
|
||||
DownloadsCommon.getIndicatorData(window).attentionSuppressed = false;
|
||||
|
||||
// Allow the anchor to be hidden.
|
||||
DownloadsButton.releaseAnchor();
|
||||
@ -1110,7 +1112,11 @@ const DownloadsViewController = {
|
||||
{
|
||||
// Handle commands that are not selection-specific.
|
||||
if (aCommand == "downloadsCmd_clearList") {
|
||||
return Services.downloads.canCleanUp;
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
return Services.downloads.canCleanUpPrivate;
|
||||
} else {
|
||||
return Services.downloads.canCleanUp;
|
||||
}
|
||||
}
|
||||
|
||||
// Other commands are selection-specific.
|
||||
@ -1157,7 +1163,11 @@ const DownloadsViewController = {
|
||||
commands: {
|
||||
downloadsCmd_clearList: function DVC_downloadsCmd_clearList()
|
||||
{
|
||||
Services.downloads.cleanUp();
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(window)) {
|
||||
Services.downloads.cleanUpPrivate();
|
||||
} else {
|
||||
Services.downloads.cleanUp();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1171,7 +1181,7 @@ const DownloadsViewController = {
|
||||
*/
|
||||
function DownloadsViewItemController(aElement) {
|
||||
let downloadGuid = aElement.getAttribute("downloadGuid");
|
||||
this.dataItem = DownloadsCommon.data.dataItems[downloadGuid];
|
||||
this.dataItem = DownloadsCommon.getData(window).dataItems[downloadGuid];
|
||||
}
|
||||
|
||||
DownloadsViewItemController.prototype = {
|
||||
@ -1457,10 +1467,10 @@ const DownloadsSummary = {
|
||||
return this._active;
|
||||
}
|
||||
if (aActive) {
|
||||
DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
|
||||
DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
|
||||
.addView(this);
|
||||
} else {
|
||||
DownloadsCommon.getSummary(DownloadsView.kItemCountLimit)
|
||||
DownloadsCommon.getSummary(window, DownloadsView.kItemCountLimit)
|
||||
.removeView(this);
|
||||
DownloadsFooter.showingSummary = false;
|
||||
}
|
||||
|
@ -286,7 +286,7 @@ const DownloadsIndicatorView = {
|
||||
this._initialized = true;
|
||||
|
||||
window.addEventListener("unload", this.onWindowUnload, false);
|
||||
DownloadsCommon.indicatorData.addView(this);
|
||||
DownloadsCommon.getIndicatorData(window).addView(this);
|
||||
},
|
||||
|
||||
/**
|
||||
@ -300,7 +300,7 @@ const DownloadsIndicatorView = {
|
||||
this._initialized = false;
|
||||
|
||||
window.removeEventListener("unload", this.onWindowUnload, false);
|
||||
DownloadsCommon.indicatorData.removeView(this);
|
||||
DownloadsCommon.getIndicatorData(window).removeView(this);
|
||||
|
||||
// Reset the view properties, so that a neutral indicator is displayed if we
|
||||
// are visible only temporarily as an anchor.
|
||||
@ -327,7 +327,7 @@ const DownloadsIndicatorView = {
|
||||
// If the view is initialized, we need to update the elements now that
|
||||
// they are finally available in the document.
|
||||
if (this._initialized) {
|
||||
DownloadsCommon.indicatorData.refreshView(this);
|
||||
DownloadsCommon.getIndicatorData(window).refreshView(this);
|
||||
}
|
||||
|
||||
aCallback();
|
||||
@ -508,7 +508,7 @@ const DownloadsIndicatorView = {
|
||||
{
|
||||
if (DownloadsCommon.useToolkitUI) {
|
||||
// The panel won't suppress attention for us, we need to clear now.
|
||||
DownloadsCommon.indicatorData.attention = false;
|
||||
DownloadsCommon.getIndicatorData(window).attention = false;
|
||||
BrowserDownloadsUI();
|
||||
} else {
|
||||
DownloadsPanel.showPanel();
|
||||
|
@ -56,6 +56,10 @@ XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
|
||||
"resource://gre/modules/PluralForm.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadUtils",
|
||||
"resource://gre/modules/DownloadUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
|
||||
const nsIDM = Ci.nsIDownloadManager;
|
||||
|
||||
@ -173,37 +177,95 @@ this.DownloadsCommon = {
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a reference to the DownloadsData singleton.
|
||||
* Get access to one of the DownloadsData or PrivateDownloadsData objects,
|
||||
* depending on the privacy status of the window in question.
|
||||
*
|
||||
* This does not need to be a lazy getter, since no initialization is required
|
||||
* at present.
|
||||
* @param aWindow
|
||||
* The browser window which owns the download button.
|
||||
*/
|
||||
get data() DownloadsData,
|
||||
getData: function DC_getData(aWindow) {
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
|
||||
return PrivateDownloadsData;
|
||||
} else {
|
||||
return DownloadsData;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a reference to the DownloadsData singleton.
|
||||
* Initializes the data link for both the private and non-private downloads
|
||||
* data objects.
|
||||
*
|
||||
* This does not need to be a lazy getter, since no initialization is required
|
||||
* at present.
|
||||
* @param aDownloadManagerService
|
||||
* Reference to the service implementing nsIDownloadManager. We need
|
||||
* this because getService isn't available for us when this method is
|
||||
* called, and we must ensure to register our listeners before the
|
||||
* getService call for the Download Manager returns.
|
||||
*/
|
||||
get indicatorData() DownloadsIndicatorData,
|
||||
initializeAllDataLinks: function DC_initializeAllDataLinks(aDownloadManagerService) {
|
||||
DownloadsData.initializeDataLink(aDownloadManagerService);
|
||||
PrivateDownloadsData.initializeDataLink(aDownloadManagerService);
|
||||
},
|
||||
|
||||
/**
|
||||
* Terminates the data link for both the private and non-private downloads
|
||||
* data objects.
|
||||
*/
|
||||
terminateAllDataLinks: function DC_terminateAllDataLinks() {
|
||||
DownloadsData.terminateDataLink();
|
||||
PrivateDownloadsData.terminateDataLink();
|
||||
},
|
||||
|
||||
/**
|
||||
* Reloads the specified kind of downloads from the non-private store.
|
||||
* This method must only be called when Private Browsing Mode is disabled.
|
||||
*
|
||||
* @param aActiveOnly
|
||||
* True to load only active downloads from the database.
|
||||
*/
|
||||
ensureAllPersistentDataLoaded:
|
||||
function DC_ensureAllPersistentDataLoaded(aActiveOnly) {
|
||||
DownloadsData.ensurePersistentDataLoaded(aActiveOnly);
|
||||
},
|
||||
|
||||
/**
|
||||
* Get access to one of the DownloadsIndicatorData or
|
||||
* PrivateDownloadsIndicatorData objects, depending on the privacy status of
|
||||
* the window in question.
|
||||
*/
|
||||
getIndicatorData: function DC_getIndicatorData(aWindow) {
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
|
||||
return PrivateDownloadsIndicatorData;
|
||||
} else {
|
||||
return DownloadsIndicatorData;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a reference to the DownloadsSummaryData singleton - creating one
|
||||
* in the process if one hasn't been instantiated yet.
|
||||
*
|
||||
* @param aWindow
|
||||
* The browser window which owns the download button.
|
||||
* @param aNumToExclude
|
||||
* The number of items on the top of the downloads list to exclude
|
||||
* from the summary.
|
||||
*/
|
||||
_summary: null,
|
||||
getSummary: function DC_getSummary(aNumToExclude)
|
||||
getSummary: function DC_getSummary(aWindow, aNumToExclude)
|
||||
{
|
||||
if (this._summary) {
|
||||
return this._summary;
|
||||
if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) {
|
||||
if (this._privateSummary) {
|
||||
return this._privateSummary;
|
||||
}
|
||||
return this._privateSummary = new DownloadsSummaryData(true, aNumToExclude);
|
||||
} else {
|
||||
if (this._summary) {
|
||||
return this._summary;
|
||||
}
|
||||
return this._summary = new DownloadsSummaryData(false, aNumToExclude);
|
||||
}
|
||||
return this._summary = new DownloadsSummaryData(aNumToExclude);
|
||||
},
|
||||
_summary: null,
|
||||
_privateSummary: null,
|
||||
|
||||
/**
|
||||
* Given an iterable collection of DownloadDataItems, generates and returns
|
||||
@ -354,8 +416,33 @@ XPCOMUtils.defineLazyGetter(DownloadsCommon, "isWinVistaOrHigher", function () {
|
||||
* service. Consumers will see an empty list of downloads until the service is
|
||||
* actually started. This is useful to display a neutral progress indicator in
|
||||
* the main browser window until the autostart timeout elapses.
|
||||
*
|
||||
* Note that DownloadsData and PrivateDownloadsData are two equivalent singleton
|
||||
* objects, one accessing non-private downloads, and the other accessing private
|
||||
* ones.
|
||||
*/
|
||||
const DownloadsData = {
|
||||
function DownloadsDataCtor(aPrivate) {
|
||||
this._isPrivate = aPrivate;
|
||||
|
||||
// This Object contains all the available DownloadsDataItem objects, indexed by
|
||||
// their globally unique identifier. The identifiers of downloads that have
|
||||
// been removed from the Download Manager data are still present, however the
|
||||
// associated objects are replaced with the value "null". This is required to
|
||||
// prevent race conditions when populating the list asynchronously.
|
||||
this.dataItems = {};
|
||||
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
// While operating in Private Browsing Mode, persistent data items are parked
|
||||
// here until we return to the normal mode.
|
||||
this._persistentDataItems = {};
|
||||
#endif
|
||||
|
||||
// Array of view objects that should be notified when the available download
|
||||
// data changes.
|
||||
this._views = [];
|
||||
}
|
||||
|
||||
DownloadsDataCtor.prototype = {
|
||||
/**
|
||||
* Starts receiving events for current downloads.
|
||||
*
|
||||
@ -368,10 +455,12 @@ const DownloadsData = {
|
||||
initializeDataLink: function DD_initializeDataLink(aDownloadManagerService)
|
||||
{
|
||||
// Start receiving real-time events.
|
||||
aDownloadManagerService.addListener(this);
|
||||
aDownloadManagerService.addPrivacyAwareListener(this);
|
||||
Services.obs.addObserver(this, "download-manager-remove-download-guid", false);
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
Services.obs.addObserver(this, "download-manager-database-type-changed",
|
||||
false);
|
||||
#endif
|
||||
},
|
||||
|
||||
/**
|
||||
@ -382,7 +471,9 @@ const DownloadsData = {
|
||||
this._terminateDataAccess();
|
||||
|
||||
// Stop receiving real-time events.
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
Services.obs.removeObserver(this, "download-manager-database-type-changed");
|
||||
#endif
|
||||
Services.obs.removeObserver(this, "download-manager-remove-download-guid");
|
||||
Services.downloads.removeListener(this);
|
||||
},
|
||||
@ -390,12 +481,6 @@ const DownloadsData = {
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Registration of views
|
||||
|
||||
/**
|
||||
* Array of view objects that should be notified when the available download
|
||||
* data changes.
|
||||
*/
|
||||
_views: [],
|
||||
|
||||
/**
|
||||
* Adds an object to be notified when the available download data changes.
|
||||
* The specified object is initialized with the currently available downloads.
|
||||
@ -454,21 +539,6 @@ const DownloadsData = {
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// In-memory downloads data store
|
||||
|
||||
/**
|
||||
* Object containing all the available DownloadsDataItem objects, indexed by
|
||||
* their numeric download identifier. The identifiers of downloads that have
|
||||
* been removed from the Download Manager data are still present, however the
|
||||
* associated objects are replaced with the value "null". This is required to
|
||||
* prevent race conditions when populating the list asynchronously.
|
||||
*/
|
||||
dataItems: {},
|
||||
|
||||
/**
|
||||
* While operating in Private Browsing Mode, persistent data items are parked
|
||||
* here until we return to the normal mode.
|
||||
*/
|
||||
_persistentDataItems: {},
|
||||
|
||||
/**
|
||||
* Clears the loaded data.
|
||||
*/
|
||||
@ -591,7 +661,9 @@ const DownloadsData = {
|
||||
|
||||
// Reload the list using the Download Manager service. The list is
|
||||
// returned in no particular order.
|
||||
let downloads = Services.downloads.activeDownloads;
|
||||
let downloads = this._isPrivate ?
|
||||
Services.downloads.activePrivateDownloads :
|
||||
Services.downloads.activeDownloads;
|
||||
while (downloads.hasMoreElements()) {
|
||||
let download = downloads.getNext().QueryInterface(Ci.nsIDownload);
|
||||
this._getOrAddDataItem(download, true);
|
||||
@ -609,7 +681,10 @@ const DownloadsData = {
|
||||
// columns are read in the _initFromDataRow method of DownloadsDataItem.
|
||||
// Order by descending download identifier so that the most recent
|
||||
// downloads are notified first to the listening views.
|
||||
let statement = Services.downloads.DBConnection.createAsyncStatement(
|
||||
let dbConnection = this._isPrivate ?
|
||||
Services.downloads.privateDBConnection :
|
||||
Services.downloads.DBConnection;
|
||||
let statement = dbConnection.createAsyncStatement(
|
||||
"SELECT guid, target, name, source, referrer, state, "
|
||||
+ "startTime, endTime, currBytes, maxBytes "
|
||||
+ "FROM moz_downloads "
|
||||
@ -714,6 +789,7 @@ const DownloadsData = {
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
case "download-manager-database-type-changed":
|
||||
let pbs = Cc["@mozilla.org/privatebrowsing;1"]
|
||||
.getService(Ci.nsIPrivateBrowsingService);
|
||||
@ -731,6 +807,7 @@ const DownloadsData = {
|
||||
// already invalidated by the previous calls.
|
||||
this._views.forEach(this._updateView, this);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
},
|
||||
|
||||
@ -739,6 +816,12 @@ const DownloadsData = {
|
||||
|
||||
onDownloadStateChange: function DD_onDownloadStateChange(aState, aDownload)
|
||||
{
|
||||
if (aDownload.isPrivate != this._isPrivate) {
|
||||
// Ignore the downloads with a privacy status other than what we are
|
||||
// tracking.
|
||||
return;
|
||||
}
|
||||
|
||||
// When a new download is added, it may have the same identifier of a
|
||||
// download that we previously deleted during this session, and we also
|
||||
// want to provide a visible indication that the download started.
|
||||
@ -784,6 +867,12 @@ const DownloadsData = {
|
||||
aCurTotalProgress,
|
||||
aMaxTotalProgress, aDownload)
|
||||
{
|
||||
if (aDownload.isPrivate != this._isPrivate) {
|
||||
// Ignore the downloads with a privacy status other than what we are
|
||||
// tracking.
|
||||
return;
|
||||
}
|
||||
|
||||
let dataItem = this._getOrAddDataItem(aDownload, false);
|
||||
if (!dataItem) {
|
||||
return;
|
||||
@ -833,7 +922,7 @@ const DownloadsData = {
|
||||
}
|
||||
|
||||
// Show the panel in the most recent browser window, if present.
|
||||
let browserWin = gBrowserGlue.getMostRecentBrowserWindow();
|
||||
let browserWin = RecentWindow.getMostRecentBrowserWindow({ private: this._isPrivate });
|
||||
if (!browserWin) {
|
||||
return;
|
||||
}
|
||||
@ -850,6 +939,14 @@ const DownloadsData = {
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsData", function() {
|
||||
return new DownloadsDataCtor(true);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "DownloadsData", function() {
|
||||
return new DownloadsDataCtor(false);
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsDataItem
|
||||
|
||||
@ -1116,8 +1213,18 @@ const DownloadsViewPrototype = {
|
||||
/**
|
||||
* Array of view objects that should be notified when the available status
|
||||
* data changes.
|
||||
*
|
||||
* SUBCLASSES MUST OVERRIDE THIS PROPERTY.
|
||||
*/
|
||||
_views: [],
|
||||
_views: null,
|
||||
|
||||
/**
|
||||
* Determines whether this view object is over the private or non-private
|
||||
* downloads.
|
||||
*
|
||||
* SUBCLASSES MUST OVERRIDE THIS PROPERTY.
|
||||
*/
|
||||
_isPrivate: false,
|
||||
|
||||
/**
|
||||
* Adds an object to be notified when the available status data changes.
|
||||
@ -1131,7 +1238,11 @@ const DownloadsViewPrototype = {
|
||||
{
|
||||
// Start receiving events when the first of our views is registered.
|
||||
if (this._views.length == 0) {
|
||||
DownloadsCommon.data.addView(this);
|
||||
if (this._isPrivate) {
|
||||
PrivateDownloadsData.addView(this);
|
||||
} else {
|
||||
DownloadsData.addView(this);
|
||||
}
|
||||
}
|
||||
|
||||
this._views.push(aView);
|
||||
@ -1167,7 +1278,11 @@ const DownloadsViewPrototype = {
|
||||
|
||||
// Stop receiving events when the last of our views is unregistered.
|
||||
if (this._views.length == 0) {
|
||||
DownloadsCommon.data.removeView(this);
|
||||
if (this._isPrivate) {
|
||||
PrivateDownloadsData.removeView(this);
|
||||
} else {
|
||||
DownloadsData.removeView(this);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -1291,7 +1406,10 @@ const DownloadsViewPrototype = {
|
||||
* actually started. This is useful to display a neutral progress indicator in
|
||||
* the main browser window until the autostart timeout elapses.
|
||||
*/
|
||||
const DownloadsIndicatorData = {
|
||||
function DownloadsIndicatorDataCtor(aPrivate) {
|
||||
this._isPrivate = aPrivate;
|
||||
}
|
||||
DownloadsIndicatorDataCtor.prototype = {
|
||||
__proto__: DownloadsViewPrototype,
|
||||
|
||||
/**
|
||||
@ -1373,23 +1491,25 @@ const DownloadsIndicatorData = {
|
||||
*/
|
||||
getViewItem: function DID_getViewItem(aDataItem)
|
||||
{
|
||||
let data = this._isPrivate ? PrivateDownloadsIndicatorData
|
||||
: DownloadsIndicatorData;
|
||||
return Object.freeze({
|
||||
onStateChange: function DIVI_onStateChange()
|
||||
{
|
||||
if (aDataItem.state == nsIDM.DOWNLOAD_FINISHED ||
|
||||
aDataItem.state == nsIDM.DOWNLOAD_FAILED) {
|
||||
DownloadsIndicatorData.attention = true;
|
||||
data.attention = true;
|
||||
}
|
||||
|
||||
// Since the state of a download changed, reset the estimated time left.
|
||||
DownloadsIndicatorData._lastRawTimeLeft = -1;
|
||||
DownloadsIndicatorData._lastTimeLeft = -1;
|
||||
data._lastRawTimeLeft = -1;
|
||||
data._lastTimeLeft = -1;
|
||||
|
||||
DownloadsIndicatorData._updateViews();
|
||||
data._updateViews();
|
||||
},
|
||||
onProgressChange: function DIVI_onProgressChange()
|
||||
{
|
||||
DownloadsIndicatorData._updateViews();
|
||||
data._updateViews();
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -1489,7 +1609,9 @@ const DownloadsIndicatorData = {
|
||||
*/
|
||||
_activeDataItems: function DID_activeDataItems()
|
||||
{
|
||||
for each (let dataItem in DownloadsCommon.data.dataItems) {
|
||||
let dataItems = this._isPrivate ? PrivateDownloadsData.dataItems
|
||||
: DownloadsData.dataItems;
|
||||
for each (let dataItem in dataItems) {
|
||||
if (dataItem && dataItem.inProgress) {
|
||||
yield dataItem;
|
||||
}
|
||||
@ -1529,7 +1651,15 @@ const DownloadsIndicatorData = {
|
||||
this._counter = DownloadsCommon.formatTimeLeft(this._lastTimeLeft);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "PrivateDownloadsIndicatorData", function() {
|
||||
return new DownloadsIndicatorDataCtor(true);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "DownloadsIndicatorData", function() {
|
||||
return new DownloadsIndicatorDataCtor(false);
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// DownloadsSummaryData
|
||||
@ -1541,16 +1671,18 @@ const DownloadsIndicatorData = {
|
||||
* constructed with aNumToExclude equal to 3, then that DownloadsSummaryData
|
||||
* would produce a summary of the last 2 downloads.
|
||||
*
|
||||
* @param aIsPrivate
|
||||
* True if the browser window which owns the download button is a private
|
||||
* window.
|
||||
* @param aNumToExclude
|
||||
* The number of items to exclude from the summary, starting from the
|
||||
* top of the list.
|
||||
*/
|
||||
function DownloadsSummaryData(aNumToExclude) {
|
||||
function DownloadsSummaryData(aIsPrivate, aNumToExclude) {
|
||||
this._numToExclude = aNumToExclude;
|
||||
// Since we can have multiple instances of DownloadsSummaryData, we
|
||||
// override these values from the prototype so that each instance can be
|
||||
// completely separated from one another.
|
||||
this._views = [];
|
||||
this._loading = false;
|
||||
|
||||
this._dataItems = [];
|
||||
@ -1574,6 +1706,9 @@ function DownloadsSummaryData(aNumToExclude) {
|
||||
this._description = "";
|
||||
this._numActive = 0;
|
||||
this._percentComplete = -1;
|
||||
|
||||
this._isPrivate = aIsPrivate;
|
||||
this._views = [];
|
||||
}
|
||||
|
||||
DownloadsSummaryData.prototype = {
|
||||
|
@ -31,16 +31,22 @@ XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gSessionStartup",
|
||||
"@mozilla.org/browser/sessionstartup;1",
|
||||
"nsISessionStartup");
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "gPrivateBrowsingService",
|
||||
"@mozilla.org/privatebrowsing;1",
|
||||
"nsIPrivateBrowsingService");
|
||||
#endif
|
||||
|
||||
const kObservedTopics = [
|
||||
"sessionstore-windows-restored",
|
||||
"sessionstore-browser-state-restored",
|
||||
"download-manager-initialized",
|
||||
"download-manager-change-retention",
|
||||
#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
"last-pb-context-exited",
|
||||
#else
|
||||
"private-browsing-transition-complete",
|
||||
#endif
|
||||
"browser-lastwindow-close-granted",
|
||||
"quit-application",
|
||||
"profile-change-teardown",
|
||||
@ -113,8 +119,8 @@ DownloadsStartup.prototype = {
|
||||
// Start receiving events for active and new downloads before we return
|
||||
// from this observer function. We can't defer the execution of this
|
||||
// step, to ensure that we don't lose events raised in the meantime.
|
||||
DownloadsCommon.data.initializeDataLink(
|
||||
aSubject.QueryInterface(Ci.nsIDownloadManager));
|
||||
DownloadsCommon.initializeAllDataLinks(
|
||||
aSubject.QueryInterface(Ci.nsIDownloadManager));
|
||||
|
||||
this._downloadsServiceInitialized = true;
|
||||
|
||||
@ -139,11 +145,13 @@ DownloadsStartup.prototype = {
|
||||
}
|
||||
break;
|
||||
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
case "private-browsing-transition-complete":
|
||||
// Ensure that persistent data is reloaded only when the database
|
||||
// connection is available again.
|
||||
this._ensureDataLoaded();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case "browser-lastwindow-close-granted":
|
||||
// When using the panel interface, downloads that are already completed
|
||||
@ -158,6 +166,16 @@ DownloadsStartup.prototype = {
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
case "last-pb-context-exited":
|
||||
// Similar to the above notification, but for private downloads.
|
||||
if (this._downloadsServiceInitialized &&
|
||||
!DownloadsCommon.useToolkitUI) {
|
||||
Services.downloads.cleanUpPrivate();
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case "quit-application":
|
||||
// When the application is shutting down, we must free all resources in
|
||||
// addition to cleaning up completed downloads. If the Download Manager
|
||||
@ -169,7 +187,7 @@ DownloadsStartup.prototype = {
|
||||
break;
|
||||
}
|
||||
|
||||
DownloadsCommon.data.terminateDataLink();
|
||||
DownloadsCommon.terminateAllDataLinks();
|
||||
|
||||
// When using the panel interface, downloads that are already completed
|
||||
// should be removed when quitting the application.
|
||||
@ -258,15 +276,18 @@ DownloadsStartup.prototype = {
|
||||
*/
|
||||
_ensureDataLoaded: function DS_ensureDataLoaded()
|
||||
{
|
||||
if (!this._downloadsServiceInitialized ||
|
||||
gPrivateBrowsingService.privateBrowsingEnabled) {
|
||||
if (!this._downloadsServiceInitialized
|
||||
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
|| gPrivateBrowsingService.privateBrowsingEnabled
|
||||
#endif
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the previous session has been already restored, then we ensure that
|
||||
// all the downloads are loaded. Otherwise, we only ensure that the active
|
||||
// downloads from the previous session are loaded.
|
||||
DownloadsCommon.data.ensurePersistentDataLoaded(!this._recoverAllDownloads);
|
||||
DownloadsCommon.ensureAllPersistentDataLoaded(!this._recoverAllDownloads);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,11 +11,14 @@ include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
EXTRA_COMPONENTS = \
|
||||
BrowserDownloads.manifest \
|
||||
DownloadsStartup.js \
|
||||
DownloadsUI.js \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_JS_MODULES = \
|
||||
EXTRA_PP_COMPONENTS = \
|
||||
DownloadsStartup.js \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_JS_MODULES = \
|
||||
DownloadsCommon.jsm \
|
||||
$(NULL)
|
||||
|
||||
|
@ -33,13 +33,13 @@ function gen_test()
|
||||
|
||||
try {
|
||||
// Ensure that state is reset in case previous tests didn't finish.
|
||||
for (let yy in gen_resetState()) yield;
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
|
||||
|
||||
// Populate the downloads database with the data required by this test.
|
||||
for (let yy in gen_addDownloadRows(DownloadData)) yield;
|
||||
|
||||
// Open the user interface and wait for data to be fully loaded.
|
||||
for (let yy in gen_openPanel()) yield;
|
||||
for (let yy in gen_openPanel(DownloadsCommon.getData(window))) yield;
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let richlistbox = document.getElementById("downloadsListBox");
|
||||
@ -57,6 +57,6 @@ function gen_test()
|
||||
}
|
||||
} finally {
|
||||
// Clean up when the test finishes.
|
||||
for (let yy in gen_resetState()) yield;
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
|
||||
}
|
||||
}
|
||||
|
@ -12,32 +12,32 @@ function gen_test()
|
||||
{
|
||||
try {
|
||||
// Ensure that state is reset in case previous tests didn't finish.
|
||||
for (let yy in gen_resetState()) yield;
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
|
||||
|
||||
// With this set to false, we should automatically open the panel
|
||||
// the first time a download is started.
|
||||
DownloadsCommon.data.panelHasShownBefore = false;
|
||||
DownloadsCommon.getData(window).panelHasShownBefore = false;
|
||||
|
||||
prepareForPanelOpen();
|
||||
DownloadsCommon.data._notifyNewDownload();
|
||||
DownloadsCommon.getData(window)._notifyNewDownload();
|
||||
yield;
|
||||
|
||||
// If we got here, that means the panel opened.
|
||||
DownloadsPanel.hidePanel();
|
||||
|
||||
ok(DownloadsCommon.data.panelHasShownBefore,
|
||||
ok(DownloadsCommon.getData(window).panelHasShownBefore,
|
||||
"Should have recorded that the panel was opened on a download.")
|
||||
|
||||
// Next, make sure that if we start another download, we don't open
|
||||
// the panel automatically.
|
||||
panelShouldNotOpen();
|
||||
DownloadsCommon.data._notifyNewDownload();
|
||||
DownloadsCommon.getData(window)._notifyNewDownload();
|
||||
yield waitFor(2);
|
||||
} catch(e) {
|
||||
ok(false, e);
|
||||
} finally {
|
||||
// Clean up when the test finishes.
|
||||
for (let yy in gen_resetState()) yield;
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ var testRunner = {
|
||||
// for (let yy in gen_example("Parameter")) yield;
|
||||
//
|
||||
|
||||
function gen_resetState()
|
||||
function gen_resetState(aData)
|
||||
{
|
||||
let statement = Services.downloads.DBConnection.createAsyncStatement(
|
||||
"DELETE FROM moz_downloads");
|
||||
@ -155,8 +155,8 @@ function gen_resetState()
|
||||
Services.prefs.clearUserPref("browser.download.panel.shown");
|
||||
|
||||
// Ensure that the panel is closed and data is unloaded.
|
||||
DownloadsCommon.data.clear();
|
||||
DownloadsCommon.data._loadState = DownloadsCommon.data.kLoadNone;
|
||||
aData.clear();
|
||||
aData._loadState = aData.kLoadNone;
|
||||
DownloadsPanel.hidePanel();
|
||||
|
||||
// Wait for focus on the main window.
|
||||
@ -224,7 +224,7 @@ function gen_openPanel(aData)
|
||||
};
|
||||
|
||||
// Start loading all the downloads from the database asynchronously.
|
||||
DownloadsCommon.data.ensurePersistentDataLoaded(false);
|
||||
aData.ensurePersistentDataLoaded(false);
|
||||
|
||||
// Wait for focus on the main window.
|
||||
waitForFocus(testRunner.continueTest);
|
||||
|
@ -53,6 +53,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "KeywordURLResetPrompter",
|
||||
"resource:///modules/KeywordURLResetPrompter.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "RecentWindow",
|
||||
"resource:///modules/RecentWindow.jsm");
|
||||
|
||||
const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
|
||||
const PREF_PLUGINS_UPDATEURL = "plugins.update.url";
|
||||
|
||||
@ -1584,41 +1587,9 @@ BrowserGlue.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
#ifndef XP_WIN
|
||||
#define BROKEN_WM_Z_ORDER
|
||||
#endif
|
||||
|
||||
// this returns the most recent non-popup browser window
|
||||
getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() {
|
||||
function isFullBrowserWindow(win) {
|
||||
return !win.closed &&
|
||||
win.toolbar.visible;
|
||||
}
|
||||
|
||||
#ifdef BROKEN_WM_Z_ORDER
|
||||
var win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
// if we're lucky, this isn't a popup, and we can just return this
|
||||
if (win && !isFullBrowserWindow(win)) {
|
||||
win = null;
|
||||
let windowList = Services.wm.getEnumerator("navigator:browser");
|
||||
// this is oldest to newest, so this gets a bit ugly
|
||||
while (windowList.hasMoreElements()) {
|
||||
let nextWin = windowList.getNext();
|
||||
if (isFullBrowserWindow(nextWin))
|
||||
win = nextWin;
|
||||
}
|
||||
}
|
||||
return win;
|
||||
#else
|
||||
var windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
|
||||
while (windowList.hasMoreElements()) {
|
||||
let win = windowList.getNext();
|
||||
if (isFullBrowserWindow(win))
|
||||
return win;
|
||||
}
|
||||
return null;
|
||||
#endif
|
||||
return RecentWindow.getMostRecentBrowserWindow();
|
||||
},
|
||||
|
||||
#ifdef MOZ_SERVICES_SYNC
|
||||
|
@ -28,11 +28,13 @@ EXTRA_JS_MODULES = \
|
||||
KeywordURLResetPrompter.jsm \
|
||||
$(NULL)
|
||||
|
||||
EXTRA_PP_JS_MODULES = RecentWindow.jsm
|
||||
|
||||
ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
|
||||
EXTRA_JS_MODULES += \
|
||||
WindowsPreviewPerTab.jsm \
|
||||
$(NULL)
|
||||
EXTRA_PP_JS_MODULES = \
|
||||
EXTRA_PP_JS_MODULES += \
|
||||
WindowsJumpLists.jsm \
|
||||
$(NULL)
|
||||
endif
|
||||
|
65
browser/modules/RecentWindow.jsm
Normal file
65
browser/modules/RecentWindow.jsm
Normal file
@ -0,0 +1,65 @@
|
||||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["RecentWindow"];
|
||||
|
||||
const Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
|
||||
|
||||
#ifndef XP_WIN
|
||||
#define BROKEN_WM_Z_ORDER
|
||||
#endif
|
||||
|
||||
this.RecentWindow = {
|
||||
/*
|
||||
* Get the most recent browser window.
|
||||
*
|
||||
* @param aOptions an object accepting the arguments for the search.
|
||||
* Set the private property to true in order to restrict the
|
||||
* search to private windows only, or to false in order to
|
||||
* restrict the search to non-private windows only. To search
|
||||
* in both groups, don't specify the private property.
|
||||
*/
|
||||
getMostRecentBrowserWindow: function RW_getMostRecentBrowserWindow(aOptions) {
|
||||
let checkPrivacy = typeof aOptions == "object" &&
|
||||
"private" in aOptions;
|
||||
|
||||
function isSuitableBrowserWindow(win) {
|
||||
return (!win.closed &&
|
||||
win.toolbar.visible &&
|
||||
(!checkPrivacy ||
|
||||
PrivateBrowsingUtils.isWindowPrivate(win) == aOptions.private));
|
||||
}
|
||||
|
||||
#ifdef BROKEN_WM_Z_ORDER
|
||||
let win = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
|
||||
// if we're lucky, this isn't a popup, and we can just return this
|
||||
if (win && !isSuitableBrowserWindow(win)) {
|
||||
win = null;
|
||||
let windowList = Services.wm.getEnumerator("navigator:browser");
|
||||
// this is oldest to newest, so this gets a bit ugly
|
||||
while (windowList.hasMoreElements()) {
|
||||
let nextWin = windowList.getNext();
|
||||
if (isSuitableBrowserWindow(nextWin))
|
||||
win = nextWin;
|
||||
}
|
||||
}
|
||||
return win;
|
||||
#else
|
||||
let windowList = Services.wm.getZOrderDOMWindowEnumerator("navigator:browser", true);
|
||||
while (windowList.hasMoreElements()) {
|
||||
let win = windowList.getNext();
|
||||
if (isSuitableBrowserWindow(win))
|
||||
return win;
|
||||
}
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
@ -35,8 +35,12 @@ case "$target" in
|
||||
;;
|
||||
*-darwin*)
|
||||
# GCC on darwin is based on gcc 4.2 and we don't support it anymore.
|
||||
MOZ_PATH_PROGS(CC, $CC clang)
|
||||
MOZ_PATH_PROGS(CXX, $CXX clang++)
|
||||
if test -z "$CC"; then
|
||||
MOZ_PATH_PROGS(CC, clang)
|
||||
fi
|
||||
if test -z "$CXX"; then
|
||||
MOZ_PATH_PROGS(CXX, clang++)
|
||||
fi
|
||||
IS_GCC=$($CC -v 2>&1 | grep gcc)
|
||||
if test -n "$IS_GCC"
|
||||
then
|
||||
|
@ -18,6 +18,8 @@ endif
|
||||
topsrcdir = $(TOPSRCDIR)
|
||||
include $(OBJDIR)/config/autoconf.mk
|
||||
|
||||
core_abspath = $(if $(filter /%,$(1)),$(1),$(CURDIR)/$(1))
|
||||
|
||||
DIST = $(OBJDIR)/dist
|
||||
|
||||
ifdef LIBXUL_SDK # {
|
||||
@ -72,7 +74,7 @@ postflight_all:
|
||||
$(DIST_ARCH_2)/$(MOZ_PKG_APPNAME)/$(APPNAME)/$(BUILDCONFIG)
|
||||
mkdir -p $(DIST_UNI)/$(MOZ_PKG_APPNAME)
|
||||
rm -f $(DIST_ARCH_2)/universal
|
||||
ln -s $(DIST_UNI) $(DIST_ARCH_2)/universal
|
||||
ln -s $(call core_abspath,$(DIST_UNI)) $(DIST_ARCH_2)/universal
|
||||
rm -rf $(DIST_UNI)/$(MOZ_PKG_APPNAME)/$(APPNAME)
|
||||
$(TOPSRCDIR)/build/macosx/universal/unify \
|
||||
--unify-with-sort "\.manifest$$" \
|
||||
|
@ -1639,7 +1639,11 @@ MOZ_ARG_ENABLE_BOOL(profiling,
|
||||
|
||||
# For profiling builds keep the symbol information
|
||||
if test "$MOZ_PROFILING" -a -z "$STRIP_FLAGS"; then
|
||||
STRIP_FLAGS="--strip-debug"
|
||||
case "$OS_TARGET" in
|
||||
Linux|DragonFly|FreeBSD|NetBSD|OpenBSD)
|
||||
STRIP_FLAGS="--strip-debug"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
dnl ========================================================
|
||||
|
@ -35,8 +35,12 @@ case "$target" in
|
||||
;;
|
||||
*-darwin*)
|
||||
# GCC on darwin is based on gcc 4.2 and we don't support it anymore.
|
||||
MOZ_PATH_PROGS(CC, $CC clang)
|
||||
MOZ_PATH_PROGS(CXX, $CXX clang++)
|
||||
if test -z "$CC"; then
|
||||
MOZ_PATH_PROGS(CC, clang)
|
||||
fi
|
||||
if test -z "$CXX"; then
|
||||
MOZ_PATH_PROGS(CXX, clang++)
|
||||
fi
|
||||
IS_GCC=$($CC -v 2>&1 | grep gcc)
|
||||
if test -n "$IS_GCC"
|
||||
then
|
||||
|
@ -21,6 +21,9 @@ from mach.decorators import (
|
||||
)
|
||||
|
||||
|
||||
DEBUGGER_HELP = 'Debugger binary to run test in. Program name or path.'
|
||||
|
||||
|
||||
class ReftestRunner(MozbuildObject):
|
||||
"""Easily run reftests.
|
||||
|
||||
@ -51,7 +54,8 @@ class ReftestRunner(MozbuildObject):
|
||||
def _make_shell_string(self, s):
|
||||
return "'%s'" % re.sub("'", r"'\''", s)
|
||||
|
||||
def run_reftest_test(self, test_file=None, filter=None, suite=None):
|
||||
def run_reftest_test(self, test_file=None, filter=None, suite=None,
|
||||
debugger=None):
|
||||
"""Runs a reftest.
|
||||
|
||||
test_file is a path to a test file. It can be a relative path from the
|
||||
@ -63,27 +67,39 @@ class ReftestRunner(MozbuildObject):
|
||||
|
||||
suite is the type of reftest to run. It can be one of ('reftest',
|
||||
'crashtest').
|
||||
|
||||
debugger is the program name (in $PATH) or the full path of the
|
||||
debugger to run.
|
||||
"""
|
||||
|
||||
if suite not in ('reftest', 'crashtest'):
|
||||
raise Exception('None or unrecognized reftest suite type.')
|
||||
|
||||
env = {}
|
||||
extra_args = []
|
||||
|
||||
if test_file:
|
||||
path = self._find_manifest(suite, test_file)
|
||||
if not os.path.exists(path):
|
||||
raise Exception('No manifest file was found at %s.' % path)
|
||||
env[b'TEST_PATH'] = path
|
||||
if filter:
|
||||
if b'EXTRA_TEST_ARGS' in os.environ:
|
||||
env[b'EXTRA_TEST_ARGS'] = os.environ[b'EXTRA_TEST_ARGS'] + ' '
|
||||
else:
|
||||
env[b'EXTRA_TEST_ARGS'] = ' '
|
||||
env[b'EXTRA_TEST_ARGS'] += ("--filter %s" %
|
||||
self._make_shell_string(filter))
|
||||
extra_args.extend(['--filter', self._make_shell_string(filter)])
|
||||
|
||||
pass_thru = False
|
||||
|
||||
if debugger:
|
||||
extra_args.append('--debugger=%s' % debugger)
|
||||
pass_thru = True
|
||||
|
||||
if extra_args:
|
||||
args = [os.environ.get(b'EXTRA_TEST_ARGS', '')]
|
||||
args.extend(extra_args)
|
||||
env[b'EXTRA_TEST_ARGS'] = ' '.join(args)
|
||||
|
||||
# TODO hook up harness via native Python
|
||||
self._run_make(directory='.', target=suite, append_env=env)
|
||||
return self._run_make(directory='.', target=suite, append_env=env,
|
||||
pass_thru=pass_thru, ensure_exit_code=False)
|
||||
|
||||
|
||||
@CommandProvider
|
||||
@ -95,18 +111,23 @@ class MachCommands(MachCommandBase):
|
||||
@CommandArgument('--filter', default=None, metavar='REGEX',
|
||||
help='A JS regular expression to match test URLs against, to select '
|
||||
'a subset of tests to run.')
|
||||
def run_reftest(self, test_file, filter):
|
||||
self._run_reftest(test_file, filter=filter, suite='reftest')
|
||||
@CommandArgument('--debugger', metavar='DEBUGGER', help=DEBUGGER_HELP)
|
||||
def run_reftest(self, test_file, filter, debugger=None):
|
||||
return self._run_reftest(test_file, filter=filter, suite='reftest',
|
||||
debugger=debugger)
|
||||
|
||||
@Command('crashtest', help='Run a crashtest.')
|
||||
@CommandArgument('test_file', default=None, nargs='?', metavar='MANIFEST',
|
||||
help='Crashtest manifest file, or a direction in which to select '
|
||||
'crashtests.list.')
|
||||
def run_crashtest(self, test_file):
|
||||
self._run_reftest(test_file, suite='crashtest')
|
||||
@CommandArgument('--debugger', metavar='DEBUGGER', help=DEBUGGER_HELP)
|
||||
def run_crashtest(self, test_file, debugger=None):
|
||||
return self._run_reftest(test_file, suite='crashtest',
|
||||
debugger=debugger)
|
||||
|
||||
def _run_reftest(self, test_file, filter, suite):
|
||||
def _run_reftest(self, test_file, filter, suite, debugger=None):
|
||||
reftest = self._spawn(ReftestRunner)
|
||||
reftest.run_reftest_test(test_file, filter, suite)
|
||||
return reftest.run_reftest_test(test_file, filter, suite,
|
||||
debugger=debugger)
|
||||
|
||||
|
||||
|
@ -32,6 +32,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
|
||||
MOCHITEST_BROWSER_FILES += \
|
||||
browser_bug248970.js \
|
||||
browser_favicon_setAndFetchFaviconForPage.js \
|
||||
$(NULL)
|
||||
else
|
||||
MOCHITEST_BROWSER_FILES += \
|
||||
|
@ -0,0 +1,145 @@
|
||||
/* 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/. */
|
||||
|
||||
// This file tests the normal operation of setAndFetchFaviconForPage.
|
||||
function test() {
|
||||
// Initialization
|
||||
waitForExplicitFinish();
|
||||
let windowsToClose = [];
|
||||
let testURI = "https://www.mozilla.org/en-US/";
|
||||
let favIconLocation =
|
||||
"http://example.org/tests/toolkit/components/places/tests/browser/favicon-normal32.png";
|
||||
let favIconURI = NetUtil.newURI(favIconLocation);
|
||||
let favIconMimeType= "image/png";
|
||||
let pageURI;
|
||||
let favIconData;
|
||||
|
||||
function testOnWindow(aOptions, aCallback) {
|
||||
whenNewWindowLoaded(aOptions, function(aWin) {
|
||||
windowsToClose.push(aWin);
|
||||
executeSoon(function() aCallback(aWin));
|
||||
});
|
||||
};
|
||||
|
||||
// This function is called after calling finish() on the test.
|
||||
registerCleanupFunction(function() {
|
||||
windowsToClose.forEach(function(aWin) {
|
||||
aWin.close();
|
||||
});
|
||||
});
|
||||
|
||||
function getIconFile(aCallback) {
|
||||
NetUtil.asyncFetch(favIconLocation, function(inputStream, status) {
|
||||
if (!Components.isSuccessCode(status)) {
|
||||
ok(false, "Could not get the icon file");
|
||||
// Handle error.
|
||||
return;
|
||||
}
|
||||
|
||||
// Check the returned size versus the expected size.
|
||||
let size = inputStream.available();
|
||||
favIconData = NetUtil.readInputStreamToString(inputStream, size);
|
||||
is(size, favIconData.length, "Check correct icon size");
|
||||
// Check that the favicon loaded correctly before starting the actual tests.
|
||||
is(favIconData.length, 344, "Check correct icon length (344)");
|
||||
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
} else {
|
||||
finish();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function testNormal(aWindow, aCallback) {
|
||||
pageURI = NetUtil.newURI("http://example.com/normal");
|
||||
waitForFaviconChanged(pageURI, favIconURI, aWindow,
|
||||
function testNormalCallback() {
|
||||
checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow,
|
||||
aCallback);
|
||||
}
|
||||
);
|
||||
|
||||
addVisits({uri: pageURI, transition: TRANSITION_TYPED}, aWindow,
|
||||
function () {
|
||||
aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI,
|
||||
favIconURI, true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function testAboutURIBookmarked(aWindow, aCallback) {
|
||||
pageURI = NetUtil.newURI("about:testAboutURI_bookmarked");
|
||||
waitForFaviconChanged(pageURI, favIconURI, aWindow,
|
||||
function testAboutURIBookmarkedCallback() {
|
||||
checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow,
|
||||
aCallback);
|
||||
}
|
||||
);
|
||||
|
||||
aWindow.PlacesUtils.bookmarks.insertBookmark(
|
||||
aWindow.PlacesUtils.unfiledBookmarksFolderId, pageURI,
|
||||
aWindow.PlacesUtils.bookmarks.DEFAULT_INDEX, pageURI.spec);
|
||||
aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI,
|
||||
true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
|
||||
}
|
||||
|
||||
function testPrivateBrowsingBookmarked(aWindow, aCallback) {
|
||||
pageURI = NetUtil.newURI("http://example.com/privateBrowsing_bookmarked");
|
||||
waitForFaviconChanged(pageURI, favIconURI, aWindow,
|
||||
function testPrivateBrowsingBookmarkedCallback() {
|
||||
checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow,
|
||||
aCallback);
|
||||
}
|
||||
);
|
||||
|
||||
aWindow.PlacesUtils.bookmarks.insertBookmark(
|
||||
aWindow.PlacesUtils.unfiledBookmarksFolderId, pageURI,
|
||||
aWindow.PlacesUtils.bookmarks.DEFAULT_INDEX, pageURI.spec);
|
||||
aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI,
|
||||
true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_PRIVATE);
|
||||
}
|
||||
|
||||
function testDisabledHistoryBookmarked(aWindow, aCallback) {
|
||||
pageURI = NetUtil.newURI("http://example.com/disabledHistory_bookmarked");
|
||||
waitForFaviconChanged(pageURI, favIconURI, aWindow,
|
||||
function testDisabledHistoryBookmarkedCallback() {
|
||||
checkFaviconDataForPage(pageURI, favIconMimeType, favIconData, aWindow,
|
||||
aCallback);
|
||||
}
|
||||
);
|
||||
|
||||
// Disable history while changing the favicon.
|
||||
aWindow.Services.prefs.setBoolPref("places.history.enabled", false);
|
||||
|
||||
aWindow.PlacesUtils.bookmarks.insertBookmark(
|
||||
aWindow.PlacesUtils.unfiledBookmarksFolderId, pageURI,
|
||||
aWindow.PlacesUtils.bookmarks.DEFAULT_INDEX, pageURI.spec);
|
||||
aWindow.PlacesUtils.favicons.setAndFetchFaviconForPage(pageURI, favIconURI,
|
||||
true, aWindow.PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE);
|
||||
|
||||
// The setAndFetchFaviconForPage function calls CanAddURI synchronously, thus
|
||||
// we can set the preference back to true immediately. We don't clear the
|
||||
// preference because not all products enable Places by default.
|
||||
aWindow.Services.prefs.setBoolPref("places.history.enabled", true);
|
||||
}
|
||||
|
||||
getIconFile(function () {
|
||||
testOnWindow({}, function(aWin) {
|
||||
testNormal(aWin, function () {
|
||||
testOnWindow({}, function(aWin) {
|
||||
testAboutURIBookmarked(aWin, function () {
|
||||
testOnWindow({private: true}, function(aWin) {
|
||||
testPrivateBrowsingBookmarked(aWin, function () {
|
||||
testOnWindow({}, function(aWin) {
|
||||
testDisabledHistoryBookmarked(aWin, finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
const TRANSITION_LINK = Ci.nsINavHistoryService.TRANSITION_LINK;
|
||||
const TRANSITION_TYPED = Ci.nsINavHistoryService.TRANSITION_TYPED;
|
||||
|
||||
Components.utils.import("resource://gre/modules/NetUtil.jsm");
|
||||
|
||||
@ -112,3 +114,235 @@ function fieldForUrl(aURI, aFieldName, aCallback)
|
||||
});
|
||||
stmt.finalize();
|
||||
}
|
||||
|
||||
function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
let win = OpenBrowserWindow(aOptions);
|
||||
win.addEventListener("load", function onLoad() {
|
||||
win.removeEventListener("load", onLoad, false);
|
||||
aCallback(win);
|
||||
}, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic nsINavHistoryObserver that doesn't implement anything, but provides
|
||||
* dummy methods to prevent errors about an object not having a certain method.
|
||||
*/
|
||||
function NavHistoryObserver() {}
|
||||
|
||||
NavHistoryObserver.prototype = {
|
||||
onBeginUpdateBatch: function () {},
|
||||
onEndUpdateBatch: function () {},
|
||||
onVisit: function () {},
|
||||
onTitleChanged: function () {},
|
||||
onBeforeDeleteURI: function () {},
|
||||
onDeleteURI: function () {},
|
||||
onClearHistory: function () {},
|
||||
onPageChanged: function () {},
|
||||
onDeleteVisits: function () {},
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsINavHistoryObserver,
|
||||
])
|
||||
};
|
||||
|
||||
/**
|
||||
* Waits for the first OnPageChanged notification for ATTRIBUTE_FAVICON, and
|
||||
* verifies that it matches the expected page URI and associated favicon URI.
|
||||
*
|
||||
* This function also double-checks the GUID parameter of the notification.
|
||||
*
|
||||
* @param aExpectedPageURI
|
||||
* nsIURI object of the page whose favicon should change.
|
||||
* @param aExpectedFaviconURI
|
||||
* nsIURI object of the newly associated favicon.
|
||||
* @param aCallback
|
||||
* This function is called after the check finished.
|
||||
*/
|
||||
function waitForFaviconChanged(aExpectedPageURI, aExpectedFaviconURI, aWindow,
|
||||
aCallback) {
|
||||
let historyObserver = {
|
||||
__proto__: NavHistoryObserver.prototype,
|
||||
onPageChanged: function WFFC_onPageChanged(aURI, aWhat, aValue, aGUID) {
|
||||
if (aWhat != Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON) {
|
||||
return;
|
||||
}
|
||||
aWindow.PlacesUtils.history.removeObserver(this);
|
||||
|
||||
ok(aURI.equals(aExpectedPageURI),
|
||||
"Check URIs are equal for the page which favicon changed");
|
||||
is(aValue, aExpectedFaviconURI.spec,
|
||||
"Check changed favicon URI is the expected");
|
||||
checkGuidForURI(aURI, aGUID);
|
||||
|
||||
if (aCallback) {
|
||||
aCallback();
|
||||
}
|
||||
}
|
||||
};
|
||||
aWindow.PlacesUtils.history.addObserver(historyObserver, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronously adds visits to a page, invoking a callback function when done.
|
||||
*
|
||||
* @param aPlaceInfo
|
||||
* Can be an nsIURI, in such a case a single LINK visit will be added.
|
||||
* Otherwise can be an object describing the visit to add, or an array
|
||||
* of these objects:
|
||||
* { uri: nsIURI of the page,
|
||||
* transition: one of the TRANSITION_* from nsINavHistoryService,
|
||||
* [optional] title: title of the page,
|
||||
* [optional] visitDate: visit date in microseconds from the epoch
|
||||
* [optional] referrer: nsIURI of the referrer for this visit
|
||||
* }
|
||||
* @param [optional] aCallback
|
||||
* Function to be invoked on completion.
|
||||
* @param [optional] aStack
|
||||
* The stack frame used to report errors.
|
||||
*/
|
||||
function addVisits(aPlaceInfo, aWindow, aCallback, aStack) {
|
||||
let stack = aStack || Components.stack.caller;
|
||||
let places = [];
|
||||
if (aPlaceInfo instanceof Ci.nsIURI) {
|
||||
places.push({ uri: aPlaceInfo });
|
||||
}
|
||||
else if (Array.isArray(aPlaceInfo)) {
|
||||
places = places.concat(aPlaceInfo);
|
||||
} else {
|
||||
places.push(aPlaceInfo)
|
||||
}
|
||||
|
||||
// Create mozIVisitInfo for each entry.
|
||||
let now = Date.now();
|
||||
for (let i = 0; i < places.length; i++) {
|
||||
if (!places[i].title) {
|
||||
places[i].title = "test visit for " + places[i].uri.spec;
|
||||
}
|
||||
places[i].visits = [{
|
||||
transitionType: places[i].transition === undefined ? TRANSITION_LINK
|
||||
: places[i].transition,
|
||||
visitDate: places[i].visitDate || (now++) * 1000,
|
||||
referrerURI: places[i].referrer
|
||||
}];
|
||||
}
|
||||
|
||||
aWindow.PlacesUtils.asyncHistory.updatePlaces(
|
||||
places,
|
||||
{
|
||||
handleError: function AAV_handleError() {
|
||||
throw("Unexpected error in adding visit.");
|
||||
},
|
||||
handleResult: function () {},
|
||||
handleCompletion: function UP_handleCompletion() {
|
||||
if (aCallback)
|
||||
aCallback();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the favicon for the given page matches the provided data.
|
||||
*
|
||||
* @param aPageURI
|
||||
* nsIURI object for the page to check.
|
||||
* @param aExpectedMimeType
|
||||
* Expected MIME type of the icon, for example "image/png".
|
||||
* @param aExpectedData
|
||||
* Expected icon data, expressed as an array of byte values.
|
||||
* @param aCallback
|
||||
* This function is called after the check finished.
|
||||
*/
|
||||
function checkFaviconDataForPage(aPageURI, aExpectedMimeType, aExpectedData,
|
||||
aWindow, aCallback) {
|
||||
aWindow.PlacesUtils.favicons.getFaviconDataForPage(aPageURI,
|
||||
function (aURI, aDataLen, aData, aMimeType) {
|
||||
is(aExpectedMimeType, aMimeType, "Check expected MimeType");
|
||||
is(aExpectedData.length, aData.length,
|
||||
"Check favicon data for the given page matches the provided data");
|
||||
checkGuidForURI(aPageURI);
|
||||
aCallback();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests that a guid was set in moz_places for a given uri.
|
||||
*
|
||||
* @param aURI
|
||||
* The uri to check.
|
||||
* @param [optional] aGUID
|
||||
* The expected guid in the database.
|
||||
*/
|
||||
function checkGuidForURI(aURI, aGUID) {
|
||||
let guid = doGetGuidForURI(aURI);
|
||||
if (aGUID) {
|
||||
doCheckValidPlacesGuid(aGUID);
|
||||
is(guid, aGUID, "Check equal guid for URIs");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the guid for a given uri.
|
||||
*
|
||||
* @param aURI
|
||||
* The uri to check.
|
||||
* @return the associated the guid.
|
||||
*/
|
||||
function doGetGuidForURI(aURI) {
|
||||
let stmt = DBConn().createStatement(
|
||||
"SELECT guid "
|
||||
+ "FROM moz_places "
|
||||
+ "WHERE url = :url "
|
||||
);
|
||||
stmt.params.url = aURI.spec;
|
||||
ok(stmt.executeStep(), "Check get guid for uri from moz_places");
|
||||
let guid = stmt.row.guid;
|
||||
stmt.finalize();
|
||||
doCheckValidPlacesGuid(guid);
|
||||
return guid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a given guid is valid for use in Places or not.
|
||||
*
|
||||
* @param aGuid
|
||||
* The guid to test.
|
||||
*/
|
||||
function doCheckValidPlacesGuid(aGuid) {
|
||||
ok(/^[a-zA-Z0-9\-_]{12}$/.test(aGuid), "Check guid for valid places");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the database connection. If the Places connection is invalid it will
|
||||
* try to create a new connection.
|
||||
*
|
||||
* @param [optional] aForceNewConnection
|
||||
* Forces creation of a new connection to the database. When a
|
||||
* connection is asyncClosed it cannot anymore schedule async statements,
|
||||
* though connectionReady will keep returning true (Bug 726990).
|
||||
*
|
||||
* @return The database connection or null if unable to get one.
|
||||
*/
|
||||
function DBConn(aForceNewConnection) {
|
||||
let gDBConn;
|
||||
if (!aForceNewConnection) {
|
||||
let db = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
|
||||
.DBConnection;
|
||||
if (db.connectionReady)
|
||||
return db;
|
||||
}
|
||||
|
||||
// If the Places database connection has been closed, create a new connection.
|
||||
if (!gDBConn || aForceNewConnection) {
|
||||
let file = Services.dirsvc.get('ProfD', Ci.nsIFile);
|
||||
file.append("places.sqlite");
|
||||
let dbConn = gDBConn = Services.storage.openDatabase(file);
|
||||
|
||||
// Be sure to cleanly close this connection.
|
||||
Services.obs.addObserver(function DBCloseCallback(aSubject, aTopic, aData) {
|
||||
Services.obs.removeObserver(DBCloseCallback, aTopic);
|
||||
dbConn.asyncClose();
|
||||
}, "profile-before-change", false);
|
||||
}
|
||||
|
||||
return gDBConn.connectionReady ? gDBConn : null;
|
||||
}
|
||||
|
@ -611,17 +611,21 @@ class Dumper_Linux(Dumper):
|
||||
return False
|
||||
|
||||
def CopyDebug(self, file, debug_file, guid):
|
||||
import zlib, struct, hashlib
|
||||
# We want to strip out the debug info, and add a
|
||||
# .gnu_debuglink section to the object, so the debugger can
|
||||
# actually load our debug info later.
|
||||
file_dbg = file + ".dbg"
|
||||
if subprocess.call([self.objcopy, '--only-keep-debug', file, file_dbg]) == 0 and \
|
||||
subprocess.call([self.objcopy, '--add-gnu-debuglink=%s' % file_dbg, file]) == 0:
|
||||
if subprocess.call([self.objcopy, '--only-keep-debug', file, file_dbg], stdout=sys.stderr) == 0 and \
|
||||
subprocess.call([self.objcopy, '--add-gnu-debuglink=%s' % file_dbg, file], stdout=sys.stderr) == 0:
|
||||
rel_path = os.path.join(debug_file,
|
||||
guid,
|
||||
debug_file + ".dbg")
|
||||
full_path = os.path.normpath(os.path.join(self.symbol_path,
|
||||
rel_path))
|
||||
# Temporary debug information
|
||||
print >>sys.stderr, read_output('objdump', '-j', '.gnu_debuglink', '-s', file)
|
||||
print >>sys.stderr, "%s crc: %08x" % (file_dbg, 0xffffffff & zlib.crc32(open(file_dbg).read()))
|
||||
shutil.move(file_dbg, full_path)
|
||||
# gzip the shipped debug files
|
||||
os.system("gzip %s" % full_path)
|
||||
|
Loading…
x
Reference in New Issue
Block a user