Bug 562797: Add-ons Manager needs to integrate with browser session history. r=Unfocused, r=sdwilsh, a=blocking-beta5

--HG--
extra : rebase_source : 8fe1d34d8b6f906799de9b2740f12c383a5feed0
This commit is contained in:
Dave Townsend 2010-08-27 15:23:53 -07:00
Родитель 39aaf3dfcc
Коммит 9c70cf448a
14 изменённых файлов: 774 добавлений и 88 удалений

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

@ -10,8 +10,10 @@
<!ENTITY installFromFile.label "Install From File…">
<!ENTITY installFromFile.accesskey "I">
<!ENTITY cmd.back.tooltip "Go back one page">
<!ENTITY cmd.forward.tooltip "Go forward one page">
<!-- categories / views -->
<!-- LOCALIZATION NOTE: These should match the header-* entries in extensions.properties -->
<!ENTITY view.search.label "Search">
<!ENTITY view.discover.label "Get Add-ons">
<!ENTITY view.locales.label "Languages">

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

@ -6,20 +6,6 @@ aboutWindowVersionString=version %S
#LOCALIZATION NOTE (aboutAddon) %S is the addon name
aboutAddon=About %S
#LOCALIZATION NOTE These should match the view.*.label entities in extensions.dtd
header-search=Search Results
header-discover=Get Add-ons
header-locale=Languages
header-searchengine=Search Engines
header-extension=Extensions
header-theme=Themes
header-plugin=Plugins
header-recentUpdates=Recent Updates
header-availableUpdates=Available Updates
#LOCALIZATION NOTE (header-goBack) %S is the name of the pane to go back to
header-goBack=Back to %S
#LOCALIZATION NOTE (uninstallNotice) %S is the add-on name
uninstallNotice=%S has been removed.

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

