Relanding Bug 675902 - New Downloads view for Places Library. r=mak. Test fix contributed by Mike Conley (r=me). The new view is still disabled.

This commit is contained in:
Asaf Romano 2012-12-17 07:38:53 +00:00
Родитель 1da79b06c2
Коммит 26944959a9
17 изменённых файлов: 1744 добавлений и 267 удалений

Просмотреть файл

@ -1187,12 +1187,6 @@ function DownloadsViewItemController(aElement) {
}
DownloadsViewItemController.prototype = {
//////////////////////////////////////////////////////////////////////////////
//// Constants
get kPrefBdmAlertOnExeOpen() "browser.download.manager.alertOnEXEOpen",
get kPrefBdmScanWhenDone() "browser.download.manager.scanWhenDone",
//////////////////////////////////////////////////////////////////////////////
//// Command dispatching
@ -1244,87 +1238,17 @@ DownloadsViewItemController.prototype = {
commands: {
cmd_delete: function DVIC_cmd_delete()
{
this.dataItem.getDownload(function (aDownload) {
if (this.dataItem.inProgress) {
aDownload.cancel();
this._ensureLocalFileRemoved();
}
aDownload.remove();
}.bind(this));
this.dataItem.remove();
},
downloadsCmd_cancel: function DVIC_downloadsCmd_cancel()
{
if (this.dataItem.inProgress) {
this.dataItem.getDownload(function (aDownload) {
aDownload.cancel();
this._ensureLocalFileRemoved();
}.bind(this));
}
this.dataItem.cancel();
},
downloadsCmd_open: function DVIC_downloadsCmd_open()
{
// Confirm opening executable files if required.
let localFile = this.dataItem.localFile;
if (localFile.isExecutable()) {
let showAlert = true;
try {
showAlert = Services.prefs.getBoolPref(this.kPrefBdmAlertOnExeOpen);
} catch (ex) { }
// On Vista and above, we rely on native security prompting for
// downloaded content unless it's disabled.
if (DownloadsCommon.isWinVistaOrHigher) {
try {
if (Services.prefs.getBoolPref(this.kPrefBdmScanWhenDone)) {
showAlert = false;
}
} catch (ex) { }
}
if (showAlert) {
let name = this.dataItem.target;
let message =
DownloadsCommon.strings.fileExecutableSecurityWarning(name, name);
let title =
DownloadsCommon.strings.fileExecutableSecurityWarningTitle;
let dontAsk =
DownloadsCommon.strings.fileExecutableSecurityWarningDontAsk;
let checkbox = { value: false };
let open = Services.prompt.confirmCheck(window, title, message,
dontAsk, checkbox);
if (!open) {
return;
}
Services.prefs.setBoolPref(this.kPrefBdmAlertOnExeOpen,
!checkbox.value);
}
}
// Actually open the file.
this.dataItem.getDownload(function (aDownload) {
try {
let launched = false;
try {
let mimeInfo = aDownload.MIMEInfo;
if (mimeInfo.preferredAction == mimeInfo.useHelperApp) {
mimeInfo.launchWithFile(localFile);
launched = true;
}
} catch (ex) { }
if (!launched) {
localFile.launch();
}
} catch (ex) {
// If launch fails, try sending it through the system's external "file:"
// URL handler.
this._openExternal(localFile);
}
}.bind(this));
this.dataItem.openLocalFile();
// We explicitly close the panel here to give the user the feedback that
// their click has been received, and we're handling the action.
// Otherwise, we'd have to wait for the file-type handler to execute
@ -1335,26 +1259,7 @@ DownloadsViewItemController.prototype = {
downloadsCmd_show: function DVIC_downloadsCmd_show()
{
let localFile = this.dataItem.localFile;
try {
// Show the directory containing the file and select the file.
localFile.reveal();
} catch (ex) {
// If reveal fails for some reason (e.g., it's not implemented on unix
// or the file doesn't exist), try using the parent if we have it.
let parent = localFile.parent.QueryInterface(Ci.nsILocalFile);
if (parent) {
try {
// Open the parent directory to show where the file should be.
parent.launch();
} catch (ex) {
// If launch also fails (probably because it's not implemented), let
// the OS handler try to open the parent.
this._openExternal(parent);
}
}
}
this.dataItem.showLocalFile();
// We explicitly close the panel here to give the user the feedback that
// their click has been received, and we're handling the action.
@ -1366,20 +1271,12 @@ DownloadsViewItemController.prototype = {
downloadsCmd_pauseResume: function DVIC_downloadsCmd_pauseResume()
{
this.dataItem.getDownload(function (aDownload) {
if (this.dataItem.paused) {
aDownload.resume();
} else {
aDownload.pause();
}
}.bind(this));
this.dataItem.togglePauseResume();
},
downloadsCmd_retry: function DVIC_downloadsCmd_retry()
{
this.dataItem.getDownload(function (aDownload) {
aDownload.retry();
});
this.dataItem.retry();
},
downloadsCmd_openReferrer: function DVIC_downloadsCmd_openReferrer()
@ -1418,31 +1315,6 @@ DownloadsViewItemController.prototype = {
// Invoke the command.
this.doCommand(defaultCommand);
}
},
/**
* Support function to open the specified nsIFile.
*/
_openExternal: function DVIC_openExternal(aFile)
{
let protocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService);
protocolSvc.loadUrl(makeFileURI(aFile));
},
/**
* Support function that deletes the local file for a download. This is
* used in cases where the Download Manager service doesn't delete the file
* from disk when cancelling. See bug 732924.
*/
_ensureLocalFileRemoved: function DVIC_ensureLocalFileRemoved()
{
try {
let localFile = this.dataItem.localFile;
if (localFile.exists()) {
localFile.remove(false);
}
} catch (ex) { }
}
};

Просмотреть файл

@ -63,6 +63,9 @@ const nsIDM = Ci.nsIDownloadManager;
const kDownloadsStringBundleUrl =
"chrome://browser/locale/downloads/downloads.properties";
const kPrefBdmScanWhenDone = "browser.download.manager.scanWhenDone";
const kPrefBdmAlertOnExeOpen = "browser.download.manager.alertOnEXEOpen";
const kDownloadsStringsRequiringFormatting = {
sizeWithUnits: true,
shortTimeLeftSeconds: true,
@ -396,6 +399,117 @@ this.DownloadsCommon = {
// never adding to the time left. Ensure that we never fall below one
// second left until all downloads are actually finished.
return aLastSeconds = Math.max(aSeconds, 1);
},
/**
* Opens a downloaded file.
* If you've a dataItem, you should call dataItem.openLocalFile.
* @param aFile
* the downloaded file to be opened.
* @param aMimeInfo
* the mime type info object. May be null.
* @param aOwnerWindow
* the window with which this action is associated.
*/
openDownloadedFile: function DC_openDownloadedFile(aFile, aMimeInfo, aOwnerWindow) {
if (!(aFile instanceof Ci.nsIFile))
throw new Error("aFile must be a nsIFile object");
if (aMimeInfo && !(aMimeInfo instanceof Ci.nsIMIMEInfo))
throw new Error("Invalid value passed for aMimeInfo");
if (!(aOwnerWindow instanceof Ci.nsIDOMWindow))
throw new Error("aOwnerWindow must be a dom-window object");
// Confirm opening executable files if required.
if (aFile.isExecutable()) {
let showAlert = true;
try {
showAlert = Services.prefs.getBoolPref(kPrefBdmAlertOnExeOpen);
} catch (ex) { }
// On Vista and above, we rely on native security prompting for
// downloaded content unless it's disabled.
if (DownloadsCommon.isWinVistaOrHigher) {
try {
if (Services.prefs.getBoolPref(kPrefBdmScanWhenDone)) {
showAlert = false;
}
} catch (ex) { }
}
if (showAlert) {
let name = this.dataItem.target;
let message =
DownloadsCommon.strings.fileExecutableSecurityWarning(name, name);
let title =
DownloadsCommon.strings.fileExecutableSecurityWarningTitle;
let dontAsk =
DownloadsCommon.strings.fileExecutableSecurityWarningDontAsk;
let checkbox = { value: false };
let open = Services.prompt.confirmCheck(aOwnerWindow, title, message,
dontAsk, checkbox);
if (!open) {
return;
}
Services.prefs.setBoolPref(kPrefBdmAlertOnExeOpen,
!checkbox.value);
}
}
// Actually open the file.
try {
if (aMimeInfo && aMimeInfo.preferredAction == aMimeInfo.useHelperApp) {
aMimeInfo.launchWithFile(aFile);
return;
}
}
catch(ex) { }
// If either we don't have the mime info, or the preferred action failed,
// attempt to launch the file directly.
try {
aFile.launch();
}
catch(ex) {
// If launch fails, try sending it through the system's external "file:"
// URL handler.
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService)
.loadUrl(NetUtil.newURI(aFile));
}
},
/**
* Show a donwloaded file in the system file manager.
* If you have a dataItem, use dataItem.showLocalFile.
*
* @param aFile
* a downloaded file.
*/
showDownloadedFile: function DC_showDownloadedFile(aFile) {
if (!(aFile instanceof Ci.nsIFile))
throw new Error("aFile must be a nsIFile object");
try {
// Show the directory containing the file and select the file.
aFile.reveal();
} catch (ex) {
// If reveal fails for some reason (e.g., it's not implemented on unix
// or the file doesn't exist), try using the parent if we have it.
let parent = aFile.parent;
if (parent) {
try {
// Open the parent directory to show where the file should be.
parent.launch();
} catch (ex) {
// If launch also fails (probably because it's not implemented), let
// the OS handler try to open the parent.
Cc["@mozilla.org/uriloader/external-protocol-service;1"]
.getService(Ci.nsIExternalProtocolService)
.loadUrl(NetUtil.newURI(parent));
}
}
}
}
};
@ -1212,6 +1326,101 @@ DownloadsDataItem.prototype = {
// file, though this may throw an exception if the path is invalid.
return new DownloadsLocalFileCtor(aFilename);
}
},
/**
* Open the target file for this download.
*
* @param aOwnerWindow
* The window with which the required action is associated.
* @throws if the file cannot be opened.
*/
openLocalFile: function DDI_openLocalFile(aOwnerWindow) {
this.getDownload(function(aDownload) {
DownloadsCommon.openDownloadedFile(this.localFile,
aDownload.MIMEInfo,
aOwnerWindow);
}.bind(this));
},
/**
* Show the downloaded file in the system file manager.
*/
showLocalFile: function DDI_showLocalFile() {
DownloadsCommon.showDownloadedFile(this.localFile);
},
/**
* Resumes the download if paused, pauses it if active.
* @throws if the download is not resumable or if has already done.
*/
togglePauseResume: function DDI_togglePauseResume() {
if (!this.inProgress || !this.resumable)
throw new Error("The given download cannot be paused or resumed");
this.getDownload(function(aDownload) {
if (this.inProgress) {
if (this.paused)
aDownload.resume();
else
aDownload.pause();
}
}.bind(this));
},
/**
* Attempts to retry the download.
* @throws if we cannot.
*/
retry: function DDI_retry() {
if (!this.canRetry)
throw new Error("Cannot rerty this download");
this.getDownload(function(aDownload) {
aDownload.retry();
});
},
/**
* Support function that deletes the local file for a download. This is
* used in cases where the Download Manager service doesn't delete the file
* from disk when cancelling. See bug 732924.
*/
_ensureLocalFileRemoved: function DDI__ensureLocalFileRemoved()
{
try {
let localFile = this.localFile;
if (localFile.exists()) {
localFile.remove(false);
}
} catch (ex) { }
},
/**
* Cancels the download.
* @throws if the download is already done.
*/
cancel: function() {
if (!this.inProgress)
throw new Error("Cannot cancel this download");
this.getDownload(function (aDownload) {
aDownload.cancel();
this._ensureLocalFileRemoved();
}.bind(this));
},
/**
* Remove the download.
*/
remove: function DDI_remove() {
this.getDownload(function (aDownload) {
if (this.inProgress) {
aDownload.cancel();
this._ensureLocalFileRemoved();
}
aDownload.remove();
}.bind(this));
}
};

Просмотреть файл

@ -20,6 +20,8 @@ PlacesViewBase.prototype = {
_viewElt: null,
get viewElt() this._viewElt,
get associatedElement() this._viewElt,
get controllers() this._viewElt.controllers,
// The xul element that represents the root container.

Просмотреть файл

@ -0,0 +1,45 @@
/* 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/. */
richlistitem.download button {
/* These buttons should never get focus, as that would "disable"
the downloads view controller (it's only used when the richlistbox
is focused). */
-moz-user-focus: none;
}
/*** Visibility of controls inside download items ***/
.download-state:-moz-any( [state="6"], /* Blocked (parental) */
[state="8"], /* Blocked (dirty) */
[state="9"]) /* Blocked (policy) */
.downloadTypeIcon:not(.blockedIcon),
.download-state:not(:-moz-any([state="6"], /* Blocked (parental) */
[state="8"], /* Blocked (dirty) */
[state="9"]) /* Blocked (policy) */)
.downloadTypeIcon.blockedIcon,
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
[state="5"], /* Starting (queued) */
[state="0"], /* Downloading */
[state="4"], /* Paused */
[state="7"]) /* Scanning */)
.downloadProgress,
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
[state="5"], /* Starting (queued) */
[state="0"], /* Downloading */
[state="4"]) /* Paused */)
.downloadCancel,
.download-state:not(:-moz-any([state="2"], /* Failed */
[state="3"]) /* Canceled */)
.downloadRetry,
.download-state:not( [state="1"] /* Finished */)
.downloadShow
{
visibility: hidden;
}

