]> git.donarmstrong.com Git - dactyl.git/blob - common/tests/functional/shared-modules/search.js
Initial import of 1.0~b6
[dactyl.git] / common / tests / functional / shared-modules / search.js
1 /* ***** BEGIN LICENSE BLOCK *****
2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
3  *
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/
8  *
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
12  * License.
13  *
14  * The Original Code is MozMill Test code.
15  *
16  * The Initial Developer of the Original Code is Mozilla Foundation.
17  * Portions created by the Initial Developer are Copyright (C) 2009
18  * the Initial Developer. All Rights Reserved.
19  *
20  * Contributor(s):
21  *   Henrik Skupin <hskupin@mozilla.com>
22  *
23  * Alternatively, the contents of this file may be used under the terms of
24  * either the GNU General Public License Version 2 or later (the "GPL"), or
25  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26  * in which case the provisions of the GPL or the LGPL are applicable instead
27  * of those above. If you wish to allow use of your version of this file only
28  * under the terms of either the GPL or the LGPL, and not to allow others to
29  * use your version of this file under the terms of the MPL, indicate your
30  * decision by deleting the provisions above and replace them with the notice
31  * and other provisions required by the GPL or the LGPL. If you do not delete
32  * the provisions above, a recipient may use your version of this file under
33  * the terms of any one of the MPL, the GPL or the LGPL.
34  *
35  * ***** END LICENSE BLOCK ***** */
36
37 /**
38  * @fileoverview
39  * The SearchAPI adds support for search related functions like the search bar.
40  */
41
42 // Include required modules
43 var modalDialog = require("modal-dialog");
44 var utils = require("utils");
45 var widgets = require("widgets");
46
47 const TIMEOUT = 5000;
48 const TIMEOUT_REQUEST_SUGGESTIONS = 750;
49
50 // Helper lookup constants for the engine manager elements
51 const MANAGER_BUTTONS   = '/id("engineManager")/anon({"anonid":"buttons"})';
52
53 // Helper lookup constants for the search bar elements
54 const NAV_BAR             = '/id("main-window")/id("tab-view-deck")/{"flex":"1"}' +
55                             '/id("navigator-toolbox")/id("nav-bar")';
56 const SEARCH_BAR          = NAV_BAR + '/id("search-container")/id("searchbar")';
57 const SEARCH_TEXTBOX      = SEARCH_BAR      + '/anon({"anonid":"searchbar-textbox"})';
58 const SEARCH_DROPDOWN     = SEARCH_TEXTBOX  + '/[0]/anon({"anonid":"searchbar-engine-button"})';
59 const SEARCH_POPUP        = SEARCH_DROPDOWN + '/anon({"anonid":"searchbar-popup"})';
60 const SEARCH_INPUT        = SEARCH_TEXTBOX  + '/anon({"class":"autocomplete-textbox-container"})' +
61                                               '/anon({"anonid":"textbox-input-box"})' +
62                                               '/anon({"anonid":"input"})';
63 const SEARCH_CONTEXT      = SEARCH_TEXTBOX  + '/anon({"anonid":"textbox-input-box"})' +
64                                               '/anon({"anonid":"input-box-contextmenu"})';
65 const SEARCH_GO_BUTTON    = SEARCH_TEXTBOX  + '/anon({"class":"search-go-container"})' +
66                                               '/anon({"class":"search-go-button"})';
67 const SEARCH_AUTOCOMPLETE =  '/id("main-window")/id("mainPopupSet")/id("PopupAutoComplete")';
68
69 /**
70  * Constructor
71  *
72  * @param {MozMillController} controller
73  *        MozMillController of the engine manager
74  */
75 function engineManager(controller)
76 {
77   this._controller = controller;
78 }
79
80 /**
81  * Search Manager class
82  */
83 engineManager.prototype = {
84   /**
85    * Get the controller of the associated engine manager dialog
86    *
87    * @returns Controller of the browser window
88    * @type MozMillController
89    */
90   get controller()
91   {
92     return this._controller;
93   },
94
95   /**
96    * Gets the list of search engines
97    *
98    * @returns List of engines
99    * @type object
100    */
101   get engines() {
102     var engines = [ ];
103     var tree = this.getElement({type: "engine_list"}).getNode();
104
105     for (var ii = 0; ii < tree.view.rowCount; ii ++) {
106       engines.push({name: tree.view.getCellText(ii, tree.columns.getColumnAt(0)),
107                     keyword: tree.view.getCellText(ii, tree.columns.getColumnAt(1))});
108     }
109
110     return engines;
111   },
112
113   /**
114    * Gets the name of the selected search engine
115    *
116    * @returns Name of the selected search engine
117    * @type string
118    */
119   get selectedEngine() {
120     var treeNode = this.getElement({type: "engine_list"}).getNode();
121
122     if(this.selectedIndex != -1) {
123       return treeNode.view.getCellText(this.selectedIndex,
124                                        treeNode.columns.getColumnAt(0));
125     } else {
126       return null;
127     }
128   },
129
130   /**
131    * Select the engine with the given name
132    *
133    * @param {string} name
134    *        Name of the search engine to select
135    */
136   set selectedEngine(name) {
137     var treeNode = this.getElement({type: "engine_list"}).getNode();
138
139     for (var ii = 0; ii < treeNode.view.rowCount; ii ++) {
140       if (name == treeNode.view.getCellText(ii, treeNode.columns.getColumnAt(0))) {
141         this.selectedIndex = ii;
142         break;
143       }
144     }
145   },
146
147   /**
148    * Gets the index of the selected search engine
149    *
150    * @returns Index of the selected search engine
151    * @type number
152    */
153   get selectedIndex() {
154     var tree = this.getElement({type: "engine_list"});
155     var treeNode = tree.getNode();
156
157     return treeNode.view.selection.currentIndex;
158   },
159
160   /**
161    * Select the engine with the given index
162    *
163    * @param {number} index
164    *        Index of the search engine to select
165    */
166   set selectedIndex(index) {
167     var tree = this.getElement({type: "engine_list"});
168     var treeNode = tree.getNode();
169
170     if (index < treeNode.view.rowCount) {
171       widgets.clickTreeCell(this._controller, tree, index, 0, {});
172     }
173
174     this._controller.waitForEval("subject.manager.selectedIndex == subject.newIndex", TIMEOUT, 100,
175                                  {manager: this, newIndex: index});
176   },
177
178   /**
179    * Gets the suggestions enabled state
180    */
181   get suggestionsEnabled() {
182     var checkbox = this.getElement({type: "suggest"});
183
184     return checkbox.getNode().checked;
185   },
186
187   /**
188    * Sets the suggestions enabled state
189    */
190   set suggestionsEnabled(state) {
191     var checkbox = this.getElement({type: "suggest"});
192     this._controller.check(checkbox, state);
193   },
194
195   /**
196    * Close the engine manager
197    *
198    * @param {MozMillController} controller
199    *        MozMillController of the window to operate on
200    * @param {boolean} saveChanges
201    *        (Optional) If true the OK button is clicked otherwise Cancel
202    */
203   close : function preferencesDialog_close(saveChanges) {
204     saveChanges = (saveChanges == undefined) ? false : saveChanges;
205
206     var button = this.getElement({type: "button", subtype: (saveChanges ? "accept" : "cancel")});
207     this._controller.click(button);
208   },
209
210   /**
211    * Edit the keyword associated to a search engine
212    *
213    * @param {string} name
214    *        Name of the engine to remove
215    * @param {function} handler
216    *        Callback function for Engine Manager
217    */
218   editKeyword : function engineManager_editKeyword(name, handler)
219   {
220     // Select the search engine
221     this.selectedEngine = name;
222
223     // Setup the modal dialog handler
224     md = new modalDialog.modalDialog(this._controller.window);
225     md.start(handler);
226
227     var button = this.getElement({type: "engine_button", subtype: "edit"});
228     this._controller.click(button);
229     md.waitForDialog();
230   },
231
232   /**
233    * Gets all the needed external DTD urls as an array
234    *
235    * @returns Array of external DTD urls
236    * @type [string]
237    */
238   getDtds : function engineManager_getDtds() {
239     var dtds = ["chrome://browser/locale/engineManager.dtd"];
240     return dtds;
241   },
242
243   /**
244    * Retrieve an UI element based on the given spec
245    *
246    * @param {object} spec
247    *        Information of the UI element which should be retrieved
248    *        type: General type information
249    *        subtype: Specific element or property
250    *        value: Value of the element or property
251    * @returns Element which has been created
252    * @type ElemBase
253    */
254   getElement : function engineManager_getElement(spec) {
255     var elem = null;
256
257     switch(spec.type) {
258       /**
259        * subtype: subtype to match
260        * value: value to match
261        */
262       case "more_engines":
263         elem = new elementslib.ID(this._controller.window.document, "addEngines");
264         break;
265       case "button":
266         elem = new elementslib.Lookup(this._controller.window.document, MANAGER_BUTTONS +
267                                       '/{"dlgtype":"' + spec.subtype + '"}');
268         break;
269       case "engine_button":
270         switch(spec.subtype) {
271           case "down":
272             elem = new elementslib.ID(this._controller.window.document, "dn");
273             break;
274           case "edit":
275             elem = new elementslib.ID(this._controller.window.document, "edit");
276             break;
277           case "remove":
278             elem = new elementslib.ID(this._controller.window.document, "remove");
279             break;
280           case "up":
281             elem = new elementslib.ID(this._controller.window.document, "up");
282             break;
283         }
284         break;
285       case "engine_list":
286         elem = new elementslib.ID(this._controller.window.document, "engineList");
287         break;
288       case "suggest":
289         elem = new elementslib.ID(this._controller.window.document, "enableSuggest");
290         break;
291       default:
292         throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
293     }
294
295     return elem;
296   },
297
298   /**
299    * Clicks the "Get more search engines..." link
300    */
301   getMoreSearchEngines : function engineManager_getMoreSearchEngines() {
302     var link = this.getElement({type: "more_engines"});
303     this._controller.click(link);
304   },
305
306   /**
307    * Move down the engine with the given name
308    *
309    * @param {string} name
310    *        Name of the engine to remove
311    */
312   moveDownEngine : function engineManager_moveDownEngine(name) {
313     this.selectedEngine = name;
314     var index = this.selectedIndex;
315
316     var button = this.getElement({type: "engine_button", subtype: "down"});
317     this._controller.click(button);
318
319     this._controller.waitForEval("subject.manager.selectedIndex == subject.oldIndex + 1", TIMEOUT, 100,
320                                  {manager: this, oldIndex: index});
321   },
322
323   /**
324    * Move up the engine with the given name
325    *
326    * @param {string} name
327    *        Name of the engine to remove
328    */
329   moveUpEngine : function engineManager_moveUpEngine(name) {
330     this.selectedEngine = name;
331     var index = this.selectedIndex;
332
333     var button = this.getElement({type: "engine_button", subtype: "up"});
334     this._controller.click(button);
335
336     this._controller.waitForEval("subject.manager.selectedIndex == subject.oldIndex - 1", TIMEOUT, 100,
337                                  {manager: this, oldIndex: index});
338   },
339
340   /**
341    * Remove the engine with the given name
342    *
343    * @param {string} name
344    *        Name of the engine to remove
345    */
346   removeEngine : function engineManager_removeEngine(name) {
347     this.selectedEngine = name;
348
349     var button = this.getElement({type: "engine_button", subtype: "remove"});
350     this._controller.click(button);
351
352     this._controller.waitForEval("subject.manager.selectedEngine != subject.removedEngine", TIMEOUT, 100,
353                                  {manager: this, removedEngine: name});
354   },
355
356   /**
357    * Restores the defaults for search engines
358    */
359   restoreDefaults : function engineManager_restoreDefaults() {
360     var button = this.getElement({type: "button", subtype: "extra2"});
361     this._controller.click(button);
362   }
363 };
364
365 /**
366  * Constructor
367  *
368  * @param {MozMillController} controller
369  *        MozMillController of the browser window to operate on
370  */
371 function searchBar(controller)
372 {
373   this._controller = controller;
374   this._bss = Cc["@mozilla.org/browser/search-service;1"]
375                  .getService(Ci.nsIBrowserSearchService);
376 }
377
378 /**
379  * Search Manager class
380  */
381 searchBar.prototype = {
382   /**
383    * Get the controller of the associated browser window
384    *
385    * @returns Controller of the browser window
386    * @type MozMillController
387    */
388   get controller()
389   {
390     return this._controller;
391   },
392
393   /**
394    * Get the names of all installed engines
395    */
396   get engines()
397   {
398     var engines = [ ];
399     var popup = this.getElement({type: "searchBar_dropDownPopup"});
400
401     for (var ii = 0; ii < popup.getNode().childNodes.length; ii++) {
402       var entry = popup.getNode().childNodes[ii];
403       if (entry.className.indexOf("searchbar-engine") != -1) {
404         engines.push({name: entry.id,
405                       selected: entry.selected,
406                       tooltipText: entry.getAttribute('tooltiptext')
407                     });
408       }
409     }
410
411     return engines;
412   },
413
414   /**
415    * Get the search engines drop down open state
416    */
417   get enginesDropDownOpen()
418   {
419     var popup = this.getElement({type: "searchBar_dropDownPopup"});
420     return popup.getNode().state != "closed";
421   },
422
423   /**
424    * Set the search engines drop down open state
425    */
426   set enginesDropDownOpen(newState)
427   {
428     if (this.enginesDropDownOpen != newState) {
429       var button = this.getElement({type: "searchBar_dropDown"});
430       this._controller.click(button);
431
432       this._controller.waitForEval("subject.searchBar.enginesDropDownOpen == subject.newState", TIMEOUT, 100,
433                                    {searchBar: this, newState: newState });
434       this._controller.sleep(0);
435     }
436   },
437
438   /**
439    * Get the names of all installable engines
440    */
441   get installableEngines()
442   {
443     var engines = [ ];
444     var popup = this.getElement({type: "searchBar_dropDownPopup"});
445
446     for (var ii = 0; ii < popup.getNode().childNodes.length; ii++) {
447       var entry = popup.getNode().childNodes[ii];
448       if (entry.className.indexOf("addengine-item") != -1) {
449         engines.push({name: entry.getAttribute('title'),
450                       selected: entry.selected,
451                       tooltipText: entry.getAttribute('tooltiptext')
452                     });
453       }
454     }
455
456     return engines;
457   },
458
459   /**
460    * Returns the currently selected search engine
461    *
462    * @return Name of the currently selected engine
463    * @type string
464    */
465   get selectedEngine()
466   {
467     // Open drop down which updates the list of search engines
468     var state = this.enginesDropDownOpen;
469     this.enginesDropDownOpen = true;
470
471     var engine = this.getElement({type: "engine", subtype: "selected", value: "true"});
472     this._controller.waitForElement(engine, TIMEOUT);
473
474     this.enginesDropDownOpen = state;
475
476     return engine.getNode().id;
477   },
478
479   /**
480    * Select the search engine with the given name
481    *
482    * @param {string} name
483    *        Name of the search engine to select
484    */
485   set selectedEngine(name) {
486     // Open drop down and click on search engine
487     this.enginesDropDownOpen = true;
488
489     var engine = this.getElement({type: "engine", subtype: "id", value: name});
490     this._controller.waitThenClick(engine, TIMEOUT);
491
492     // Wait until the drop down has been closed
493     this._controller.waitForEval("subject.searchBar.enginesDropDownOpen == false", TIMEOUT, 100,
494                                  {searchBar: this});
495
496     this._controller.waitForEval("subject.searchBar.selectedEngine == subject.newEngine", TIMEOUT, 100,
497                                  {searchBar: this, newEngine: name});
498   },
499
500   /**
501    * Returns all the visible search engines (API call)
502    */
503   get visibleEngines()
504   {
505     return this._bss.getVisibleEngines({});
506   },
507
508   /**
509    * Checks if the correct target URL has been opened for the search
510    *
511    * @param {string} searchTerm
512    *        Text which should be checked for
513    */
514   checkSearchResultPage : function searchBar_checkSearchResultPage(searchTerm) {
515     // Retrieve the URL which is used for the currently selected search engine
516     var targetUrl = this._bss.currentEngine.getSubmission(searchTerm, null).uri;
517     var currentUrl = this._controller.tabs.activeTabWindow.document.location;
518
519     var domainRegex = /[^\.]+\.([^\.]+)\..+$/gi;
520     var targetDomainName = targetUrl.host.replace(domainRegex, "$1");
521     var currentDomainName = currentUrl.host.replace(domainRegex, "$1");
522
523     this._controller.assert(function () {
524       return currentDomainName === targetDomainName;
525     }, "Current domain name matches target domain name - got '" +
526       currentDomainName + "', expected '" + targetDomainName + "'");
527
528     // Check if search term is listed in URL
529     this._controller.assert(function () {
530       return currentUrl.href.toLowerCase().indexOf(searchTerm.toLowerCase()) != -1;
531     }, "Current URL contains the search term - got '" +
532       currentUrl.href.toLowerCase() + "', expected '" + searchTerm.toLowerCase() + "'");
533
534   },
535
536   /**
537    * Clear the search field
538    */
539   clear : function searchBar_clear()
540   {
541     var activeElement = this._controller.window.document.activeElement;
542
543     var searchInput = this.getElement({type: "searchBar_input"});
544     var cmdKey = utils.getEntity(this.getDtds(), "selectAllCmd.key");
545     this._controller.keypress(searchInput, cmdKey, {accelKey: true});
546     this._controller.keypress(searchInput, 'VK_DELETE', {});
547
548     if (activeElement)
549       activeElement.focus();
550   },
551
552   /**
553    * Focus the search bar text field
554    *
555    * @param {object} event
556    *        Specifies the event which has to be used to focus the search bar
557    */
558   focus : function searchBar_focus(event)
559   {
560     var input = this.getElement({type: "searchBar_input"});
561
562     switch (event.type) {
563       case "click":
564         this._controller.click(input);
565         break;
566       case "shortcut":
567         if (mozmill.isLinux) {
568           var cmdKey = utils.getEntity(this.getDtds(), "searchFocusUnix.commandkey");
569         } else {
570           var cmdKey = utils.getEntity(this.getDtds(), "searchFocus.commandkey");
571         }
572         this._controller.keypress(null, cmdKey, {accelKey: true});
573         break;
574       default:
575         throw new Error(arguments.callee.name + ": Unknown element type - " + event.type);
576     }
577
578     // Check if the search bar has the focus
579     var activeElement = this._controller.window.document.activeElement;
580     this._controller.assertJS("subject.isFocused == true",
581                               {isFocused: input.getNode() == activeElement});
582   },
583
584   /**
585    * Gets all the needed external DTD urls as an array
586    *
587    * @returns Array of external DTD urls
588    * @type [string]
589    */
590   getDtds : function searchBar_getDtds() {
591     var dtds = ["chrome://browser/locale/browser.dtd"];
592     return dtds;
593   },
594
595   /**
596    * Retrieve an UI element based on the given spec
597    *
598    * @param {object} spec
599    *        Information of the UI element which should be retrieved
600    *        type: General type information
601    *        subtype: Specific element or property
602    *        value: Value of the element or property
603    * @returns Element which has been created
604    * @type ElemBase
605    */
606   getElement : function searchBar_getElement(spec) {
607     var elem = null;
608
609     switch(spec.type) {
610       /**
611        * subtype: subtype to match
612        * value: value to match
613        */
614       case "engine":
615         // XXX: bug 555938 - Mozmill can't fetch the element via a lookup here.
616         // That means we have to grab it temporarily by iterating through all childs.
617         var popup = this.getElement({type: "searchBar_dropDownPopup"}).getNode();
618         for (var ii = 0; ii < popup.childNodes.length; ii++) {
619           var entry = popup.childNodes[ii];
620           if (entry.getAttribute(spec.subtype) == spec.value) {
621             elem = new elementslib.Elem(entry);
622             break;
623           }
624         }
625         //elem = new elementslib.Lookup(this._controller.window.document, SEARCH_POPUP +
626         //                              '/anon({"' + spec.subtype + '":"' + spec.value + '"})');
627         break;
628       case "engine_manager":
629         // XXX: bug 555938 - Mozmill can't fetch the element via a lookup here.
630         // That means we have to grab it temporarily by iterating through all childs.
631         var popup = this.getElement({type: "searchBar_dropDownPopup"}).getNode();
632         for (var ii = popup.childNodes.length - 1; ii >= 0; ii--) {
633           var entry = popup.childNodes[ii];
634           if (entry.className == "open-engine-manager") {
635             elem = new elementslib.Elem(entry);
636             break;
637           }
638         }
639         //elem = new elementslib.Lookup(this._controller.window.document, SEARCH_POPUP +
640         //                              '/anon({"anonid":"open-engine-manager"})');
641         break;
642       case "searchBar":
643         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_BAR);
644         break;
645       case "searchBar_autoCompletePopup":
646         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_AUTOCOMPLETE);
647         break;
648       case "searchBar_contextMenu":
649         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_CONTEXT);
650         break;
651       case "searchBar_dropDown":
652         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_DROPDOWN);
653         break;
654       case "searchBar_dropDownPopup":
655         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_POPUP);
656         break;
657       case "searchBar_goButton":
658         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_GO_BUTTON);
659         break;
660       case "searchBar_input":
661         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_INPUT);
662         break;
663       case "searchBar_suggestions":
664         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_AUTOCOMPLETE +
665                                       '/anon({"anonid":"tree"})');
666          break;
667       case "searchBar_textBox":
668         elem = new elementslib.Lookup(this._controller.window.document, SEARCH_TEXTBOX);
669         break;
670       default:
671         throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
672     }
673
674     return elem;
675   },
676
677   /**
678    * Returns the search suggestions for the search term
679    */
680   getSuggestions : function(searchTerm) {
681     var suggestions = [ ];
682     var popup = this.getElement({type: "searchBar_autoCompletePopup"});
683     var treeElem = this.getElement({type: "searchBar_suggestions"});
684
685     // XXX Bug 542990, Bug 392633
686     // Typing too fast can cause several issue like the suggestions not to appear.
687     // Lets type the letters one by one and wait for the popup or the timeout
688     for (var i = 0; i < searchTerm.length; i++) {
689       try {
690         this.type(searchTerm[i]);
691         this._controller.waitFor(function () {
692           return popup.getNode().state === 'open';
693         }, "", TIMEOUT_REQUEST_SUGGESTIONS);
694       }
695       catch (e) {
696         // We are not interested in handling the timeout for now
697       }
698     }
699
700     // After entering the search term the suggestions have to be visible
701     this._controller.assert(function () {
702       return popup.getNode().state === 'open';
703     }, "Search suggestions are visible");
704     this._controller.waitForElement(treeElem, TIMEOUT);
705
706     // Get all suggestions
707     var tree = treeElem.getNode();
708     this._controller.waitForEval("subject.tree.view != null", TIMEOUT, 100,
709                                  {tree: tree});
710     for (var i = 0; i < tree.view.rowCount; i ++) {
711       suggestions.push(tree.view.getCellText(i, tree.columns.getColumnAt(0)));
712     }
713
714     // Close auto-complete popup
715     this._controller.keypress(popup, "VK_ESCAPE", {});
716     this._controller.waitForEval("subject.popup.state == 'closed'", TIMEOUT, 100,
717                                  {popup: popup.getNode()});
718
719     return suggestions;
720   },
721
722   /**
723    * Check if a search engine is installed (API call)
724    *
725    * @param {string} name
726    *        Name of the search engine to check
727    */
728   isEngineInstalled : function searchBar_isEngineInstalled(name)
729   {
730     var engine = this._bss.getEngineByName(name);
731     return (engine != null);
732   },
733
734   /**
735    * Open the Engine Manager
736    *
737    * @param {function} handler
738    *        Callback function for Engine Manager
739    */
740   openEngineManager : function searchBar_openEngineManager(handler)
741   {
742     this.enginesDropDownOpen = true;
743     var engineManager = this.getElement({type: "engine_manager"});
744
745     // Setup the modal dialog handler
746     md = new modalDialog.modalDialog(this._controller.window);
747     md.start(handler);
748
749     // XXX: Bug 555347 - Process any outstanding events before clicking the entry
750     this._controller.sleep(0);
751     this._controller.click(engineManager);
752     md.waitForDialog();
753
754     this._controller.assert(function () {
755       return this.enginesDropDownOpen == false;
756     }, "The search engine drop down menu has been closed", this);
757   },
758
759   /**
760    * Remove the search engine with the given name (API call)
761    *
762    * @param {string} name
763    *        Name of the search engine to remove
764    */
765   removeEngine : function searchBar_removeEngine(name)
766   {
767     if (this.isEngineInstalled(name)) {
768       var engine = this._bss.getEngineByName(name);
769       this._bss.removeEngine(engine);
770     }
771   },
772
773   /**
774    * Restore the default set of search engines (API call)
775    */
776   restoreDefaultEngines : function searchBar_restoreDefaults()
777   {
778     // XXX: Bug 556477 - Restore default sorting
779     this.openEngineManager(function(controller) {
780       var manager = new engineManager(controller);
781
782       // We have to do any action so the restore button gets enabled
783       manager.moveDownEngine(manager.engines[0].name);
784       manager.restoreDefaults();
785       manager.close(true);
786     });
787
788     // Update the visibility status for each engine and reset the default engine
789     this._bss.restoreDefaultEngines();
790     this._bss.currentEngine = this._bss.defaultEngine;
791
792     // Clear any entered search term
793     this.clear();
794   },
795
796   /**
797    * Start a search with the given search term and check if the resulting URL
798    * contains the search term.
799    *
800    * @param {object} data
801    *        Object which contains the search term and the action type
802    */
803   search : function searchBar_search(data)
804   {
805     var searchBar = this.getElement({type: "searchBar"});
806     this.type(data.text);
807
808     switch (data.action) {
809       case "returnKey":
810         this._controller.keypress(searchBar, 'VK_RETURN', {});
811         break;
812       case "goButton":
813       default:
814         this._controller.click(this.getElement({type: "searchBar_goButton"}));
815         break;
816     }
817
818     this._controller.waitForPageLoad();
819     this.checkSearchResultPage(data.text);
820   },
821
822   /**
823    * Enter a search term into the search bar
824    *
825    * @param {string} searchTerm
826    *        Text which should be searched for
827    */
828   type : function searchBar_type(searchTerm) {
829     var searchBar = this.getElement({type: "searchBar"});
830     this._controller.type(searchBar, searchTerm);
831   }
832 };
833
834 // Export of classes
835 exports.engineManager = engineManager;
836 exports.searchBar = searchBar;