@ -93,9 +93,11 @@ XPCOMUtils.defineLazyGetter(gStrings, "appVersion", function() {
return Services.appinfo.version;
});
window.addEventListener("load", initialize, false);
window.addEventListener("unload", shutdown, false);
window.addEventListener("popstate", function(event) {
gViewController.statePopped(event);
}, false);
var gPendingInitializations = 1;
__defineGetter__("gIsInitializing", function() gPendingInitializations > 0);
@ -105,19 +107,6 @@ function initialize() {
gHeader.initialize();
gViewController.initialize();
gEventManager.initialize();
var view = VIEW_DEFAULT;
if (gCategories.node.selectedItem &&
gCategories.node.selectedItem.id != "category-search")
view = gCategories.node.selectedItem.value;
if ("arguments" in window && window.arguments.length > 0) {
if ("view" in window.arguments[0])
view = window.arguments[0].view;
}
gViewController.loadView(view);
notifyInitialized();
}
function notifyInitialized() {
@ -140,8 +129,14 @@ function shutdown() {
}
// Used by external callers to load a specific view into the manager
function loadView(aViewId, aCallback) {
gViewController.loadView(aViewId, aCallback);
function loadView(aViewId) {
if (!gViewController.initialViewSelected) {
// The caller opened the window and immediately loaded the view so it
// should be the initial history entry
gViewController.loadInitialView(aViewId);
} else {
gViewController.loadView(aViewId);
}
}
var gEventManager = {
@ -258,9 +253,9 @@ var gViewController = {
currentViewId: "",
currentViewObj: null,
currentViewRequest: 0,
previousViewId: "",
viewObjects: {},
viewChangeCallback: null,
initialViewSelected: false,
initialize: function() {
this.viewPort = document.getElementById("view-port");
@ -294,6 +289,32 @@ var gViewController = {
}
},
statePopped: function(e) {
// If this is a navigation to a previous state then load that state
if (e.state) {
this.loadViewInternal(e.state.view, e.state.previousView);
return;
}
// If the initial view has already been selected (by a call to loadView) then
// bail out now
if (this.initialViewSelected)
return;
// Otherwise load the default view
var view = VIEW_DEFAULT;
if (gCategories.node.selectedItem &&
gCategories.node.selectedItem.id != "category-search")
view = gCategories.node.selectedItem.value;
if ("arguments" in window && window.arguments.length > 0) {
if ("view" in window.arguments[0])
view = window.arguments[0].view;
}
this.loadInitialView(view);
},
parseViewId: function(aViewId) {
var matchRegex = /^addons:\/\/([^\/]+)\/(.*)$/;
var [,viewType, viewParam] = aViewId.match(matchRegex) || [];
@ -301,13 +322,32 @@ var gViewController = {
},
get isLoading() {
return this.currentViewObj.node.hasAttribute("loading");
return !this.currentViewObj || this.currentViewObj.node.hasAttribute("loading");
},
loadView: function(aViewId, aCallback) {
loadView: function(aViewId) {
if (aViewId == this.currentViewId)
return;
window.history.pushState({
view: aViewId,
previousView: this.currentViewId
}, document.title);
this.loadViewInternal(aViewId, this.currentViewId);
},
loadInitialView: function(aViewId) {
window.history.replaceState({
view: aViewId,
previousView: null
}, document.title);
this.loadViewInternal(aViewId, null);
this.initialViewSelected = true;
notifyInitialized();
},
loadViewInternal: function(aViewId, aPreviousView) {
var view = this.parseViewId(aViewId);
if (!view.type || !(view.type in this.viewObjects))
@ -329,25 +369,41 @@ var gViewController = {
}
}
gCategories.select(aViewId);
this.previousViewId = this.currentViewId;
gCategories.select(aViewId, aPreviousView);
this.currentViewId = aViewId;
this.currentViewObj = viewObj;
this.viewChangeCallback = aCallback;
this.viewPort.selectedPanel = this.currentViewObj.node;
this.viewPort.selectedPanel.setAttribute("loading", "true");
this.currentViewObj.show(view.param, ++this.currentViewRequest);
},
// Moves back in the document history and removes the current history entry
popState: function(aCallback) {
this.viewChangeCallback = function() {
// TODO To ensure we can't go forward again we put an additional entry for
// the current page into the history. Ideally we would just strip the
// history but there doesn't seem to be a way to do that. Bug 590661
window.history.pushState({
view: gViewController.currentViewId,
previousView: gViewController.currentViewId
}, document.title);
this.updateCommands();
if (aCallback)
aCallback();
};
window.history.back();
},
notifyViewChanged: function() {
this.viewPort.selectedPanel.removeAttribute("loading");
if (this.viewChangeCallback)
if (this.viewChangeCallback) {
this.viewChangeCallback();
this.viewChangeCallback = null;
}
var event = document.createEvent("Events");
event.initEvent("ViewChanged", true, true);
@ -355,6 +411,28 @@ var gViewController = {
},
commands: {
cmd_back: {
isEnabled: function() {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.canGoBack;
},
doCommand: function() {
window.history.back();
}
},
cmd_forward: {
isEnabled: function() {
return window.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.canGoForward;
},
doCommand: function() {
window.history.forward();
}
},
cmd_restartApp: {
isEnabled: function() true,
doCommand: function() {
@ -616,7 +694,7 @@ var gViewController = {
return;
}
gViewController.loadView(gViewController.previousViewId, function() {
gViewController.popState(function() {
gViewController.currentViewObj.getListItemForID(aAddon.id).uninstall();
});
},
@ -921,15 +999,17 @@ var gCategories = {
});
},
select: function(aId) {
select: function(aId, aPreviousView) {
var view = gViewController.parseViewId(aId);
if (view.type == "detail") {
aId = aPreviousView;
view = gViewController.parseViewId(aPreviousView);
}
if (this.node.selectedItem &&
this.node.selectedItem.value == aId)
return;
var view = gViewController.parseViewId(aId);
if (view.type == "detail")
return;
if (view.type == "search")
var item = this._search;
else
@ -942,6 +1022,8 @@ var gCategories = {
this.node.selectedItem = item;
this.node.suppressOnSelect = false;
this.node.ensureElementIsVisible(item);
// When supressing onselect last-selected doesn't get updated
this.node.setAttribute("last-selected", item.id);
this.maybeHideSearch();
}
@ -970,21 +1052,12 @@ var gCategories = {
var gHeader = {
_search: null,
_searching: null,
_name: null,
_link: null,
_dest: "",
initialize: function() {
this._name = document.getElementById("header-name");
this._link = document.getElementById("header-link");
this._search = document.getElementById("header-search");
this._searching = document.getElementById("header-searching");
var self = this;
this._link.addEventListener("command", function() {
gViewController.loadView(gViewController.previousViewId);
}, false);
this._search.addEventListener("command", function(aEvent) {
var query = aEvent.target.value;
if (query.length == 0)
@ -992,21 +1065,6 @@ var gHeader = {
gViewController.loadView("addons://search/" + encodeURIComponent(query));
}, false);
this.setName("");
},
setName: function(aName) {
this._name.value = aName;
this._name.hidden = false;
this._link.hidden = true;
},
showBackButton: function() {
this._link.label = gStrings.ext.formatStringFromName("header-goBack",
[this._name.value], 1);
this._name.hidden = true;
this._link.hidden = false;
},
get searchQuery() {
@ -1069,7 +1127,6 @@ var gDiscoverView = {
},
show: function() {
gHeader.setName(gStrings.ext.GetStringFromName("header-discover"));
// load content only if we're not already showing something on AMO
// XXXunf should only be comparing hostname. bug 557698
if (this._browser.currentURI.spec.indexOf(this._browser.homePage) == -1)
@ -1127,7 +1184,6 @@ var gSearchView = {
},
show: function(aQuery, aRequest) {
gHeader.setName(gStrings.ext.GetStringFromName("header-search"));
gHeader.isSearching = true;
this.showEmptyNotice(false);
@ -1360,7 +1416,6 @@ var gListView = {
},
show: function(aType, aRequest) {
gHeader.setName(gStrings.ext.GetStringFromName("header-" + aType));
this.showEmptyNotice(false);
while (this._listBox.itemCount > 0)
@ -1499,7 +1554,6 @@ var gDetailView = {
this._loadingTimer = setTimeout(function() {
self.node.setAttribute("loading-extended", true);
}, LOADING_MSG_DELAY);
gHeader.showBackButton();
var view = gViewController.currentViewId;
@ -1624,7 +1678,7 @@ var gDetailView = {
},
onUninstalled: function() {
gViewController.loadView(gViewController.previousViewId);
gViewController.popState();
},
onOperationCancelled: function() {
@ -1678,8 +1732,6 @@ var gUpdatesView = {
},
show: function(aType, aRequest) {
gHeader.setName(gStrings.ext.GetStringFromName("header-" + aType + "Updates"));
document.getElementById("empty-availableUpdates-msg").hidden = aType != "available";
document.getElementById("empty-recentUpdates-msg").hidden = aType != "recent";
this.showEmptyNotice(false);

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

@ -88,6 +88,8 @@
<command id="cmd_goToAvailableUpdates"/>
<command id="cmd_toggleBackgroundUpdateCheck"/>
<command id="cmd_installFromFile"/>
<command id="cmd_back"/>
<command id="cmd_forward"/>
</commandset>
<!-- view commands - these act on the selected addon -->
@ -107,8 +109,10 @@
<!-- main header -->
<hbox id="header" align="center">
<label id="header-name"/>
<button id="header-link"/>
<button id="back-btn" class="nav-button" command="cmd_back"
tooltiptext="&cmd.back.tooltip;"/>
<button id="forward-btn" class="nav-button" command="cmd_forward"
tooltiptext="&cmd.forward.tooltip;"/>
<spacer flex="1"/>
<hbox id="updates-container" align="center">
<image class="spinner"/>

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

@ -52,6 +52,7 @@ _TEST_FILES = \
browser_bug557956.rdf \
browser_bug557956_8_2.xpi \
browser_bug557956_9_2.xpi \
browser_bug562797.js \
browser_bug562890.js \
browser_bug562899.js \
browser_bug562992.js \

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

@ -0,0 +1,498 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* Tests that history navigation works for the add-ons manager. Only used if
* in-content UI is supported for this application.
*/
function test() {
if (!gUseInContentUI)
return;
waitForExplicitFinish();
var gProvider = new MockProvider();
gProvider.createAddons([{
id: "test1@tests.mozilla.org",
name: "Test add-on 1",
description: "foo"
},
{
id: "test2@tests.mozilla.org",
name: "Test add-on 2",
description: "bar"
},
{
id: "test3@tests.mozilla.org",
name: "Test add-on 3",
type: "theme",
description: "bar"
}]);
run_next_test();
}
function end_test() {
finish();
}
function is_in_list(aManager, view, canGoBack, canGoForward) {
var doc = aManager.document;
is(doc.getElementById("categories").selectedItem.value, view, "Should be on the right category");
is(doc.getElementById("view-port").selectedPanel.id, "list-view", "Should be on the right view");
is(gBrowser.canGoBack, canGoBack, "canGoBack should be correct");
is(!doc.getElementById("back-btn").disabled, canGoBack, "Back button should have the right state");
is(gBrowser.canGoForward, canGoForward, "canGoForward should be correct");
is(!doc.getElementById("forward-btn").disabled, canGoForward, "Forward button should have the right state");
}
function is_in_search(aManager, query, canGoBack, canGoForward) {
var doc = aManager.document;
is(doc.getElementById("categories").selectedItem.value, "addons://search/", "Should be on the right category");
is(doc.getElementById("view-port").selectedPanel.id, "search-view", "Should be on the right view");
is(doc.getElementById("header-search").value, query, "Should have used the right query");
is(gBrowser.canGoBack, canGoBack, "canGoBack should be correct");
is(!doc.getElementById("back-btn").disabled, canGoBack, "Back button should have the right state");
is(gBrowser.canGoForward, canGoForward, "canGoForward should be correct");
is(!doc.getElementById("forward-btn").disabled, canGoForward, "Forward button should have the right state");
}
function is_in_detail(aManager, view, canGoBack, canGoForward) {
var doc = aManager.document;
is(doc.getElementById("categories").selectedItem.value, view, "Should be on the right category");
is(doc.getElementById("view-port").selectedPanel.id, "detail-view", "Should be on the right view");
is(gBrowser.canGoBack, canGoBack, "canGoBack should be correct");
is(!doc.getElementById("back-btn").disabled, canGoBack, "Back button should have the right state");
is(gBrowser.canGoForward, canGoForward, "canGoForward should be correct");
is(!doc.getElementById("forward-btn").disabled, canGoForward, "Forward button should have the right state");
}
// Tests simple forward and back navigation and that the right heading and
// category is selected
add_test(function() {
open_manager("addons://list/extension", function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/extension", false, false);
EventUtils.synthesizeMouse(aManager.document.getElementById("category-plugins"), 2, 2, { }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_list(aManager, "addons://list/plugin", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 3");
is_in_list(aManager, "addons://list/extension", false, true);
gBrowser.goForward();
wait_for_view_load(aManager, function(aManager) {
info("Part 4");
is_in_list(aManager, "addons://list/plugin", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 5");
is_in_list(aManager, "addons://list/extension", false, true);
EventUtils.synthesizeMouse(get_addon_element(aManager, "test1@tests.mozilla.org"),
2, 2, { clickCount: 2 }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 6");
is_in_detail(aManager, "addons://list/extension", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 7");
is_in_list(aManager, "addons://list/extension", false, true);
close_manager(aManager, run_next_test);
});
});
});
});
});
});
});
});
// Tests that browsing to the add-ons manager from a website and going back works
add_test(function() {
info("Part 1");
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI("http://example.com/");
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "http://example.com/")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
//Must let the load complete for it to go into the session history
executeSoon(function() {
info("Part 2");
ok(!gBrowser.canGoBack, "Should not be able to go back");
ok(!gBrowser.canGoForward, "Should not be able to go forward");
gBrowser.loadURI("about:addons");
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "about:addons")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, true);
wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
info("Part 3");
is_in_list(aManager, "addons://list/extension", true, false);
gBrowser.goBack();
gBrowser.addEventListener("pageshow", function() {
gBrowser.removeEventListener("pageshow", arguments.callee, false);
info("Part 4");
is(gBrowser.currentURI.spec, "http://example.com/", "Should be showing the webpage");
ok(!gBrowser.canGoBack, "Should not be able to go back");
ok(gBrowser.canGoForward, "Should be able to go forward");
gBrowser.goForward();
gBrowser.addEventListener("pageshow", function() {
gBrowser.removeEventListener("pageshow", arguments.callee, false);
wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
info("Part 5");
is_in_list(aManager, "addons://list/extension", true, false);
close_manager(aManager, run_next_test);
});
}, false);
}, false);
});
}, true);
});
}, false);
});
// Tests that opening a custom first view only stores a single history entry
add_test(function() {
open_manager("addons://list/plugin", function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/plugin", false, false);
EventUtils.synthesizeMouse(aManager.document.getElementById("category-extensions"), 2, 2, { }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_list(aManager, "addons://list/extension", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 3");
is_in_list(aManager, "addons://list/plugin", false, true);
close_manager(aManager, run_next_test);
});
});
});
});
// Tests that opening a view while the manager is already open adds a new
// history entry
add_test(function() {
open_manager("addons://list/extension", function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/extension", false, false);
aManager.loadView("addons://list/plugin");
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_list(aManager, "addons://list/plugin", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 3");
is_in_list(aManager, "addons://list/extension", false, true);
gBrowser.goForward();
wait_for_view_load(aManager, function(aManager) {
info("Part 4");
is_in_list(aManager, "addons://list/plugin", true, false);
close_manager(aManager, run_next_test);
});
});
});
});
});
// Tests than navigating to a website and then going back returns to the
// previous view
add_test(function() {
open_manager("addons://list/plugin", function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/plugin", false, false);
gBrowser.loadURI("http://example.com/");
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "http://example.com/")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
info("Part 2");
executeSoon(function() {
ok(gBrowser.canGoBack, "Should be able to go back");
ok(!gBrowser.canGoForward, "Should not be able to go forward");
gBrowser.goBack();
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "about:addons")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
info("Part 3");
is_in_list(aManager, "addons://list/plugin", false, true);
gBrowser.goForward();
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "http://example.com/")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
info("Part 4");
executeSoon(function() {
ok(gBrowser.canGoBack, "Should be able to go back");
ok(!gBrowser.canGoForward, "Should not be able to go forward");
gBrowser.goBack();
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "about:addons")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
info("Part 5");
is_in_list(aManager, "addons://list/plugin", false, true);
close_manager(aManager, run_next_test);
});
}, false);
});
}, false);
});
}, false);
});
}, false);
});
});
// Tests that going back to search results works
add_test(function() {
open_manager("addons://list/extension", function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/extension", false, false);
var search = aManager.document.getElementById("header-search");
search.focus();
search.value = "bar";
EventUtils.synthesizeKey("VK_RETURN", {});
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_search(aManager, "bar", true, false);
check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
EventUtils.synthesizeMouse(get_addon_element(aManager, "test2@tests.mozilla.org"),
2, 2, { clickCount: 2 }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 3");
is_in_detail(aManager, "addons://search/", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 4");
is_in_search(aManager, "bar", true, true);
check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
gBrowser.goForward();
wait_for_view_load(aManager, function(aManager) {
info("Part 5");
is_in_detail(aManager, "addons://search/", true, false);
close_manager(aManager, run_next_test);
});
});
});
});
});
});
// Tests that going back to a detail view loaded from a search result works
add_test(function() {
open_manager(null, function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/extension", false, false);
var search = aManager.document.getElementById("header-search");
search.focus();
search.value = "bar";
EventUtils.synthesizeKey("VK_RETURN", {});
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_search(aManager, "bar", true, false);
check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
EventUtils.synthesizeMouse(get_addon_element(aManager, "test2@tests.mozilla.org"),
2, 2, { clickCount: 2 }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 3");
is_in_detail(aManager, "addons://search/", true, false);
gBrowser.loadURI("http://example.com/");
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "http://example.com/")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
info("Part 4");
executeSoon(function() {
ok(gBrowser.canGoBack, "Should be able to go back");
ok(!gBrowser.canGoForward, "Should not be able to go forward");
gBrowser.goBack();
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "about:addons")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
info("Part 5");
is_in_detail(aManager, "addons://search/", true, true);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 6");
is_in_search(aManager, "bar", true, true);
check_all_in_list(aManager, ["test2@tests.mozilla.org", "test3@tests.mozilla.org"]);
close_manager(aManager, run_next_test);
});
});
}, false);
});
}, false);
});
});
});
});
// Tests that refreshing a list view does not affect the history
add_test(function() {
open_manager(null, function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/extension", false, false);
EventUtils.synthesizeMouse(aManager.document.getElementById("category-plugins"), 2, 2, { }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_list(aManager, "addons://list/plugin", true, false);
gBrowser.reload();
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "about:addons")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
info("Part 3");
is_in_list(aManager, "addons://list/plugin", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 4");
is_in_list(aManager, "addons://list/extension", false, true);
close_manager(aManager, run_next_test);
});
});
}, false);
});
});
});
// Tests that refreshing a detail view does not affect the history
add_test(function() {
open_manager(null, function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/extension", false, false);
EventUtils.synthesizeMouse(get_addon_element(aManager, "test1@tests.mozilla.org"),
2, 2, { clickCount: 2 }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_detail(aManager, "addons://list/extension", true, false);
gBrowser.reload();
gBrowser.addEventListener("pageshow", function(event) {
if (event.target.location != "about:addons")
return;
gBrowser.removeEventListener("pageshow", arguments.callee, false);
wait_for_view_load(gBrowser.contentWindow.wrappedJSObject, function(aManager) {
info("Part 3");
is_in_detail(aManager, "addons://list/extension", true, false);
gBrowser.goBack();
wait_for_view_load(aManager, function(aManager) {
info("Part 4");
is_in_list(aManager, "addons://list/extension", false, true);
close_manager(aManager, run_next_test);
});
});
}, false);
});
});
});
// Tests that removing an extension from the detail view goes back and doesn't
// allow you to go forward again.
add_test(function() {
open_manager(null, function(aManager) {
info("Part 1");
is_in_list(aManager, "addons://list/extension", false, false);
EventUtils.synthesizeMouse(get_addon_element(aManager, "test1@tests.mozilla.org"),
2, 2, { clickCount: 2 }, aManager);
wait_for_view_load(aManager, function(aManager) {
info("Part 2");
is_in_detail(aManager, "addons://list/extension", true, false);
EventUtils.synthesizeMouse(aManager.document.getElementById("detail-uninstall"),
2, 2, { }, aManager);
wait_for_view_load(aManager, function() {
// TODO until bug 590661 is fixed the back button will be enabled
is_in_list(aManager, "addons://list/extension", true, false);
close_manager(aManager, run_next_test);
});
});
});
});

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

