1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is MozMill Test code.
16 * The Initial Developer of the Original Code is the Mozilla Foundation.
17 * Portions created by the Initial Developer are Copyright (C) 2009
18 * the Initial Developer. All Rights Reserved.
21 * Henrik Skupin <hskupin@mozilla.com>
22 * Geo Mealer <gmealer@mozilla.com>
24 * Alternatively, the contents of this file may be used under the terms of
25 * either the GNU General Public License Version 2 or later (the "GPL"), or
26 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 // Include required modules
39 var domUtils = require("dom-utils");
40 var prefs = require("prefs");
41 var tabs = require("tabs");
42 var utils = require("utils");
46 const TIMEOUT_DOWNLOAD = 15000;
47 const TIMEOUT_SEARCH = 30000;
49 var pm = Cc["@mozilla.org/permissionmanager;1"].
50 getService(Ci.nsIPermissionManager);
53 const AMO_PREVIEW_DOMAIN = "addons.allizom.org";
54 const AMO_PREVIEW_SITE = "https://" + AMO_PREVIEW_DOMAIN;
56 // Available search filters
57 const SEARCH_FILTER = [
62 // Preferences which have to be changed to make sure we do not interact with the
63 // official AMO page but the preview site instead
64 const AMO_PREFERENCES = [
65 {name: "extensions.getAddons.browseAddons", old: "addons.mozilla.org", new: AMO_PREVIEW_DOMAIN},
66 {name: "extensions.getAddons.recommended.browseURL", old: "addons.mozilla.org", new: AMO_PREVIEW_DOMAIN},
67 {name: "extensions.getAddons.recommended.url", old: "services.addons.mozilla.org", new: AMO_PREVIEW_DOMAIN},
68 {name: "extensions.getAddons.search.browseURL", old: "addons.mozilla.org", new: AMO_PREVIEW_DOMAIN},
69 {name: "extensions.getAddons.search.url", old: "services.addons.mozilla.org", new: AMO_PREVIEW_DOMAIN},
70 {name: "extensions.getMoreThemesURL", old: "addons.mozilla.org", new: AMO_PREVIEW_DOMAIN}
76 function addonsManager(aController) {
77 this._controller = aController;
78 this._tabBrowser = new tabs.tabBrowser(this._controller);
82 * Addons Manager class
84 addonsManager.prototype = {
86 ///////////////////////////////
88 ///////////////////////////////
91 * Get the controller of the window
93 * @returns Mozmill Controller
94 * @type {MozMillController}
97 return this._controller;
101 * Gets all the needed external DTD urls as an array
103 * @returns URL's of external DTD files
104 * @type {array of string}
108 "chrome://mozapps/locale/extensions/extensions.dtd",
109 "chrome://browser/locale/browser.dtd"
116 * Open the Add-ons Manager
118 * @param {object} aSpec
119 * Information how to open the Add-ons Manager
120 * Elements: type - Event, can be menu, or shortcut
121 * [optional - default: menu]
122 * waitFor - Wait until the Add-ons Manager has been opened
123 * [optional - default: true]
126 * @returns Reference the tab with the Add-ons Manager open
128 * Elements: controller - Mozmill Controller of the window
129 * index - Index of the tab
131 open : function addonsManager_open(aSpec) {
132 var spec = aSpec || { };
133 var type = (spec.type == undefined) ? "menu" : spec.type;
134 var waitFor = (spec.waitFor == undefined) ? true : spec.waitFor;
138 var menuItem = new elementslib.Elem(this._controller.
139 menus["tools-menu"].menu_openAddons);
140 this._controller.click(menuItem);
143 var cmdKey = utils.getEntity(this.dtds, "addons.commandkey");
144 this._controller.keypress(null, cmdKey, {accelKey: true, shiftKey: true});
147 throw new Error(arguments.callee.name + ": Unknown event type - " +
151 return waitFor ? this.waitForOpened() : null;
155 * Check if the Add-ons Manager is open
157 * @returns True if the Add-ons Manager is open
161 return (this.getTabs().length > 0);
165 * Waits until the Addons Manager has been opened and returns its controller
167 * @param {object} aSpec
168 * Object with parameters for customization
169 * Elements: timeout - Duration to wait for the target state
170 * [optional - default: 5s]
172 * @returns Currently selected tab
174 waitForOpened : function addonsManager_waitforOpened(aSpec) {
175 var spec = aSpec || { };
176 var timeout = (spec.timeout == undefined) ? TIMEOUT : spec.timeout;
178 // TODO: restore after 1.5.1 has landed
181 // mozmill.utils.waitFor(function() {
182 // return self.isOpen;
183 // }, timeout, 100, "Add-ons Manager has been opened");
185 mozmill.utils.waitForEval("subject.isOpen", timeout, 100, this);
187 // The first tab found will be the selected one
188 var tab = this.getTabs()[0];
189 tab.controller.waitForPageLoad();
195 * Close the Addons Manager
197 * @param {object} aSpec
198 * Information about the event to send
199 * Elements: type - Event type (closeButton, menu, middleClick, shortcut)
201 close : function addonsManager_close(aSpec) {
202 this._tabBrowser.closeTab(aSpec);
206 * Retrieves the list of open add-ons manager tabs
208 * @returns List of open tabs
209 * @type {array of object}
210 * Elements: controller - MozMillController
211 * index - Index of the tab
213 getTabs : function addonsManager_getTabs() {
214 return tabs.getTabsWithURL("about:addons");
218 * Opens the utils button menu and clicks the specified menu entry
220 * @param {object} aSpec
221 * Information about the menu
222 * Elements: item - menu item to click (updateNow, viewUpdates,
223 * installFromFile, autoUpdateDefault,
224 * resetAddonUpdatesToAutomatic,
225 * resetAddonUpdatesToManual)
227 handleUtilsButton : function addonsManager_handleUtilsButton(aSpec) {
228 var spec = aSpec || { };
229 var item = spec.item;
232 throw new Error(arguments.callee.name + ": Menu item not specified.");
234 var button = this.getElement({type: "utilsButton"});
235 var menu = this.getElement({type: "utilsButton_menu"});
238 this._controller.click(button);
240 // Click the button and wait until menu has been opened
242 // TODO: restore after 1.5.1 has landed
243 // mozmill.utils.waitFor(function() {
244 // return menu.getNode() && menu.getNode().state == "open";
245 // }, TIMEOUT, 100, "Menu of utils button has been opened.");
247 mozmill.utils.waitForEval("subject && subject.state == 'open'",
248 TIMEOUT, 100, menu.getNode());
250 // Click the given menu entry and make sure the
251 var menuItem = this.getElement({
252 type: "utilsButton_menuItem",
253 value: "#utils-" + item
256 this._controller.click(menuItem);
258 // Make sure the menu has been closed
259 this._controller.keypress(menu, "VK_ESCAPE", {});
261 // TODO: restore after 1.5.1 has landed
262 // mozmill.utils.waitFor(function() {
263 // return menu.getNode() && menu.getNode().state == "closed";
264 // }, TIMEOUT, 100, "Menu of utils button has been closed.");
266 mozmill.utils.waitForEval("subject && subject.state == 'closed'",
267 TIMEOUT, 100, menu.getNode());
272 ///////////////////////////////
274 ///////////////////////////////
277 * Check if the specified add-on is compatible
279 * @param {object} aSpec
280 * Information on which add-on to operate on
281 * Elements: addon - Add-on element
283 * @returns True if the add-on is compatible
286 isAddonCompatible : function addonsManager_isAddonCompatible(aSpec) {
287 var spec = aSpec || { };
288 var addon = spec.addon;
291 throw new Error(arguments.callee.name + ": Add-on not specified.");
293 // XXX: Bug 599702 doens't give enough information which type of notification
294 return addon.getNode().getAttribute("notification") != "warning";
298 * Check if the specified add-on is enabled
300 * @param {object} aSpec
301 * Information on which add-on to operate on
302 * Elements: addon - Add-on element
304 * @returns True if the add-on is enabled
307 isAddonEnabled : function addonsManager_isAddonEnabled(aSpec) {
308 var spec = aSpec || { };
309 var addon = spec.addon;
312 throw new Error(arguments.callee.name + ": Add-on not specified.");
314 return addon.getNode().getAttribute("active") == "true";
318 * Check if the specified add-on is installed
320 * @param {object} aSpec
321 * Information on which add-on to operate on
322 * Elements: addon - Add-on element
324 * @returns True if the add-on is installed
327 isAddonInstalled : function addonsManager_isAddonInstalled(aSpec) {
328 var spec = aSpec || { };
329 var addon = spec.addon;
332 throw new Error(arguments.callee.name + ": Add-on not specified.");
334 // Bug 600502 : Add-ons in search view are not initialized correctly
335 return addon.getNode().getAttribute("remote") == "false" &&
336 addon.getNode().getAttribute("status") == "installed";
340 * Enables the specified add-on
342 * @param {object} aSpec
343 * Information on which add-on to operate on
344 * Elements: addon - Add-on element
346 enableAddon : function addonsManager_enableAddon(aSpec) {
347 var spec = aSpec || { };
348 spec.button = "enable";
350 var button = this.getAddonButton(spec);
351 this._controller.click(button);
355 * Disables the specified add-on
357 * @param {object} aSpec
358 * Information on which add-on to operate on
359 * Elements: addon - Add-on element
361 disableAddon : function addonsManager_disableAddon(aSpec) {
362 var spec = aSpec || { };
363 spec.button = "disable";
365 var button = this.getAddonButton(spec);
366 this._controller.click(button);
370 * Installs the specified add-on
372 * @param {object} aSpec
373 * Information on which add-on to operate on
374 * Elements: addon - Add-on element
375 * waitFor - Wait until the category has been selected
376 * [optional - default: true]
377 * timeout - Duration to wait for the download
378 * [optional - default: 15s]
380 installAddon : function addonsManager_installAddon(aSpec) {
381 var spec = aSpec || { };
382 var addon = spec.addon;
383 var timeout = spec.timeout;
384 var button = "install";
385 var waitFor = (spec.waitFor == undefined) ? true : spec.waitFor;
387 var button = this.getAddonButton({addon: addon, button: button});
388 this._controller.click(button);
391 this.waitForDownloaded({addon: addon, timeout: timeout});
395 * Removes the specified add-on
397 * @param {object} aSpec
398 * Information on which add-on to operate on
399 * Elements: addon - Add-on element
401 removeAddon : function addonsManager_removeAddon(aSpec) {
402 var spec = aSpec || { };
403 spec.button = "remove";
405 var button = this.getAddonButton(spec);
406 this._controller.click(button);
410 * Undo the last action performed for the given add-on
412 * @param {object} aSpec
413 * Information on which add-on to operate on
414 * Elements: addon - Add-on element
416 undo : function addonsManager_undo(aSpec) {
417 var spec = aSpec || { };
420 var link = this.getAddonLink(spec);
421 this._controller.click(link);
425 * Returns the addons from the currently selected view which match the
428 * @param {object} aSpec
429 * Information about the filter to apply
430 * Elements: attribute - DOM attribute of the wanted addon
431 * [optional - default: ""]
432 * value - Value of the DOM attribute
433 * [optional - default: ""]
435 * @returns List of addons
436 * @type {array of ElemBase}
438 getAddons : function addonsManager_addons(aSpec) {
439 var spec = aSpec || {};
441 return this.getElements({
443 subtype: spec.attribute,
445 parent: this.selectedView
450 * Returns the element of the specified add-ons button
452 * @param {object} aSpec
453 * Information on which add-on to operate on
454 * Elements: addon - Add-on element
455 * button - Button (disable, enable, preferences, remove)
457 * @returns Add-on button
460 getAddonButton : function addonsManager_getAddonButton(aSpec) {
461 var spec = aSpec || { };
462 var addon = spec.addon;
463 var button = spec.button;
466 throw new Error(arguments.callee.name + ": Button not specified.");
468 return this.getAddonChildElement({addon: addon, type: button + "Button"});
472 * Returns the element of the specified add-ons link
474 * @param {object} aSpec
475 * Information on which add-on to operate on
476 * Elements: addon - Add-on element
478 * List view (more, restart, undo)
479 * Detail view (findUpdates, restart, undo)
481 * @return Add-on link
484 getAddonLink : function addonsManager_getAddonLink(aSpec) {
485 var spec = aSpec || { };
486 var addon = spec.addon;
487 var link = spec.link;
490 throw new Error(arguments.callee.name + ": Link not specified.");
492 return this.getAddonChildElement({addon: addon, type: link + "Link"});
496 * Returns the element of the specified add-ons radio group
498 * @param {object} aSpec
499 * Information on which add-on to operate on
500 * Elements: addon - Add-on element
501 * radiogroup - Radiogroup
502 * Detail View (autoUpdate)
504 * @returns Add-on radiogroup
507 getAddonRadiogroup : function addonsManager_getAddonRadiogroup(aSpec) {
508 var spec = aSpec || { };
509 var addon = spec.addon;
510 var radiogroup = spec.radiogroup;
513 throw new Error(arguments.callee.name + ": Radiogroup not specified.");
515 return this.getAddonChildElement({addon: addon, type: radiogroup + "Radiogroup"});
519 * Retrieve the given child element of the specified add-on
521 * @param {object} aSpec
522 * Information for getting the add-ons child node
523 * Elements: addon - Add-on element
524 * type - Type of the element
525 * [optional - default: use attribute/value]
526 * attribute - DOM attribute of the node
527 * value - Value of the DOM attribute
532 getAddonChildElement : function addonsManager_getAddonChildElement(aSpec) {
533 var spec = aSpec || { };
534 var addon = spec.addon;
535 var attribute = spec.attribute;
536 var value = spec.value;
537 var type = spec.type;
540 throw new Error(arguments.callee.name + ": Add-on not specified.");
542 // If no type has been set retrieve a general element which needs an
543 // attribute and value
548 throw new Error(arguments.callee.name + ": DOM attribute not specified.");
550 throw new Error(arguments.callee.name + ": Value not specified.");
553 // For the details view the elements don't have anonymous nodes
554 if (this.selectedView.getNode().id == "detail-view") {
555 return this.getElement({
556 type: "detailView_" + type,
561 return this.getElement({
562 type: "listView_" + type,
571 * Wait until the specified add-on has been downloaded
573 * @param {object} aSpec
574 * Object with parameters for customization
575 * Elements: addon - Add-on element to wait for being downloaded
576 * timeout - Duration to wait for the target state
577 * [optional - default: 15s]
579 waitForDownloaded : function addonsManager_waitForDownloaded(aSpec) {
580 var spec = aSpec || { };
581 var addon = spec.addon;
582 var timeout = (spec.timeout == undefined) ? TIMEOUT_DOWNLOAD : spec.timeout;
585 throw new Error(arguments.callee.name + ": Add-on not specified.");
588 var node = addon.getNode();
590 // TODO: restore after 1.5.1 has landed
591 // mozmill.utils.waitFor(function () {
592 // return node.getAttribute("pending") == "install" &&
593 // node.getAttribute("status") != "installing";
594 // }, timeout, 100, "'" + node.getAttribute("name") + "' has been downloaded");
596 mozmill.utils.waitForEval("subject.getAttribute('pending') == 'install' &&" +
597 "subject.getAttribute('status') != 'installing'",
602 ///////////////////////////////
604 ///////////////////////////////
607 * Retrieve the currently selected category
609 * @returns Element which represents the currently selected category
612 get selectedCategory() {
613 return this.getCategories({attribute: "selected", value: "true"})[0];
617 * Returns the categories which match the filter criteria
619 * @param {object} aSpec
620 * Information about the filter to apply
621 * Elements: attribute - DOM attribute of the wanted category
622 * [optional - default: ""]
623 * value - Value of the DOM attribute
624 * [optional - default: ""]
626 * @returns List of categories
627 * @type {array of ElemBase}
629 getCategories : function addonsManager_categories(aSpec) {
630 var spec = aSpec || { };
632 var categories = this.getElements({
634 subtype: spec.attribute,
638 if (categories.length == 0)
639 throw new Error(arguments.callee.name + ": Categories could not be found.");
645 * Get the category element for the specified id
647 * @param {object} aSpec
648 * Information for getting a category
649 * Elements: id - Category id (search, discover, languages,
650 * searchengines, extensions, themes, plugins,
651 * availableUpdates, recentUpdates)
656 getCategoryById : function addonsManager_getCategoryById(aSpec) {
657 var spec = aSpec || { };
661 throw new Error(arguments.callee.name + ": Category ID not specified.");
663 return this.getCategories({
665 value: "category-" + id
670 * Get the ID of the given category element
672 * @param {object} aSpec
673 * Information for getting a category
674 * Elements: category - Category to get the id from
676 * @returns Category Id
679 getCategoryId : function addonsManager_getCategoryId(aSpec) {
680 var spec = aSpec || { };
681 var category = spec.category;
684 throw new Error(arguments.callee.name + ": Category not specified.");
686 return category.getNode().id;
690 * Select the given category
692 * @param {object} aSpec
693 * Information for selecting a category
694 * Elements: category - Category element
695 * waitFor - Wait until the category has been selected
696 * [optional - default: true]
698 setCategory : function addonsManager_setCategory(aSpec) {
699 var spec = aSpec || { };
700 var category = spec.category;
701 var waitFor = (spec.waitFor == undefined) ? true : spec.waitFor;
704 throw new Error(arguments.callee.name + ": Category not specified.");
706 this._controller.click(category);
709 this.waitForCategory({category: category});
713 * Select the category with the given id
715 * @param {object} aSpec
716 * Information for selecting a category
717 * Elements: id - Category id (search, discover, languages,
718 * searchengines, extensions, themes, plugins,
719 * availableUpdates, recentUpdates)
720 * waitFor - Wait until the category has been selected
721 * [optional - default: true]
723 setCategoryById : function addonsManager_setCategoryById(aSpec) {
724 var spec = aSpec || { };
726 var waitFor = (spec.waitFor == undefined) ? true : spec.waitFor;
729 throw new Error(arguments.callee.name + ": Category ID not specified.");
731 // Retrieve the category and set it as active
732 var category = this.getCategoryById({id: id});
734 this.setCategory({category: category, waitFor: waitFor});
736 throw new Error(arguments.callee.name + ": Category '" + id + " not found.");
740 * Wait until the specified category has been selected
742 * @param {object} aSpec
743 * Object with parameters for customization
744 * Elements: category - Category element to wait for
745 * timeout - Duration to wait for the target state
746 * [optional - default: 5s]
748 waitForCategory : function addonsManager_waitForCategory(aSpec) {
749 var spec = aSpec || { };
750 var category = spec.category;
751 var timeout = (spec.timeout == undefined) ? TIMEOUT : spec.timeout;
754 throw new Error(arguments.callee.name + ": Category not specified.");
756 // TODO: restore after 1.5.1 has landed
758 // mozmill.utils.waitFor(function () {
759 // return self.selectedCategory.getNode() == category.getNode();
760 // }, timeout, 100, "Category '" + category.getNode().id + "' has been set");
762 mozmill.utils.waitForEval("subject.self.selectedCategory.getNode() == subject.aCategory.getNode()",
764 {self: this, aCategory: category});
767 ///////////////////////////////
769 ///////////////////////////////
772 * Clear the search field
774 clearSearchField : function addonsManager_clearSearchField() {
775 var textbox = this.getElement({type: "search_textbox"});
776 var cmdKey = utils.getEntity(this.dtds, "selectAllCmd.key");
778 this._controller.keypress(textbox, cmdKey, {accelKey: true});
779 this._controller.keypress(textbox, 'VK_DELETE', {});
783 * Search for a specified add-on
785 * @param {object} aSpec
786 * Information to execute the search
787 * Elements: value - Search term
788 * timeout - Duration to wait for search results
789 * [optional - default: 30s]
790 * waitFor - Wait until the search has been finished
791 * [optional - default: true]
793 search : function addonsManager_search(aSpec) {
794 var spec = aSpec || { };
795 var value = spec.value;
796 var timeout = (spec.timeout == undefined) ? TIMEOUT_SEARCH : spec.timeout;
797 var waitFor = (spec.waitFor == undefined) ? true : spec.waitFor;
800 throw new Error(arguments.callee.name + ": Search term not specified.");
802 var textbox = this.getElement({type: "search_textbox"});
804 this.clearSearchField();
805 this._controller.type(textbox, value);
806 this._controller.keypress(textbox, "VK_RETURN", {});
809 this.waitForSearchFinished();
813 * Check if a search is active
815 * @returns State of the search
819 var throbber = this.getElement({type: "search_throbber"});
820 return throbber.getNode().hasAttribute("active");
824 * Retrieve the currently selected search filter
826 * @returns Element which represents the currently selected search filter
829 get selectedSearchFilter() {
830 var filter = this.getSearchFilter({attribute: "selected", value: "true"});
832 return (filter.length > 0) ? filter[0] : undefined;
836 * Set the currently selected search filter status
838 * @param {string} aValue
839 * Filter for the search results (local, remote)
841 set selectedSearchFilter(aValue) {
842 var filter = this.getSearchFilter({attribute: "value", value: aValue});
844 if (SEARCH_FILTER.indexOf(aValue) == -1)
845 throw new Error(arguments.callee.name + ": '" + aValue +
846 "' is not a valid search filter");
848 if (filter.length > 0) {
849 this._controller.click(filter[0]);
850 this.waitForSearchFilter({filter: filter[0]});
855 * Returns the available search filters which match the filter criteria
857 * @param {object} aSpec
858 * Information about the filter to apply
859 * Elements: attribute - DOM attribute of the wanted filter
860 * [optional - default: ""]
861 * value - Value of the DOM attribute
862 * [optional - default: ""]
864 * @returns List of search filters
865 * @type {array of ElemBase}
867 getSearchFilter : function addonsManager_getSearchFilter(aSpec) {
868 var spec = aSpec || { };
870 return this.getElements({
871 type: "search_filterRadioButtons",
872 subtype: spec.attribute,
878 * Get the search filter element for the specified value
880 * @param {string} aValue
881 * Search filter value (local, remote)
883 * @returns Search filter element
886 getSearchFilterByValue : function addonsManager_getSearchFilterByValue(aValue) {
888 throw new Error(arguments.callee.name + ": Search filter value not specified.");
890 return this.getElement({
891 type: "search_filterRadioGroup",
898 * Get the value of the given search filter element
900 * @param {object} aSpec
901 * Information for getting the views matched by the criteria
902 * Elements: filter - Filter element
904 * @returns Value of the search filter
907 getSearchFilterValue : function addonsManager_getSearchFilterValue(aSpec) {
908 var spec = aSpec || { };
909 var filter = spec.filter;
912 throw new Error(arguments.callee.name + ": Search filter not specified.");
914 return filter.getNode().value;
918 * Waits until the specified search filter has been selected
920 * @param {object} aSpec
921 * Object with parameters for customization
922 * Elements: filter - Filter element to wait for
923 * timeout - Duration to wait for the target state
924 * [optional - default: 5s]
926 waitForSearchFilter : function addonsManager_waitForSearchFilter(aSpec) {
927 var spec = aSpec || { };
928 var filter = spec.filter;
929 var timeout = (spec.timeout == undefined) ? TIMEOUT : spec.timeout;
932 throw new Error(arguments.callee.name + ": Search filter not specified.");
934 // TODO: restore after 1.5.1 has landed
937 // mozmill.utils.waitFor(function () {
938 // return self.selectedSearchFilter.getNode() == filter.getNode();
939 // }, timeout, 100, "Search filter '" + filter.getNode().value + "' has been set");
941 mozmill.utils.waitForEval("subject.self.selectedSearchFilter.getNode() == subject.aFilter.getNode()",
943 {self: this, aFilter: filter});
947 * Returns the list of add-ons found by the selected filter
949 * @returns List of add-ons
952 getSearchResults : function addonsManager_getSearchResults() {
953 var filterValue = this.getSearchFilterValue({
954 filter: this.selectedSearchFilter
957 switch (filterValue) {
959 return this.getAddons({attribute: "status", value: "installed"});
961 return this.getAddons({attribute: "remote", value: "true"});
963 throw new Error(arguments.callee.name + ": Unknown search filter '" +
964 filterValue + "' selected");
969 * Waits until the active search has been finished
971 * @param {object} aSpec
972 * Object with parameters for customization
973 * Elements: timeout - Duration to wait for the target state
975 waitForSearchFinished : function addonsManager_waitForSearchFinished(aSpec) {
976 var spec = aSpec || { };
977 var timeout = (spec.timeout == undefined) ? TIMEOUT_SEARCH : spec.timeout;
979 // TODO: restore after 1.5.1 has landed
982 // mozmill.utils.waitFor(function () {
983 // return self.isSearching == false;
984 // }, timeout, 100, "Search has been finished");
986 mozmill.utils.waitForEval("subject.isSearching == false",
990 ///////////////////////////////
992 ///////////////////////////////
995 * Returns the views which match the filter criteria
997 * @param {object} aSpec
998 * Information for getting the views matched by the criteria
999 * Elements: attribute - DOM attribute of the node
1000 * [optional - default: ""]
1001 * value - Value of the DOM attribute
1002 * [optional - default: ""]
1004 * @returns Filtered list of views
1005 * @type {array of ElemBase}
1007 getViews : function addonsManager_getViews(aSpec) {
1008 var spec = aSpec || { };
1009 var attribute = spec.attribute;
1010 var value = spec.value;
1012 return this.getElements({type: "views", subtype: attribute, value: value});
1016 * Check if the details view is active
1018 * @returns True if the default view is selected
1021 get isDetailViewActive() {
1022 return (this.selectedView.getNode().id == "detail-view");
1026 * Retrieve the currently used view
1028 * @returns Element which represents the currently selected view
1031 get selectedView() {
1032 var viewDeck = this.getElement({type: "viewDeck"});
1033 var views = this.getViews();
1035 return views[viewDeck.getNode().selectedIndex];
1039 ///////////////////////////////
1040 // UI Elements section
1041 ///////////////////////////////
1044 * Retrieve an UI element based on the given specification
1046 * @param {object} aSpec
1047 * Information of the UI elements which should be retrieved
1048 * Elements: type - Identifier of the element
1049 * subtype - Attribute of the element to filter
1050 * [optional - default: ""]
1051 * value - Value of the attribute to filter
1052 * [optional - default: ""]
1053 * parent - Parent of the to find element
1054 * [optional - default: document]
1056 * @returns Element which has been found
1059 getElement : function addonsManager_getElement(aSpec) {
1060 var elements = this.getElements(aSpec);
1062 return (elements.length > 0) ? elements[0] : undefined;
1066 * Retrieve list of UI elements based on the given specification
1068 * @param {object} aSpec
1069 * Information of the UI elements which should be retrieved
1070 * Elements: type - Identifier of the element
1071 * subtype - Attribute of the element to filter
1072 * [optional - default: ""]
1073 * value - Value of the attribute to filter
1074 * [optional - default: ""]
1075 * parent - Parent of the to find element
1076 * [optional - default: document]
1078 * @returns Elements which have been found
1079 * @type {array of ElemBase}
1081 getElements : function addonsManager_getElements(aSpec) {
1082 var spec = aSpec || { };
1083 var type = spec.type;
1084 var subtype = spec.subtype;
1085 var value = spec.value;
1086 var parent = spec.parent;
1088 var root = parent ? parent.getNode() : this._controller.tabs.activeTab;
1089 var nodeCollector = new domUtils.nodeCollector(root);
1094 nodeCollector.queryNodes(".addon").filterByDOMProperty(subtype, value);
1097 nodeCollector.queryNodes("#addon-list");
1100 case "categoriesList":
1101 nodeCollector.queryNodes("#categories");
1104 nodeCollector.queryNodes(".category").filterByDOMProperty(subtype, value);
1107 case "detailView_element":
1108 nodeCollector.queryNodes(value);
1110 case "detailView_disableButton":
1111 nodeCollector.queryNodes("#detail-disable");
1113 case "detailView_enableButton":
1114 nodeCollector.queryNodes("#detail-enable");
1116 case "detailView_installButton":
1117 nodeCollector.queryNodes("#detail-install");
1119 case "detailView_preferencesButton":
1120 nodeCollector.queryNodes("#detail-prefs");
1122 case "detailView_removeButton":
1123 nodeCollector.queryNodes("#detail-uninstall");
1125 case "detailView_findUpdatesLink":
1126 nodeCollector.queryNodes("#detail-findUpdates");
1128 // Bug 599771 - button-link's are missing id or anonid
1129 //case "detailView_restartLink":
1130 // nodeCollector.queryNodes("#detail-restart");
1132 case "detailView_undoLink":
1133 nodeCollector.queryNodes("#detail-undo");
1135 case "detailView_findUpdatesRadiogroup":
1136 nodeCollector.queryNodes("#detail-findUpdates");
1139 case "listView_element":
1140 nodeCollector.queryAnonymousNodes(subtype, value);
1142 case "listView_disableButton":
1143 nodeCollector.queryAnonymousNodes("anonid", "disable-btn");
1145 case "listView_enableButton":
1146 nodeCollector.queryAnonymousNodes("anonid", "enable-btn");
1148 case "listView_installButton":
1149 // There is another binding we will have to skip
1150 nodeCollector.queryAnonymousNodes("anonid", "install-status");
1151 nodeCollector.root = nodeCollector.nodes[0];
1152 nodeCollector.queryAnonymousNodes("anonid", "install-remote");
1154 case "listView_preferencesButton":
1155 nodeCollector.queryAnonymousNodes("anonid", "preferences-btn");
1157 case "listView_removeButton":
1158 nodeCollector.queryAnonymousNodes("anonid", "remove-btn");
1160 case "listView_moreLink":
1161 // Bug 599771 - button-link's are missing id or anonid
1162 nodeCollector.queryAnonymousNodes("class", "details button-link");
1164 // Bug 599771 - button-link's are missing id or anonid
1165 //case "listView_restartLink":
1166 // nodeCollector.queryAnonymousNodes("anonid", "restart");
1168 case "listView_undoLink":
1169 nodeCollector.queryAnonymousNodes("anonid", "undo");
1171 case "listView_cancelDownload":
1172 // There is another binding we will have to skip
1173 nodeCollector.queryAnonymousNodes("anonid", "install-status");
1174 nodeCollector.root = nodeCollector.nodes[0];
1175 nodeCollector.queryAnonymousNodes("anonid", "cancel");
1177 case "listView_pauseDownload":
1178 // There is another binding we will have to skip
1179 nodeCollector.queryAnonymousNodes("anonid", "install-status");
1180 nodeCollector.root = nodeCollector.nodes[0];
1181 nodeCollector.queryAnonymousNodes("anonid", "pause");
1183 case "listView_progressDownload":
1184 // There is another binding we will have to skip
1185 nodeCollector.queryAnonymousNodes("anonid", "install-status");
1186 nodeCollector.root = nodeCollector.nodes[0];
1187 nodeCollector.queryAnonymousNodes("anonid", "progress");
1190 // Bug 599775 - Controller needs to handle radio groups correctly
1191 // Means for now we have to use the radio buttons
1192 case "search_filterRadioButtons":
1193 nodeCollector.queryNodes(".search-filter-radio").filterByDOMProperty(subtype, value);
1195 case "search_filterRadioGroup":
1196 nodeCollector.queryNodes("#search-filter-radiogroup");
1198 case "search_textbox":
1199 nodeCollector.queryNodes("#header-search");
1201 case "search_throbber":
1202 nodeCollector.queryNodes("#header-searching");
1206 nodeCollector.queryNodes("#header-utils-btn");
1208 case "utilsButton_menu":
1209 nodeCollector.queryNodes("#utils-menu");
1211 case "utilsButton_menuItem":
1212 nodeCollector.queryNodes(value);
1216 nodeCollector.queryNodes("#view-port");
1219 nodeCollector.queryNodes(".view-pane").filterByDOMProperty(subtype, value);
1222 throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
1225 return nodeCollector.elements;
1230 * Whitelist permission for the specified domain
1231 * @param {string} aDomain
1232 * The domain to add the permission for
1234 function addToWhiteList(aDomain) {
1235 pm.add(utils.createURI(aDomain),
1237 Ci.nsIPermissionManager.ALLOW_ACTION);
1241 * Remove whitelist permission for the specified host
1242 * @param {string} aHost
1243 * The host whose permission will be removed
1245 function removeFromWhiteList(aHost) {
1246 pm.remove(aHost, "install");
1250 * Reset all preferences which point to the preview sub domain
1252 function resetAmoPreviewUrls() {
1253 var prefSrv = prefs.preferences;
1255 for each (var preference in AMO_PREFERENCES) {
1256 prefSrv.clearUserPref(preference.name);
1261 * Updates all necessary preferences to the preview sub domain
1263 function useAmoPreviewUrls() {
1264 var prefSrv = prefs.preferences;
1266 for each (var preference in AMO_PREFERENCES) {
1267 var pref = prefSrv.getPref(preference.name, "");
1268 prefSrv.setPref(preference.name,
1269 pref.replace(preference.old, preference.new));
1273 // Export of variables
1274 exports.AMO_PREVIEW_DOMAIN = AMO_PREVIEW_DOMAIN;
1275 exports.AMO_PREVIEW_SITE = AMO_PREVIEW_SITE;
1277 // Export of functions
1278 exports.addToWhiteList = addToWhiteList;
1279 exports.removeFromWhiteList = removeFromWhiteList;
1280 exports.resetAmoPreviewUrls = resetAmoPreviewUrls;
1281 exports.useAmoPreviewUrls = useAmoPreviewUrls;
1283 // Export of classes
1284 exports.addonsManager = addonsManager;