Просмотреть файл

@ -0,0 +1,54 @@
<?xml version="1.0"?>
<!-- -*- Mode: HTML; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- -->
<!-- vim: set ts=2 et sw=2 tw=80: -->
<!-- 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/. -->
<!DOCTYPE bindings SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
<bindings id="downloadBindings"
xmlns="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:xbl="http://www.mozilla.org/xbl">
<binding id="download"
extends="chrome://global/content/bindings/richlistbox.xml#richlistitem">
<resources>
<stylesheet src="chrome://browser/content/places/download.css"/>
</resources>
<content orient="horizontal" align="center">
<xul:image class="downloadTypeIcon"
validate="always"
xbl:inherits="src=image"/>
<xul:image class="downloadTypeIcon blockedIcon"/>
<xul:vbox pack="center" flex="1">
<xul:description class="downloadTarget"
crop="center"
xbl:inherits="value=displayName,tooltiptext=displayName"/>
<xul:progressmeter anonid="progressmeter"
class="downloadProgress"
min="0"
max="100"
xbl:inherits="mode=progressmode,value=progress"/>
<xul:description class="downloadDetails"
style="width: &downloadDetails.width;"
crop="end"
xbl:inherits="value=status,tooltiptext=statusTip"/>
</xul:vbox>
<xul:stack>
<xul:button class="downloadButton downloadCancel"
command="downloadsCmd_cancel"
tooltiptext="&cmd.cancel.label;"/>
<xul:button class="downloadButton downloadRetry"
command="downloadsCmd_retry"
tooltiptext="&cmd.retry.label;"/>
<xul:button class="downloadButton downloadShow"
command="downloadsCmd_show"
tooltiptext="&cmd.show.label;"/>
</xul:stack>
</content>
</binding>
</bindings>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -14,3 +14,33 @@ tree[type="places"] {
menupopup[placespopup="true"] {
-moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-base");
}
richlistitem.download {
-moz-binding: url('chrome://browser/content/places/download.xml#download');
}
.download-state:not( [state="0"] /* Downloading */)
.downloadPauseMenuItem,
.download-state:not( [state="4"] /* Paused */)
.downloadResumeMenuItem,
.download-state:not(:-moz-any([state="2"], /* Failed */
[state="4"]) /* Paused */)
.downloadCancelMenuItem,
.download-state:not(:-moz-any([state="1"], /* Finished */
[state="2"], /* Failed */
[state="3"], /* Canceled */
[state="6"], /* Blocked (parental) */
[state="8"], /* Blocked (dirty) */
[state="9"]) /* Blocked (policy) */)
.downloadRemoveFromListMenuItem,
.download-state:not(:-moz-any([state="-1"],/* Starting (initial) */
[state="5"], /* Starting (queued) */
[state="0"], /* Downloading */
[state="4"]) /* Paused */)
.downloadShowMenuItem,
.download-state[state="7"] .downloadCommandsSeparator
{
display: none;
}

Просмотреть файл

@ -5,9 +5,13 @@
Components.utils.import("resource:///modules/MigrationUtils.jsm");
const DOWNLOADS_QUERY = "place:transition=" +
Components.interfaces.nsINavHistoryService.TRANSITION_DOWNLOAD +
"&sort=" +
Components.interfaces.nsINavHistoryQueryOptions.SORT_BY_DATE_DESCENDING;
var PlacesOrganizer = {
_places: null,
_content: null,
// IDs of fields from editBookmarkOverlay that should be hidden when infoBox
// is minimal. IDs should be kept in sync with the IDs of the elements
@ -32,8 +36,9 @@ var PlacesOrganizer = {
},
init: function PO_init() {
ContentArea.init();
this._places = document.getElementById("placesList");
this._content = document.getElementById("placeContent");
this._initFolderTree();
var leftPaneSelection = "AllBookmarks"; // default to all-bookmarks
@ -45,12 +50,6 @@ var PlacesOrganizer = {
this._backHistory.splice(0, this._backHistory.length);
document.getElementById("OrganizerCommand:Back").setAttribute("disabled", true);
var view = this._content.treeBoxObject.view;
if (view.rowCount > 0)
view.selection.select(0);
this._content.focus();
// Set up the search UI.
PlacesSearchBox.init();
@ -84,6 +83,13 @@ var PlacesOrganizer = {
#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
gPrivateBrowsingListener.init();
#endif
// Select the first item in the content area view.
let view = ContentArea.currentView;
let root = view.result ? view.result.root : null;
if (root && root.containerOpen && root.childCount >= 0)
view.selectNode(root.getChild(0));
ContentArea.focus();
},
QueryInterface: function PO_QueryInterface(aIID) {
@ -139,9 +145,9 @@ var PlacesOrganizer = {
if (!this._places.hasSelection) {
// If no node was found for the given place: uri, just load it directly
this._content.place = aLocation;
ContentArea.currentPlace = aLocation;
}
this.onContentTreeSelect();
this.updateDetailsPane();
// update navigation commands
if (this._backHistory.length == 0)
@ -201,8 +207,8 @@ var PlacesOrganizer = {
// If either the place of the content tree in the right pane has changed or
// the user cleared the search box, update the place, hide the search UI,
// and update the back/forward buttons by setting location.
if (this._content.place != placeURI || !resetSearchBox) {
this._content.place = placeURI;
if (ContentArea.currentPlace != placeURI || !resetSearchBox) {
ContentArea.currentPlace = placeURI;
this.location = node.uri;
}
@ -221,8 +227,7 @@ var PlacesOrganizer = {
PlacesSearchBox.searchFilter.reset();
this._setSearchScopeForNode(node);
if (this._places.treeBoxObject.focused)
this._fillDetailsPane([node]);
this.updateDetailsPane();
},
/**
@ -247,50 +252,39 @@ var PlacesOrganizer = {
},
/**
* Handle clicks on the tree.
* Handle clicks on the places list.
* Single Left click, right click or modified click do not result in any
* special action, since they're related to selection.
* @param aEvent
* The mouse event.
*/
onTreeClick: function PO_onTreeClick(aEvent) {
onPlacesListClick: function PO_onPlacesListClick(aEvent) {
// Only handle clicks on tree children.
if (aEvent.target.localName != "treechildren")
return;
var currentView = aEvent.currentTarget;
var selectedNode = currentView.selectedNode;
if (selectedNode) {
var doubleClickOnFlatList = (aEvent.button == 0 && aEvent.detail == 2 &&
aEvent.target.parentNode.flatList);
var middleClick = (aEvent.button == 1 && aEvent.detail == 1);
if (PlacesUtils.nodeIsURI(selectedNode) &&
(doubleClickOnFlatList || middleClick)) {
// Open associated uri in the browser.
PlacesOrganizer.openSelectedNode(aEvent);
}
else if (middleClick &&
PlacesUtils.nodeIsContainer(selectedNode)) {
let node = this._places.selectedNode;
if (node) {
let middleClick = aEvent.button == 1 && aEvent.detail == 1;
if (middleClick && PlacesUtils.nodeIsContainer(node)) {
// The command execution function will take care of seeing if the
// selection is a folder or a different container type, and will
// load its contents in tabs.
PlacesUIUtils.openContainerNodeInTabs(selectedNode, aEvent, currentView);
PlacesUIUtils.openContainerNodeInTabs(selectedNode, aEvent, this._places);
}
}
},
/**
* Handle focus changes on the trees.
* When moving focus between panes we should update the details pane contents.
* @param aEvent
* The mouse event.
* Handle focus changes on the places list and the current content view.
*/
onTreeFocus: function PO_onTreeFocus(aEvent) {
var currentView = aEvent.currentTarget;
var selectedNodes = currentView.selectedNode ? [currentView.selectedNode] :
this._content.selectedNodes;
updateDetailsPane: function PO_updateDetailsPane() {
let view = PlacesUIUtils.getViewForNode(document.activeElement);
if (view) {
let selectedNodes = view.selectedNode ?
[view.selectedNode] : view.selectedNodes;
this._fillDetailsPane(selectedNodes);
}
},
openFlatContainer: function PO_openFlatContainerFlatContainer(aContainer) {
@ -300,17 +294,12 @@ var PlacesOrganizer = {
this._places.selectPlaceURI(aContainer.uri);
},
openSelectedNode: function PO_openSelectedNode(aEvent) {
PlacesUIUtils.openNodeWithEvent(this._content.selectedNode, aEvent,
this._content);
},
/**
* Returns the options associated with the query currently loaded in the
* main places pane.
*/
getCurrentOptions: function PO_getCurrentOptions() {
return PlacesUtils.asQuery(this._content.result.root).queryOptions;
return PlacesUtils.asQuery(ContentArea.currentView.result.root).queryOptions;
},
/**
@ -318,7 +307,7 @@ var PlacesOrganizer = {
* main places pane.
*/
getCurrentQueries: function PO_getCurrentQueries() {
return PlacesUtils.asQuery(this._content.result.root).getQueries();
return PlacesUtils.asQuery(ContentArea.currentView.result.root).getQueries();
},
/**
@ -564,11 +553,6 @@ var PlacesOrganizer = {
canvas.height = height;
},
onContentTreeSelect: function PO_onContentTreeSelect() {
if (this._content.treeBoxObject.focused)
this._fillDetailsPane(this._content.selectedNodes);
},
_fillDetailsPane: function PO__fillDetailsPane(aNodeList) {
var infoBox = document.getElementById("infoBox");
var detailsDeck = document.getElementById("detailsDeck");
@ -671,10 +655,15 @@ var PlacesOrganizer = {
else {
detailsDeck.selectedIndex = 0;
infoBox.hidden = true;
var selectItemDesc = document.getElementById("selectItemDescription");
var itemsCountLabel = document.getElementById("itemsCountText");
var rowCount = this._content.treeBoxObject.view.rowCount;
if (rowCount == 0) {
let selectItemDesc = document.getElementById("selectItemDescription");
let itemsCountLabel = document.getElementById("itemsCountText");
let itemsCount = 0;
if (ContentArea.currentView.result) {
let rootNode = ContentArea.currentView.result.root;
if (rootNode.containerOpen)
itemsCount = rootNode.childCount;
}
if (itemsCount == 0) {
selectItemDesc.hidden = true;
itemsCountLabel.value = PlacesUIUtils.getString("detailsPane.noItems");
}
@ -682,7 +671,7 @@ var PlacesOrganizer = {
selectItemDesc.hidden = false;
itemsCountLabel.value =
PlacesUIUtils.getPluralString("detailsPane.itemsCountLabel",
rowCount, [rowCount]);
itemsCount, [itemsCount]);
}
}
},
@ -779,14 +768,14 @@ var PlacesSearchBox = {
return;
}
var currentOptions = PO.getCurrentOptions();
var content = PO._content;
let currentView = ContentArea.currentView;
let currentOptions = PO.getCurrentOptions();
// Search according to the current scope, which was set by
// PQB_setScope()
switch (PlacesSearchBox.filterCollection) {
case "bookmarks":
content.applyFilter(filterString, this.folders);
currentView.applyFilter(filterString, this.folders);
break;
case "history":
if (currentOptions.queryType != Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY) {
@ -797,13 +786,14 @@ var PlacesSearchBox = {
options.resultType = currentOptions.RESULT_TYPE_URI;
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
options.includeHidden = true;
content.load([query], options);
currentView.load([query], options);
}
else {
content.applyFilter(filterString, null, true);
currentView.applyFilter(filterString, null, true);
}
break;
case "downloads": {
case "downloads":
if (currentView == ContentTree.view) {
let query = PlacesUtils.history.getNewQuery();
query.searchTerms = filterString;
query.setTransitions([Ci.nsINavHistoryService.TRANSITION_DOWNLOAD], 1);
@ -812,16 +802,19 @@ var PlacesSearchBox = {
options.resultType = currentOptions.RESULT_TYPE_URI;
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
options.includeHidden = true;
content.load([query], options);
break;
currentView.load([query], options);
}
else {
// The new downloads view doesn't use places for searching downloads.
currentView.searchTerm = filterString;
}
break;
default:
throw "Invalid filterCollection on search";
break;
}
// Update the details panel
PlacesOrganizer.onContentTreeSelect();
PlacesOrganizer.updateDetailsPane();
},
/**
@ -1251,3 +1244,94 @@ let gPrivateBrowsingListener = {
}
};
#endif
let ContentArea = {
init: function CA_init() {
this._deck = document.getElementById("placesViewsDeck");
this._specialViews = new Map();
ContentTree.init();
},
_shouldUseNewDownloadsView: function CA_shouldUseNewDownloadsView() {
try {
return Services.prefs.getBoolPref("browser.library.useNewDownloadsView");
}
catch(ex) { }
return false;
},
getContentViewForQueryString:
function CA_getContentViewForQueryString(aQueryString) {
if (this._specialViews.has(aQueryString))
return this._specialViews.get(aQueryString);
if (aQueryString == DOWNLOADS_QUERY && this._shouldUseNewDownloadsView()) {
let view = new DownloadsPlacesView(document.getElementById("downloadsRichListBox"), aQueryString);
this.setContentViewForQueryString(aQueryString, view);
return view;
}
return ContentTree.view;
},
setContentViewForQueryString:
function CA_setContentViewForQueryString(aQueryString, aView) {
this._specialViews.set(aQueryString, aView);
},
get currentView() PlacesUIUtils.getViewForNode(this._deck.selectedPanel),
set currentView(aView) {
if (this.currentView != aView)
this._deck.selectedPanel = aView.associatedElement;
return aView;
},
get currentPlace() this.currentView.place,
set currentPlace(aQueryString) {
this.currentView = this.getContentViewForQueryString(aQueryString);
this.currentView.place = aQueryString;
return aQueryString;
},
focus: function() {
this._deck.selectedPanel.focus();
}
};
let ContentTree = {
init: function CT_init() {
this._view = document.getElementById("placeContent");
},
get view() this._view,
openSelectedNode: function CT_openSelectedNode(aEvent) {
let view = this.view;
PlacesUIUtils.openNodeWithEvent(view.selectedNode, aEvent, view);
},
onClick: function CT_onClick(aEvent) {
// Only handle clicks on tree children.
if (aEvent.target.localName != "treechildren")
return;
let node = this.view.selectedNode;
if (node) {
let doubleClick = aEvent.button == 0 && aEvent.detail == 2;
let middleClick = aEvent.button == 1 && aEvent.detail == 1;
if (PlacesUtils.nodeIsURI(node) && (doubleClick || middleClick)) {
// Open associated uri in the browser.
this.openSelectedNode(aEvent);
}
else if (middleClick && PlacesUtils.nodeIsContainer(node)) {
// The command execution function will take care of seeing if the
// selection is a folder or a different container type, and will
// load its contents in tabs.
PlacesUIUtils.openContainerNodeInTabs(node, aEvent, this.view);
}
}
},
onKeyPress: function CT_onKeyPress(aEvent) {
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
this.openSelectedNode(aEvent);
}
};

Просмотреть файл

@ -10,6 +10,7 @@
<?xml-stylesheet href="chrome://global/skin/"?>
<?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
<?xml-stylesheet href="chrome://browser/skin/places/organizer.css"?>
<?xml-stylesheet href="chrome://browser/skin/downloads/downloads.css"?>
<?xul-overlay href="chrome://browser/content/places/editBookmarkOverlay.xul"?>
@ -28,6 +29,8 @@
%editMenuOverlayDTD;
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
<!ENTITY % downloadsDTD SYSTEM "chrome://browser/locale/downloads/downloads.dtd">
%downloadsDTD;
]>
<window id="places"
@ -42,12 +45,16 @@
toggletoolbar="true"
persist="width height screenX screenY sizemode">
<script type="application/javascript"
src="chrome://browser/content/places/downloadsView.js"/>
<script type="application/javascript"
src="chrome://browser/content/places/places.js"/>
<script type="application/javascript"
src="chrome://browser/content/utilityOverlay.js"/>
<script type="application/javascript"
src="chrome://browser/content/places/editBookmarkOverlay.js"/>
<script type="application/javascript"
src="chrome://global/content/contentAreaUtils.js"/>
<stringbundleset id="placesStringSet">
<stringbundle id="brandStrings" src="chrome://branding/locale/brand.properties"/>
@ -344,8 +351,8 @@
type="places"
hidecolumnpicker="true" context="placesContext"
onselect="PlacesOrganizer.onPlaceSelected(true);"
onclick="PlacesOrganizer.onTreeClick(event);"
onfocus="PlacesOrganizer.onTreeFocus(event);"
onclick="PlacesOrganizer.onPlacesListClick(event);"
onfocus="PlacesOrganizer.updateDetailsPane(event);"
seltype="single"
persist="width"
width="200"
@ -358,6 +365,9 @@
</tree>
<splitter collapse="none" persist="state"></splitter>
<vbox id="contentView" flex="4">
<deck id="placesViewsDeck"
selectedIndex="0"
flex="1">
<tree id="placeContent"
class="plain placesTree"
context="placesContext"
@ -366,11 +376,11 @@
type="places"
flatList="true"
enableColumnDrag="true"
onkeypress="if (event.keyCode == KeyEvent.DOM_VK_RETURN) PlacesOrganizer.openSelectedNode(event);"
onfocus="PlacesOrganizer.updateDetailsPane(event)"
onselect="PlacesOrganizer.updateDetailsPane(event)"
onkeypress="ContentTree.onKeyPress(event);"
onopenflatcontainer="PlacesOrganizer.openFlatContainer(aContainer);"
onselect="PlacesOrganizer.onContentTreeSelect();"
onfocus="PlacesOrganizer.onTreeFocus(event);"
onclick="PlacesOrganizer.onTreeClick(event);">
onclick="ContentTree.onClick(event);">
<treecols id="placeContentColumns" context="placesColumnsContext">
<treecol label="&col.name.label;" id="placesContentTitle" anonid="title" flex="5" primary="true" ordinal="1"
persist="width hidden ordinal sortActive sortDirection"/>
@ -401,6 +411,12 @@
</treecols>
<treechildren flex="1"/>
</tree>
<richlistbox flex="1"
seltype="multiple"
id="downloadsRichListBox" context="downloadsContextMenu"
onkeypress="return this._placesView.onKeyPress(event);"
oncontextmenu="return this._placesView.onContextMenu(event);"/>
</deck>
<deck id="detailsDeck" style="height: 11em;">
<vbox id="itemsCountBox" align="center">
<spacer flex="3"/>
@ -434,4 +450,61 @@
</deck>
</vbox>
</hbox>
<commandset id="downloadCommands"
commandupdater="true"
events="focus,select,contextmenu"
oncommandupdate="goUpdatePlacesCommands(); goUpdateDownloadCommands();">
<command id="downloadsCmd_pauseResume"
oncommand="goDoCommand('downloadsCmd_pauseResume')"/>
<command id="downloadsCmd_cancel"
oncommand="goDoCommand('downloadsCmd_cancel')"/>
<command id="downloadsCmd_open"
oncommand="goDoCommand('downloadsCmd_open')"/>
<command id="downloadsCmd_show"
oncommand="goDoCommand('downloadsCmd_show')"/>
<command id="downloadsCmd_retry"
oncommand="goDoCommand('downloadsCmd_retry')"/>
<command id="downloadsCmd_openReferrer"
oncommand="goDoCommand('downloadsCmd_openReferrer')"/>
</commandset>
<menupopup id="downloadsContextMenu"
class="download-state">
<menuitem command="downloadsCmd_pauseResume"
class="downloadPauseMenuItem"
label="&cmd.pause.label;"
accesskey="&cmd.pause.accesskey;"/>
<menuitem command="downloadsCmd_pauseResume"
class="downloadResumeMenuItem"
label="&cmd.resume.label;"
accesskey="&cmd.resume.accesskey;"/>
<menuitem command="downloadsCmd_cancel"
class="downloadCancelMenuItem"
label="&cmd.cancel.label;"
accesskey="&cmd.cancel.accesskey;"/>
<menuitem command="cmd_delete"
class="downloadRemoveFromListMenuItem"
label="&cmd.removeFromList.label;"
accesskey="&cmd.removeFromList.accesskey;"/>
<menuitem command="downloadsCmd_show"
class="downloadShowMenuItem"
#ifdef XP_MACOSX
label="&cmd.showMac.label;"
accesskey="&cmd.showMac.accesskey;"
#else
label="&cmd.show.label;"
accesskey="&cmd.show.accesskey;"
#endif
/>
<menuseparator class="downloadCommandsSeparator"/>
<menuitem command="downloadsCmd_openReferrer"
label="&cmd.goToDownloadPage.label;"
accesskey="&cmd.goToDownloadPage.accesskey;"/>
<menuitem command="cmd_copy"
label="&cmd.copyDownloadLink.label;"
accesskey="&cmd.copyDownloadLink.accesskey;"/>
</menupopup>
</window>

Просмотреть файл

@ -56,6 +56,10 @@
]]></setter>
</property>
<property name="associatedElement"
readonly="true"
onget="return this"/>
<method name="applyFilter">
<parameter name="filterString"/>
<parameter name="folderRestrict"/>

Просмотреть файл

@ -8,6 +8,9 @@ browser.jar:
content/browser/places/bookmarkProperties2.xul (content/bookmarkProperties.xul)
* content/browser/places/places.xul (content/places.xul)
* content/browser/places/places.js (content/places.js)
content/browser/places/downloadsView.js (content/downloadsView.js)
content/browser/places/download.xml (content/download.xml)
content/browser/places/download.css (content/download.css)
content/browser/places/places.css (content/places.css)
content/browser/places/organizer.css (content/organizer.css)
content/browser/places/bookmarkProperties.xul (content/bookmarkProperties.xul)

Просмотреть файл

@ -21,6 +21,7 @@ const MOZURISPEC = "http://mozilla.com/";
let gLibrary;
let PlacesOrganizer;
let ContentTree;
function test() {
waitForExplicitFinish();
@ -34,6 +35,9 @@ function onLibraryReady() {
PlacesOrganizer = gLibrary.PlacesOrganizer;
ok(PlacesOrganizer, "Places organizer in scope");
ContentTree = gLibrary.ContentTree;
ok(ContentTree, "ContentTree is in scope");
tests.makeHistVisit();
tests.makeTag();
tests.focusTag();
@ -101,12 +105,12 @@ let tests = {
PlacesUtils.asContainer(histContainer);
histContainer.containerOpen = true;
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
let histNode = PlacesOrganizer._content.view.nodeForTreeIndex(0);
PlacesOrganizer._content.selectNode(histNode);
let histNode = ContentTree.view.view.nodeForTreeIndex(0);
ContentTree.view.selectNode(histNode);
is(histNode.uri, MOZURISPEC,
"historyNode exists: " + histNode.uri);
// copy the history node
PlacesOrganizer._content.controller.copy();
ContentTree.view.controller.copy();
},
historyNode: function (){
@ -116,7 +120,7 @@ let tests = {
PlacesUtils.asContainer(histContainer);
histContainer.containerOpen = true;
PlacesOrganizer._places.selectNode(histContainer.getChild(0));
let histNode = PlacesOrganizer._content.view.nodeForTreeIndex(0);
let histNode = ContentTree.view.view.nodeForTreeIndex(0);
ok(histNode, "histNode exists: " + histNode.title);
// check to see if the history node is tagged!
let tags = PlacesUtils.tagging.getTagsForURI(NetUtil.newURI(MOZURISPEC));
@ -133,8 +137,8 @@ let tests = {
// is the bookmark visible in the UI?
// get the Unsorted Bookmarks node
PlacesOrganizer.selectLeftPaneQuery("UnfiledBookmarks");
// now we can see what is in the _content tree
let unsortedNode = PlacesOrganizer._content.view.nodeForTreeIndex(1);
// now we can see what is in the ContentTree tree
let unsortedNode = ContentTree.view.view.nodeForTreeIndex(1);
ok(unsortedNode, "unsortedNode is not null: " + unsortedNode.uri);
is(unsortedNode.uri, MOZURISPEC, "node uri's are the same");
},

Просмотреть файл

@ -7,6 +7,7 @@ const TEST_URL = "http://example.com/";
let gLibrary;
let gItemId;
let PlacesOrganizer;
let ContentTree;
function test() {
waitForExplicitFinish();
@ -15,11 +16,13 @@ function test() {
function onLibraryReady() {
PlacesOrganizer = gLibrary.PlacesOrganizer;
ContentTree = gLibrary.ContentTree;
// Sanity checks.
ok(PlacesUtils, "PlacesUtils in scope");
ok(PlacesUIUtils, "PlacesUIUtils in scope");
ok(PlacesOrganizer, "PlacesOrganizer in scope");
ok(ContentTree, "ContentTree is in scope");
gItemId = PlacesUtils.bookmarks.insertBookmark(
PlacesUtils.toolbarFolderId, NetUtil.newURI(TEST_URL),
@ -41,21 +44,21 @@ function selectBookmarkIn(aLeftPaneQuery) {
is(PlacesUtils.bookmarks.getFolderIdForItem(gItemId), rootId,
"Bookmark has the right parent");
info("Selecting the bookmark in the right pane");
PlacesOrganizer._content.selectItems([gItemId]);
let bookmarkNode = PlacesOrganizer._content.selectedNode;
ContentTree.view.selectItems([gItemId]);
let bookmarkNode = ContentTree.view.selectedNode;
is(bookmarkNode.uri, TEST_URL, "Found the expected bookmark");
}
function cutSelection() {
info("Cutting selection");
PlacesOrganizer._content.controller.cut();
ContentTree.view.controller.cut();
}
function pasteClipboard(aLeftPaneQuery) {
info("Selecting " + aLeftPaneQuery + " in the left pane");
PlacesOrganizer.selectLeftPaneQuery(aLeftPaneQuery);
info("Pasting clipboard");
PlacesOrganizer._content.controller.paste();
ContentTree.view.controller.paste();
}
function onClipboardReady() {

Просмотреть файл

@ -69,19 +69,20 @@ gTests.push({
desc: "Ensure correct selection and functionality in Library",
run: function() {
let PO = gLibrary.PlacesOrganizer;
let ContentTree = gLibrary.ContentTree;
// Move selection forth and back.
PO.selectLeftPaneQuery("History");
PO.selectLeftPaneQuery("UnfiledBookmarks");
// Now select the "keepme" folder in the right pane and delete it.
PO._content.selectNode(PO._content.result.root.getChild(0));
is(PO._content.selectedNode.title, "keepme",
ContentTree.view.selectNode(ContentTree.view.result.root.getChild(0));
is(ContentTree.view.selectedNode.title, "keepme",
"Found folder in content pane");
// Test live update.
PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
makeURI(TEST_URL),
PlacesUtils.bookmarks.DEFAULT_INDEX,
"bm");
is(PO._content.result.root.childCount, 2,
is(ContentTree.view.result.root.childCount, 2,
"Right pane was correctly updated");
nextTest();
}

Просмотреть файл

@ -18,6 +18,7 @@ gTests.push({
desc: "Bug 430148 - Remove or hide the more/less button in details pane...",
run: function() {
var PO = gLibrary.PlacesOrganizer;
let ContentTree = gLibrary.ContentTree;
var infoBoxExpanderWrapper = getAndCheckElmtById("infoBoxExpanderWrapper");
// add a visit to browser history
@ -57,7 +58,7 @@ gTests.push({
checkAddInfoFieldsCollapsed(PO);
// open history item
var view = PO._content.treeBoxObject.view;
var view = ContentTree.view.treeBoxObject.view;
ok(view.rowCount > 0, "History item exists.");
view.selection.select(0);
ok(infoBoxExpanderWrapper.hidden,
@ -94,7 +95,7 @@ gTests.push({
checkAddInfoFieldsNotCollapsed(PO);
// open first bookmark
var view = PO._content.treeBoxObject.view;
var view = ContentTree.view.treeBoxObject.view;
ok(view.rowCount > 0, "Bookmark item exists.");
view.selection.select(0);
checkInfoBoxSelected(PO);

Просмотреть файл

@ -88,7 +88,7 @@ gTests.push({
isnot(gLibrary.PlacesOrganizer._places.selectedNode, null,
"We correctly have selection in the Library left pane");
// Get our bookmark in the right pane.
var bookmarkNode = gLibrary.PlacesOrganizer._content.view.nodeForTreeIndex(0);
var bookmarkNode = gLibrary.ContentTree.view.view.nodeForTreeIndex(0);
is(bookmarkNode.uri, this.URIs[0], "Found bookmark in the right pane");
},
@ -130,7 +130,7 @@ gTests.push({
isnot(gLibrary.PlacesOrganizer._places.selectedNode, null,
"We correctly have selection in the Library left pane");
// Get our bookmark in the right pane.
var folderNode = gLibrary.PlacesOrganizer._content.view.nodeForTreeIndex(0);
var folderNode = gLibrary.ContentTree.view.view.nodeForTreeIndex(0);
is(folderNode.title, "Folder", "Found folder in the right pane");
},
@ -187,7 +187,7 @@ gTests.push({
isnot(gLibrary.PlacesOrganizer._places.selectedNode, null,
"We correctly have selection in the Library left pane");
// Get our bookmark in the right pane.
var folderNode = gLibrary.PlacesOrganizer._content.view.nodeForTreeIndex(0);
var folderNode = gLibrary.ContentTree.view.view.nodeForTreeIndex(0);
is(folderNode.title, "Query", "Found query in the right pane");
},
@ -243,7 +243,7 @@ function runNextTest() {
// Middle click on first node in the content tree of the Library.
gLibrary.focus();
waitForFocus(function() {
mouseEventOnCell(gLibrary.PlacesOrganizer._content, 0, 0, { button: 1 });
mouseEventOnCell(gLibrary.ContentTree.view, 0, 0, { button: 1 });
}, gLibrary);
}
else {

Просмотреть файл

@ -202,3 +202,71 @@ treechildren::-moz-tree-image(cutting) {
treechildren::-moz-tree-cell-text(cutting) {
opacity: 0.7;
}
/** Downloads View **/
richlistitem.download {
height: 7em;
margin: 0;
padding: 8px;
-moz-padding-end: 0;
}
richlistitem.download:first-child {
border-top: 1px solid transparent;
}
richlistitem.download:last-child {
border-bottom: 1px solid transparent;
}
.downloadTypeIcon {
-moz-margin-end: 8px;
/* Prevent flickering when changing states. */
min-height: 32px;
min-width: 32px;
}
.blockedIcon {
list-style-image: url("chrome://global/skin/icons/Error.png");
}
.downloadTarget {
margin-bottom: 6px;
cursor: inherit;
}
.downloadDetails {
opacity: 0.7;
font-size: 95%;
cursor: inherit;
}
.downloadButton {
-moz-appearance: none;
min-width: 0;
min-height: 0;
margin: 3px;
border: none;
padding: 5px;
list-style-image: url("chrome://browser/skin/downloads/buttons.png");
}
.downloadButton > .button-box {
padding: 0;
}
/*** Button icons ***/
.downloadButton.downloadCancel {
-moz-image-region: rect(0px, 16px, 16px, 0px);
}
.downloadButton.downloadShow {
-moz-image-region: rect(16px, 16px, 32px, 0px);
}
.downloadButton.downloadRetry {
-moz-image-region: rect(32px, 16px, 48px, 0px);
}