@ -437,10 +437,8 @@ add_test(function() {
var version = gManagerWindow.document.getElementById("detail-version").value;
is(version, item.mAddon.version, "Version in detail view should be correct");
var headerLink = gManagerWindow.document.getElementById("header-link");
is(headerLink.hidden, false, "Header link should be showing in detail view");
EventUtils.synthesizeMouse(headerLink, 2, 2, { }, gManagerWindow);
EventUtils.synthesizeMouse(gManagerWindow.document.getElementById("back-btn"),
2, 2, { }, gManagerWindow);
wait_for_view_load(gManagerWindow, run_next_double_click_test);
});
}

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

@ -20,6 +20,8 @@ const PREF_SEARCH_MAXRESULTS = "extensions.getAddons.maxResults";
var gPendingTests = [];
var gTestsRun = 0;
var gUseInContentUI = ("switchToTabHavingURI" in window);
// Turn logging on for all tests
Services.prefs.setBoolPref(PREF_LOGGING_ENABLED, true);
// Turn off remote results in searches
@ -71,6 +73,49 @@ function get_addon_file_url(aFilename) {
}
}
function check_all_in_list(aManager, aIds, aIgnoreExtras) {
var doc = aManager.document;
var view = doc.getElementById("view-port").selectedPanel;
var listid = view.id == "search-view" ? "search-list" : "addon-list";
var list = doc.getElementById(listid);
var inlist = [];
var node = list.firstChild;
while (node) {
if (node.value)
inlist.push(node.value);
node = node.nextSibling;
}
for (var i = 0; i < aIds.length; i++) {
if (inlist.indexOf(aIds[i]) == -1)
ok(false, "Should find " + aIds[i] + " in the list");
}
if (aIgnoreExtras)
return;
for (i = 0; i < inlist.length; i++) {
if (aIds.indexOf(inlist[i]) == -1)
ok(false, "Shouldn't have seen " + inlist[i] + " in the list");
}
}
function get_addon_element(aManager, aId) {
var doc = aManager.document;
var view = doc.getElementById("view-port").selectedPanel;
var listid = view.id == "search-view" ? "search-list" : "addon-list";
var list = doc.getElementById(listid);
var node = list.firstChild;
while (node) {
if (node.value == aId)
return node;
node = node.nextSibling;
}
return null;
}
function wait_for_view_load(aManagerWindow, aCallback, aForceWait) {
if (!aForceWait && !aManagerWindow.gViewController.isLoading) {
aCallback(aManagerWindow);
@ -89,6 +134,7 @@ function wait_for_manager_load(aManagerWindow, aCallback) {
return;
}
info("Waiting for initialization");
aManagerWindow.document.addEventListener("Initialized", function() {
aManagerWindow.document.removeEventListener("Initialized", arguments.callee, false);
aCallback(aManagerWindow);
@ -111,7 +157,7 @@ function open_manager(aView, aCallback, aLoadCallback) {
});
}
if ("switchToTabHavingURI" in window) {
if (gUseInContentUI) {
gBrowser.selectedTab = gBrowser.addTab();
switchToTabHavingURI(MANAGER_URI, true, function(aBrowser) {
setup_manager(aBrowser.contentWindow.wrappedJSObject);

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

@ -46,6 +46,42 @@
-moz-border-radius: 5px;
}
.nav-button {
-moz-appearance: toolbarbutton;
min-width: 0;
}
#back-btn:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar");
}
#forward-btn:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar");
}
#back-btn:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar");
}
#forward-btn:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar");
}
#back-btn[disabled="true"]:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled");
}
#forward-btn[disabled="true"]:-moz-locale-dir(ltr) {
list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
}
#back-btn[disabled="true"]:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled");
}
#forward-btn[disabled="true"]:-moz-locale-dir(rtl) {
list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
}
/*** category selector ***/
@ -155,7 +191,6 @@
#header {
margin-bottom: 20px;
height: 2em;
}
#header-name, #header-link {

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

@ -35,6 +35,8 @@
*
* ***** END LICENSE BLOCK ***** */
%include ../../global/shared.inc
#addons-page {
background-color: #c5ccd7;
-moz-appearance: none;
@ -47,6 +49,52 @@
-moz-border-radius: 5px;
}
.nav-button {
-moz-appearance: none;
min-width: 0;
padding: 0 4px;
margin: 0;
text-shadow: @loweredShadow@;
border: 1px solid @toolbarbuttonBorderColor@;
-moz-box-shadow: @loweredShadow@;
background: @toolbarbuttonBackground@;
background-origin: border-box;
list-style-image: url(chrome://mozapps/skin/extensions/navigation.png);
}
.nav-button .button-text {
display: none;
}
.nav-button[disabled="true"] .button-icon {
opacity: 0.4;
}
#back-btn:-moz-locale-dir(ltr),
#forward-btn:-moz-locale-dir(rtl) {
-moz-border-radius-topleft: @toolbarbuttonCornerRadius@;
-moz-border-radius-bottomleft: @toolbarbuttonCornerRadius@;
border-right: none;
-moz-image-region: rect(0, 20px, 20px, 0);
}
#back-btn:-moz-locale-dir(rtl),
#forward-btn:-moz-locale-dir(ltr) {
-moz-border-radius-topright: @toolbarbuttonCornerRadius@;
-moz-border-radius-bottomright: @toolbarbuttonCornerRadius@;
-moz-image-region: rect(0, 40px, 20px, 20px);
}
.nav-button:hover:active:not([disabled="true"]) {
background: @toolbarbuttonPressedBackgroundColor@;
text-shadow: @loweredShadow@;
-moz-box-shadow: @toolbarbuttonPressedInnerShadow@, @loweredShadow@;
}
.nav-button:-moz-window-inactive {
border-color: @toolbarbuttonInactiveBorderColor@;
background-image: @toolbarbuttonInactiveBackgroundImage@;
}
/*** category selector ***/
@ -156,7 +204,6 @@
#header {
margin-bottom: 20px;
height: 2em;
}
#header-name, #header-link {

Двоичные данные
toolkit/themes/pinstripe/mozapps/extensions/navigation.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 586 B

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

@ -26,8 +26,9 @@ toolkit.jar:
skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
skin/classic/mozapps/extensions/pause.png (extensions/pause.png)
skin/classic/mozapps/extensions/utilities.png (extensions/utilities.png)
skin/classic/mozapps/extensions/navigation.png (extensions/navigation.png)
skin/classic/mozapps/extensions/about.css (extensions/about.css)
skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
* skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
skin/classic/mozapps/extensions/update.css (extensions/update.css)
skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/mozapps/extensions/blocklist.css (extensions/blocklist.css)

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

@ -47,6 +47,21 @@
-moz-border-radius: 5px;
}
.nav-button {
list-style-image: url(chrome://mozapps/skin/extensions/navigation.png);
min-width: 0;
-moz-margin-end: 0;
}
#back-btn:-moz-locale-dir(ltr),
#forward-btn:-moz-locale-dir(rtl) {
-moz-image-region: rect(0, 18px, 18px, 0);
}
#back-btn:-moz-locale-dir(rtl),
#forward-btn:-moz-locale-dir(ltr) {
-moz-image-region: rect(0, 36px, 18px, 18px);
}
/*** category selector ***/
@ -156,7 +171,6 @@
#header {
margin-bottom: 20px;
height: 2em;
}
#header-name, #header-link {

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

@ -32,6 +32,7 @@ toolkit.jar:
skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
skin/classic/mozapps/extensions/pause.png (extensions/pause.png)
skin/classic/mozapps/extensions/utilities.png (extensions/utilities.png)
skin/classic/mozapps/extensions/navigation.png (extensions/navigation.png)
skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/mozapps/handling/handling.css (handling/handling.css)
skin/classic/mozapps/passwordmgr/key.png (passwordmgr/key.png)
@ -95,6 +96,7 @@ toolkit.jar:
skin/classic/aero/mozapps/extensions/cancel.png (extensions/cancel.png)
skin/classic/aero/mozapps/extensions/pause.png (extensions/pause.png)
skin/classic/aero/mozapps/extensions/utilities.png (extensions/utilities.png)
skin/classic/aero/mozapps/extensions/navigation.png (extensions/navigation.png)
skin/classic/aero/mozapps/extensions/eula.css (extensions/eula.css)
skin/classic/aero/mozapps/handling/handling.css (handling/handling.css)
skin/classic/aero/mozapps/passwordmgr/key.png (passwordmgr/key-aero.png)