~ubuntu-branches/ubuntu/trusty/fennec/trusty

« back to all changes in this revision

Viewing changes to mobile/chrome/content/browser-ui.js

  • Committer: Bazaar Package Importer
  • Author(s): Chris Coulson
  • Date: 2011-01-26 20:31:40 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20110126203140-zcg54f8ost2vmrxr
Tags: 4.0~b3-0ubuntu1
* New upstream release v4.0 B3 (FENNEC_4_0b3_RELEASE)

* Update build-depends for xulrunner-2.0
  - update debian/control
* Update mozclient to point to the mobile-browser repo
  - update debian/mozclient/fennec.conf
* Build with "--with-system-libxul"
  - update debian/rules
* Add launcher script, based on the one used in Firefox but with the
  unnecessary bits stripped out
  - add debian/fennec.sh
  - update debian/rules
* Refresh patches for new version
  - update debian/patches/bump_gecko_versions_in_application.ini.patch
  - update debian/patches/ubuntu_codes_google.patch
  - update debian/patches/installer.patch
* Drop unneeded patches
  - remove debian/patches/nspr_flags_by_pkg_config_hack.patch
  - remove debian/patches/xul191_l10n.patch
  - update debian/patches/series

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
 *
22
22
 * Contributor(s):
23
23
 *   Mark Finkle <mfinkle@mozilla.com>
 
24
 *   Matt Brubeck <mbrubeck@mozilla.com>
24
25
 *
25
26
 * Alternatively, the contents of this file may be used under the terms of
26
27
 * either the GNU General Public License Version 2 or later (the "GPL"), or
37
38
 * ***** END LICENSE BLOCK ***** */
38
39
 
39
40
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
41
Cu.import("resource://gre/modules/Services.jsm");
40
42
 
41
43
XPCOMUtils.defineLazyGetter(this, "PluralForm", function() {
42
44
  Cu.import("resource://gre/modules/PluralForm.jsm");
44
46
});
45
47
 
46
48
XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
47
 
  Cu.import("resource://gre/modules/utils.js");
 
49
  Cu.import("resource://gre/modules/PlacesUtils.jsm");
48
50
  return PlacesUtils;
49
51
});
50
52
 
 
53
XPCOMUtils.defineLazyServiceGetter(window, "gHistSvc", "@mozilla.org/browser/nav-history-service;1", "nsINavHistoryService", "nsIBrowserHistory");
 
54
XPCOMUtils.defineLazyServiceGetter(window, "gURIFixup", "@mozilla.org/docshell/urifixup;1", "nsIURIFixup");
 
55
XPCOMUtils.defineLazyServiceGetter(window, "gFaviconService", "@mozilla.org/browser/favicon-service;1", "nsIFaviconService");
 
56
XPCOMUtils.defineLazyServiceGetter(window, "gFocusManager", "@mozilla.org/focus-manager;1", "nsIFocusManager");
 
57
 
 
58
[
 
59
  ["AllPagesList", "popup_autocomplete", "cmd_openLocation"],
 
60
  ["HistoryList", "history-items", "cmd_history"],
 
61
  ["BookmarkList", "bookmarks-items", "cmd_bookmarks"],
 
62
#ifdef MOZ_SERVICES_SYNC
 
63
  ["RemoteTabsList", "remotetabs-items", "cmd_remoteTabs"]
 
64
#endif
 
65
].forEach(function(aPanel) {
 
66
  let [name, id, command] = aPanel;
 
67
  XPCOMUtils.defineLazyGetter(window, name, function() {
 
68
    return new AwesomePanel(id, command);
 
69
  });
 
70
});
 
71
 
 
72
/**
 
73
 * Cache of commonly used elements.
 
74
 */
 
75
let Elements = {};
 
76
 
 
77
[
 
78
  ["browserBundle",      "bundle_browser"],
 
79
  ["contentShowing",     "bcast_contentShowing"],
 
80
  ["urlbarState",        "bcast_urlbarState"],
 
81
  ["stack",              "stack"],
 
82
  ["tabs",               "tabs-container"],
 
83
  ["controls",           "browser-controls"],
 
84
  ["panelUI",            "panel-container"],
 
85
  ["toolbarContainer",   "toolbar-container"],
 
86
  ["browsers",           "browsers"],
 
87
  ["contentViewport",    "content-viewport"],
 
88
  ["contentNavigator",   "content-navigator"]
 
89
].forEach(function (aElementGlobal) {
 
90
  let [name, id] = aElementGlobal;
 
91
  XPCOMUtils.defineLazyGetter(Elements, name, function() {
 
92
    return document.getElementById(id);
 
93
  });
 
94
});
 
95
 
51
96
const TOOLBARSTATE_LOADING  = 1;
52
97
const TOOLBARSTATE_LOADED   = 2;
53
98
 
54
 
[
55
 
  [
56
 
    "gHistSvc",
57
 
    "@mozilla.org/browser/nav-history-service;1",
58
 
    [Ci.nsINavHistoryService, Ci.nsIBrowserHistory]
59
 
  ],
60
 
  [
61
 
    "gFaviconService",
62
 
     "@mozilla.org/browser/favicon-service;1",
63
 
     [Ci.nsIFaviconService]
64
 
  ],
65
 
  [
66
 
    "gIOService",
67
 
    "@mozilla.org/network/io-service;1",
68
 
    [Ci.nsIIOService],
69
 
  ],
70
 
  [
71
 
    "gURIFixup",
72
 
    "@mozilla.org/docshell/urifixup;1",
73
 
    [Ci.nsIURIFixup]
74
 
  ],
75
 
  [
76
 
    "gPrefService",
77
 
    "@mozilla.org/preferences-service;1",
78
 
    [Ci.nsIPrefBranch2]
79
 
  ],
80
 
  [
81
 
    "gFocusManager",
82
 
    "@mozilla.org/focus-manager;1",
83
 
    [Ci.nsIFocusManager]
84
 
  ],
85
 
  [
86
 
    "gWindowMediator",
87
 
    "@mozilla.org/appshell/window-mediator;1",
88
 
    [Ci.nsIWindowMediator]
89
 
  ],
90
 
  [
91
 
    "gObserverService",
92
 
    "@mozilla.org/observer-service;1",
93
 
    [Ci.nsIObserverService]
94
 
  ]
95
 
].forEach(function (service) {
96
 
  let [name, contract, ifaces] = service;
97
 
  window.__defineGetter__(name, function () {
98
 
    delete window[name];
99
 
    window[name] = Cc[contract].getService(ifaces.splice(0, 1)[0]);
100
 
    if (ifaces.length)
101
 
      ifaces.forEach(function (i) { return window[name].QueryInterface(i); });
102
 
    return window[name];
103
 
  });
104
 
});
105
 
 
106
99
var BrowserUI = {
107
 
  _edit : null,
108
 
  _throbber : null,
109
 
  _favicon : null,
 
100
  _edit: null,
 
101
  _title: null,
 
102
  _throbber: null,
 
103
  _favicon: null,
110
104
  _dialogs: [],
111
105
 
112
 
  _domWillOpenModalDialog: function(e) {
113
 
    if (!e.isTrusted)
114
 
      return;
115
 
 
 
106
  _domWillOpenModalDialog: function(aBrowser) {
116
107
    // We're about to open a modal dialog, make sure the opening
117
108
    // tab is brought to the front.
118
109
 
119
 
    let window = e.target.top;
120
 
    for (let i = 0; i < Browser._tabs.length; i++) {
121
 
      if (Browser._tabs[i].browser.contentWindow == window) {
122
 
        Browser.selectedTab = Browser._tabs[i];
 
110
    for (let i = 0; i < Browser.tabs.length; i++) {
 
111
      if (Browser._tabs[i].browser == aBrowser) {
 
112
        Browser.selectedTab = Browser.tabs[i];
123
113
        break;
124
114
      }
125
115
    }
126
116
  },
127
117
 
128
 
  _titleChanged : function(aDocument) {
129
 
    var browser = Browser.selectedBrowser;
130
 
    if (browser && aDocument != browser.contentDocument)
 
118
  _titleChanged: function(aBrowser) {
 
119
    let browser = Browser.selectedBrowser;
 
120
    if (browser && aBrowser != browser)
131
121
      return;
132
122
 
133
 
    var url = this.getDisplayURI(browser);
134
 
    var caption = aDocument.title || url;
 
123
    let url = this.getDisplayURI(browser);
 
124
    let caption = browser.contentTitle || url;
135
125
 
136
 
    if (Util.isURLEmpty(url))
 
126
    if (browser.contentTitle == "" && !Util.isURLEmpty(browser.userTypedValue))
 
127
      caption = browser.userTypedValue;
 
128
    else if (Util.isURLEmpty(url))
137
129
      caption = "";
138
130
 
139
 
    this._setURI(caption);
 
131
    if (caption) {
 
132
      this._title.value = caption;
 
133
      this._title.classList.remove("placeholder");
 
134
    } else {
 
135
      this._title.value = this._title.getAttribute("placeholder");
 
136
      this._title.classList.add("placeholder");
 
137
    }
140
138
  },
141
139
 
142
140
  /*
143
141
   * Dispatched by window.close() to allow us to turn window closes into tabs
144
142
   * closes.
145
143
   */
146
 
  _domWindowClose: function (aEvent) {
147
 
    if (!aEvent.isTrusted)
148
 
      return;
149
 
 
150
 
    // Find the relevant tab, and close it.
151
 
    let browsers = Browser.browsers;
152
 
    for (let i = 0; i < browsers.length; i++) {
153
 
      if (browsers[i].contentWindow == aEvent.target) {
 
144
  _domWindowClose: function(aBrowser) {
 
145
     // Find the relevant tab, and close it.
 
146
     let browsers = Browser.browsers;
 
147
     for (let i = 0; i < browsers.length; i++) {
 
148
      if (browsers[i] == aBrowser) {
154
149
        Browser.closeTab(Browser.getTabAtIndex(i));
155
 
        aEvent.preventDefault();
156
 
        break;
157
 
      }
158
 
    }
159
 
  },
160
 
 
161
 
  _linkAdded : function(aEvent) {
162
 
    let link = aEvent.originalTarget;
163
 
    if (!link || !link.href)
164
 
      return;
165
 
 
166
 
    if (/\bicon\b/i(link.rel)) {
167
 
      // Must have an owner document and not be in a frame
168
 
      var ownerDoc = link.ownerDocument;
169
 
      if (!ownerDoc || ownerDoc.defaultView.frameElement)
170
 
        return;
171
 
 
172
 
      let tab = Browser.getTabForDocument(ownerDoc);
173
 
      tab.setIcon(link.href);
174
 
      tab.updateViewportMetadata(); // XXX Hack - See bug 568261
175
 
 
176
 
      // If the link changes after pageloading, update it right away.
177
 
      // otherwise we wait until the pageload finishes
178
 
      if ((tab.browser == Browser.selectedBrowser) && !tab.isLoading())
179
 
        this._updateIcon(tab.browser.mIconURL);
180
 
    }
181
 
    else if (/\bsearch\b/i(link.rel)) {
182
 
      var type = link.type && link.type.toLowerCase();
183
 
      type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
184
 
      if (type == "application/opensearchdescription+xml" && link.title && /^(?:https?|ftp):/i.test(link.href)) {
185
 
        var engine = { title: link.title, href: link.href };
186
 
 
187
 
        BrowserSearch.addPageSearchEngine(engine, link.ownerDocument);
188
 
      }
189
 
    }
190
 
  },
191
 
 
192
 
  _updateButtons : function(aBrowser) {
 
150
        return { preventDefault: true };
 
151
      }
 
152
    }
 
153
  },
 
154
 
 
155
  _updateButtons: function(aBrowser) {
193
156
    let back = document.getElementById("cmd_back");
194
157
    let forward = document.getElementById("cmd_forward");
195
158
 
198
161
  },
199
162
 
200
163
  _updateToolbar: function _updateToolbar() {
201
 
    let icons = document.getElementById("urlbar-icons");
202
 
    let mode = icons.getAttribute("mode");
203
 
    if (Browser.selectedTab.isLoading() && mode != "loading") {
204
 
      icons.setAttribute("mode", "loading");
205
 
    }
206
 
    else if (mode != "view") {
207
 
      icons.setAttribute("mode", "view");
208
 
    }
 
164
    let mode = Elements.urlbarState.getAttribute("mode");
 
165
    if (mode == "edit" && this.activePanel)
 
166
      return;
 
167
 
 
168
    if (Browser.selectedTab.isLoading() && mode != "loading")
 
169
      Elements.urlbarState.setAttribute("mode", "loading");
 
170
    else if (mode != "view")
 
171
      Elements.urlbarState.setAttribute("mode", "view");
209
172
  },
210
173
 
211
 
  _tabSelect : function(aEvent) {
 
174
  _tabSelect: function(aEvent) {
212
175
    let browser = Browser.selectedBrowser;
213
 
    this._titleChanged(browser.contentDocument);
 
176
    this._titleChanged(browser);
214
177
    this._updateToolbar();
215
178
    this._updateButtons(browser);
216
179
    this._updateIcon(browser.mIconURL);
217
180
    this.updateStar();
218
181
  },
219
182
 
220
 
  showToolbar : function showToolbar(aEdit) {
221
 
    this.hidePanel();
222
 
    this._editURI(aEdit);
223
 
  },
224
 
 
225
183
  _toolbarLocked: 0,
226
184
 
227
185
  isToolbarLocked: function isToolbarLocked() {
244
202
      document.getElementById("toolbar-moveable-container").top = "";
245
203
  },
246
204
 
247
 
  _setURI: function _setURI(aCaption) {
248
 
    if (this.isAutoCompleteOpen())
249
 
      this._edit.defaultValue = aCaption;
 
205
  _setURL: function _setURL(aURL) {
 
206
    if (this.activePanel)
 
207
      this._edit.defaultValue = aURL;
250
208
    else
251
 
      this._edit.value = aCaption;
252
 
  },
253
 
 
254
 
  _editURI : function _editURI(aEdit) {
255
 
    var icons = document.getElementById("urlbar-icons");
256
 
    if (aEdit && icons.getAttribute("mode") != "edit") {
257
 
      icons.setAttribute("mode", "edit");
258
 
      this._edit.defaultValue = this._edit.value;
259
 
 
260
 
      let urlString = this.getDisplayURI(Browser.selectedBrowser);
261
 
      if (Util.isURLEmpty(urlString))
262
 
        urlString = "";
263
 
      this._edit.value = urlString;
264
 
 
265
 
      // This is a workaround for bug 488420, needed to cycle focus for the
266
 
      // IME state to be set properly. Testing shows we only really need to
267
 
      // do this the first time.
268
 
      this._edit.blur();
269
 
      gFocusManager.setFocus(this._edit, Ci.nsIFocusManager.FLAG_NOSCROLL);
270
 
    }
271
 
    else if (!aEdit) {
272
 
      this._updateToolbar();
273
 
    }
 
209
      this._edit.value = aURL;
 
210
  },
 
211
 
 
212
  _editURI: function _editURI(aEdit) {
 
213
    Elements.urlbarState.setAttribute("mode", "edit");
 
214
    this._edit.defaultValue = this._edit.value;
 
215
  },
 
216
 
 
217
  _showURI: function _showURI() {
 
218
    // Replace the web page title by the url of the page
 
219
    let urlString = this.getDisplayURI(Browser.selectedBrowser);
 
220
    if (Util.isURLEmpty(urlString))
 
221
      urlString = "";
 
222
 
 
223
    this._edit.value = urlString;
 
224
  },
 
225
 
 
226
  updateAwesomeHeader: function updateAwesomeHeader(aString) {
 
227
    document.getElementById("awesome-header").hidden = (aString != "");
 
228
 
 
229
    // During an awesome search we always show the popup_autocomplete/AllPagesList
 
230
    // panel since this looks in every places and the rationale behind typing
 
231
    // is to find something, whereever it is.
 
232
    if (this.activePanel != AllPagesList) {
 
233
      let inputField = this._edit;
 
234
      let oldClickSelectsAll = inputField.clickSelectsAll;
 
235
      inputField.clickSelectsAll = false;
 
236
 
 
237
      this.activePanel = AllPagesList;
 
238
 
 
239
      // changing the searchString property call updateAwesomeHeader again
 
240
      inputField.controller.searchString = aString;
 
241
      inputField.readOnly = false;
 
242
      inputField.clickSelectsAll = oldClickSelectsAll;
 
243
      return;
 
244
    }
 
245
 
 
246
    let event = document.createEvent("Events");
 
247
    event.initEvent("onsearchbegin", true, true);
 
248
    this._edit.dispatchEvent(event);
274
249
  },
275
250
 
276
251
  _closeOrQuit: function _closeOrQuit() {
277
252
    // Close active dialog, if we have one. If not then close the application.
278
 
    let dialog = this.activeDialog;
279
 
    if (dialog) {
280
 
      dialog.close();
 
253
    if (this.activePanel) {
 
254
      this.activePanel = null;
 
255
    } else if (this.activeDialog) {
 
256
      this.activeDialog.close();
281
257
    } else {
282
258
      // Check to see if we should really close the window
283
259
      if (Browser.closing())
285
261
    }
286
262
  },
287
263
 
 
264
  _activePanel: null,
 
265
  get activePanel() {
 
266
    return this._activePanel;
 
267
  },
 
268
 
 
269
  set activePanel(aPanel) {
 
270
    if (this._activePanel == aPanel)
 
271
      return;
 
272
 
 
273
    let awesomePanel = document.getElementById("awesome-panels");
 
274
    let awesomeHeader = document.getElementById("awesome-header");
 
275
 
 
276
    let willShowPanel = (!this._activePanel && aPanel);
 
277
    if (willShowPanel) {
 
278
      this.pushDialog(aPanel);
 
279
      this._edit.attachController();
 
280
      this._editURI();
 
281
      awesomePanel.hidden = awesomeHeader.hidden = false;
 
282
    };
 
283
 
 
284
    if (aPanel) {
 
285
      aPanel.open();
 
286
      if (this._edit.value == "")
 
287
        this._showURI();
 
288
    }
 
289
 
 
290
    let willHidePanel = (this._activePanel && !aPanel);
 
291
    if (willHidePanel) {
 
292
      awesomePanel.hidden = true;
 
293
      awesomeHeader.hidden = false;
 
294
      this._edit.reset();
 
295
      this._edit.detachController();
 
296
      this.popDialog();
 
297
    }
 
298
 
 
299
    if (this._activePanel)
 
300
      this._activePanel.close();
 
301
 
 
302
    // The readOnly state of the field enabled/disabled the VKB
 
303
    let isReadOnly = !(aPanel == AllPagesList && Util.isPortrait() && (willShowPanel || !this._edit.readOnly));
 
304
    this._edit.readOnly = isReadOnly;
 
305
    if (isReadOnly)
 
306
      this._edit.blur();
 
307
 
 
308
    this._activePanel = aPanel;
 
309
    if (willHidePanel || willShowPanel) {
 
310
      let event = document.createEvent("UIEvents");
 
311
      event.initUIEvent("NavigationPanel" + (willHidePanel ? "Hidden" : "Shown"), true, true, window, false);
 
312
      window.dispatchEvent(event);
 
313
    }
 
314
  },
 
315
 
288
316
  get activeDialog() {
289
317
    // Return the topmost dialog
290
318
    if (this._dialogs.length)
292
320
    return null;
293
321
  },
294
322
 
295
 
  pushDialog : function pushDialog(aDialog) {
 
323
  pushDialog: function pushDialog(aDialog) {
296
324
    // If we have a dialog push it on the stack and set the attr for CSS
297
325
    if (aDialog) {
298
326
      this.lockToolbar();
302
330
    }
303
331
  },
304
332
 
305
 
  popDialog : function popDialog() {
 
333
  popDialog: function popDialog() {
306
334
    if (this._dialogs.length) {
307
335
      this._dialogs.pop();
308
336
      this.unlockToolbar();
319
347
    this._hidePopup();
320
348
    this._popup =  { "panel": aPanel,
321
349
                     "elements": (aElements instanceof Array) ? aElements : [aElements] };
322
 
    this._dispatchPopupChanged();
 
350
    this._dispatchPopupChanged(true);
323
351
  },
324
352
 
325
 
  popPopup: function popPopup() {
 
353
  popPopup: function popPopup(aPanel) {
 
354
    if (!this._popup || aPanel != this._popup.panel)
 
355
      return;
326
356
    this._popup = null;
327
 
    this._dispatchPopupChanged();
 
357
    this._dispatchPopupChanged(false);
328
358
  },
329
359
 
330
 
  _dispatchPopupChanged: function _dispatchPopupChanged() {
 
360
  _dispatchPopupChanged: function _dispatchPopupChanged(aVisible) {
331
361
    let stack = document.getElementById("stack");
332
 
    let event = document.createEvent("Events");
333
 
    event.initEvent("PopupChanged", true, false);
 
362
    let event = document.createEvent("UIEvents");
 
363
    event.initUIEvent("PopupChanged", true, true, window, aVisible);
334
364
    event.popup = this._popup;
335
365
    stack.dispatchEvent(event);
336
366
  },
353
383
    return targetNode ? true : false;
354
384
  },
355
385
 
356
 
  switchPane : function switchPane(id) {
357
 
    let button = document.getElementsByAttribute("linkedpanel", id)[0];
 
386
  switchPane: function switchPane(aPanelId) {
 
387
    let button = document.getElementsByAttribute("linkedpanel", aPanelId)[0];
358
388
    if (button)
359
389
      button.checked = true;
360
390
 
361
 
    let pane = document.getElementById(id);
 
391
    this.blurFocusedElement();
 
392
 
 
393
    let pane = document.getElementById(aPanelId);
362
394
    document.getElementById("panel-items").selectedPanel = pane;
363
395
  },
364
396
 
371
403
  },
372
404
 
373
405
  get sidebarW() {
374
 
    if (!this._sidebarW) {
375
 
      let sidebar = document.getElementById("browser-controls");
376
 
      this._sidebarW = sidebar.boxObject.width;
377
 
    }
378
 
    return this._sidebarW;
 
406
    delete this._sidebarW;
 
407
    return this._sidebarW = Elements.controls.getBoundingClientRect().width;
379
408
  },
380
409
 
381
410
  get starButton() {
383
412
    return this.starButton = document.getElementById("tool-star");
384
413
  },
385
414
 
386
 
  sizeControls : function(windowW, windowH) {
 
415
  sizeControls: function(windowW, windowH) {
387
416
    // tabs
388
417
    document.getElementById("tabs").resize();
389
418
 
390
 
    // awesomebar
391
 
    let popup = document.getElementById("popup_autocomplete");
 
419
    // awesomebar and related panels
 
420
    let popup = document.getElementById("awesome-panels");
392
421
    popup.top = this.toolbarH;
393
422
    popup.height = windowH - this.toolbarH;
394
423
    popup.width = windowW;
395
424
 
396
 
    // form helper
397
 
    let formHelper = document.getElementById("form-helper-container");
398
 
    formHelper.top = windowH - formHelper.getBoundingClientRect().height;
 
425
    // content navigator helper
 
426
    let contentHelper = document.getElementById("content-navigator");
 
427
    contentHelper.top = windowH - contentHelper.getBoundingClientRect().height;
399
428
  },
400
429
 
401
 
  init : function() {
 
430
  init: function() {
402
431
    this._edit = document.getElementById("urlbar-edit");
 
432
    this._title = document.getElementById("urlbar-title");
403
433
    this._throbber = document.getElementById("urlbar-throbber");
404
434
    this._favicon = document.getElementById("urlbar-favicon");
405
435
    this._favicon.addEventListener("error", this, false);
406
436
 
407
 
    let urlbarEditArea = document.getElementById("urlbar-editarea");
408
 
    urlbarEditArea.addEventListener("mouseup", this, false);
409
 
    urlbarEditArea.addEventListener("mousedown", this, false);
410
 
 
411
 
    document.getElementById("toolbar-main").ignoreDrag = true;
 
437
    this._edit.addEventListener("click", this, false);
 
438
    this._edit.addEventListener("mousedown", this, false);
 
439
 
 
440
    BadgeHandlers.register(this._edit.popup);
 
441
 
 
442
    window.addEventListener("NavigationPanelShown", this, false);
 
443
    window.addEventListener("NavigationPanelHidden", this, false);
412
444
 
413
445
    let tabs = document.getElementById("tabs");
414
446
    tabs.addEventListener("TabSelect", this, true);
415
447
    tabs.addEventListener("TabOpen", this, true);
416
 
 
417
 
    let browsers = document.getElementById("browsers");
418
 
    browsers.addEventListener("DOMWindowClose", this, true);
419
 
 
420
 
    // XXX these really want to listen to only the current browser
421
 
    browsers.addEventListener("DOMTitleChanged", this, true);
422
 
    browsers.addEventListener("DOMLinkAdded", this, true);
423
 
    browsers.addEventListener("DOMWillOpenModalDialog", this, true);
 
448
    tabs.addEventListener("TabOpen", NewTabPopup, true);
 
449
    window.addEventListener("PanFinished", this, true);
 
450
 
 
451
    // listen content messages
 
452
    messageManager.addMessageListener("DOMLinkAdded", this);
 
453
    messageManager.addMessageListener("DOMTitleChanged", this);
 
454
    messageManager.addMessageListener("DOMWillOpenModalDialog", this);
 
455
    messageManager.addMessageListener("DOMWindowClose", this);
 
456
 
 
457
    messageManager.addMessageListener("Browser:OpenURI", this);
 
458
    messageManager.addMessageListener("Browser:SaveAs:Return", this);
424
459
 
425
460
    // listening mousedown for automatically dismiss some popups (e.g. larry)
426
461
    window.addEventListener("mousedown", this, true);
428
463
    // listening escape to dismiss dialog on VK_ESCAPE
429
464
    window.addEventListener("keypress", this, true);
430
465
 
431
 
    // Push the panel initialization out of the startup path
432
 
    // (Using an event because we have no good way to delay-init [Bug 535366])
433
 
    browsers.addEventListener("load", function() {
434
 
      // We only want to delay one time
435
 
      browsers.removeEventListener("load", arguments.callee, true);
436
 
      
 
466
    // listening AppCommand to handle special keys
 
467
    window.addEventListener("AppCommand", this, true);
 
468
 
 
469
    // We can delay some initialization until after startup.  We wait until
 
470
    // the first page is shown, then dispatch a UIReadyDelayed event.
 
471
    messageManager.addMessageListener("pageshow", function() {
 
472
      if (getBrowser().currentURI.spec == "about:blank")
 
473
        return;
 
474
 
 
475
      messageManager.removeMessageListener("pageshow", arguments.callee, true);
 
476
 
 
477
      let event = document.createEvent("Events");
 
478
      event.initEvent("UIReadyDelayed", true, false);
 
479
      window.dispatchEvent(event);
 
480
    });
 
481
 
 
482
    // Delay the panel UI and Sync initialization.
 
483
    window.addEventListener("UIReadyDelayed", function(aEvent) {
 
484
      window.removeEventListener(aEvent.type, arguments.callee, false);
 
485
 
437
486
      // We unhide the panelUI so the XBL and settings can initialize
438
487
      Elements.panelUI.hidden = false;
439
488
 
442
491
      DownloadsView.init();
443
492
      PreferencesView.init();
444
493
      ConsoleView.init();
445
 
    }, true);
 
494
      FullScreenVideo.init();
 
495
 
 
496
#ifdef MOZ_IPC
 
497
      // Pre-start the content process
 
498
      Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
 
499
          .ensureContentProcess();
 
500
#endif
 
501
 
 
502
#ifdef MOZ_SERVICES_SYNC
 
503
      // Init the sync system
 
504
      WeaveGlue.init();
 
505
#endif
 
506
    }, false);
 
507
 
 
508
    FormHelperUI.init();
 
509
    FindHelperUI.init();
 
510
    PageActions.init();
446
511
  },
447
512
 
448
 
  uninit : function() {
 
513
  uninit: function() {
449
514
    ExtensionsView.uninit();
450
515
    ConsoleView.uninit();
 
516
    FormHelperUI.uninit();
451
517
  },
452
518
 
453
 
  update : function(aState) {
454
 
    let icons = document.getElementById("urlbar-icons");
 
519
  update: function(aState) {
455
520
    let browser = Browser.selectedBrowser;
456
521
 
457
522
    switch (aState) {
458
523
      case TOOLBARSTATE_LOADED:
459
 
        if (icons.getAttribute("mode") != "edit")
460
 
          this._updateToolbar();
 
524
        this._updateToolbar();
461
525
 
462
526
        this._updateIcon(browser.mIconURL);
463
527
        this.unlockToolbar();
464
528
        break;
465
529
 
466
530
      case TOOLBARSTATE_LOADING:
467
 
        if (icons.getAttribute("mode") != "edit")
468
 
          this._updateToolbar();
 
531
        this._updateToolbar();
469
532
 
470
533
        browser.mIconURL = "";
471
534
        this._updateIcon();
474
537
    }
475
538
  },
476
539
 
477
 
  _updateIcon : function(aIconSrc) {
 
540
  _updateIcon: function(aIconSrc) {
478
541
    this._favicon.src = aIconSrc || "";
479
542
    if (Browser.selectedTab.isLoading()) {
480
543
      this._throbber.hidden = false;
488
551
    }
489
552
  },
490
553
 
491
 
  getDisplayURI : function(browser) {
492
 
    let loadGroup = browser.webNavigation.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
493
 
    if (loadGroup.activeCount && loadGroup.defaultLoadRequest) {
494
 
      // browser.currentURI may not be valid if the request is still active.
495
 
      // For chrome URIs especially, we want the urlbar during loading to use the
496
 
      // "original" URI (about:home), not a rewritten one (jar:file:///...).
497
 
      return loadGroup.defaultLoadRequest.QueryInterface(Ci.nsIChannel).originalURI.spec;
498
 
    }
499
 
 
500
 
    if (!this._URIFixup)
501
 
      this._URIFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
502
 
 
 
554
  getDisplayURI: function(browser) {
503
555
    let uri = browser.currentURI;
504
556
    try {
505
 
      uri = this._URIFixup.createExposableURI(uri);
 
557
      uri = gURIFixup.createExposableURI(uri);
506
558
    } catch (ex) {}
507
559
 
508
560
    return uri.spec;
509
561
  },
510
562
 
511
563
  /* Set the location to the current content */
512
 
  updateURI : function() {
513
 
    var browser = Browser.selectedBrowser;
 
564
  updateURI: function() {
 
565
    let browser = Browser.selectedBrowser;
514
566
 
515
567
    // FIXME: deckbrowser should not fire TabSelect on the initial tab (bug 454028)
516
568
    if (!browser.currentURI)
519
571
    // Update the navigation buttons
520
572
    this._updateButtons(browser);
521
573
 
522
 
    // Close the forms assistant
523
 
    FormHelper.close();
524
 
 
525
574
    // Check for a bookmarked page
526
575
    this.updateStar();
527
576
 
528
 
    var urlString = this.getDisplayURI(browser);
 
577
    let urlString = this.getDisplayURI(browser);
529
578
    if (Util.isURLEmpty(urlString))
530
579
      urlString = "";
531
580
 
532
 
    this._setURI(urlString);
 
581
    this._setURL(urlString);
533
582
  },
534
583
 
535
 
  goToURI : function(aURI) {
 
584
  goToURI: function(aURI) {
536
585
    aURI = aURI || this._edit.value;
537
586
    if (!aURI)
538
587
      return;
542
591
 
543
592
    // Give the new page lots of room
544
593
    Browser.hideSidebars();
545
 
    this.closeAutoComplete(true);
 
594
    this.closeAutoComplete();
546
595
 
547
596
    this._edit.value = aURI;
548
597
 
549
 
    var flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
550
 
    getBrowser().loadURIWithFlags(aURI, flags, null, null);
551
 
 
552
 
    gHistSvc.markPageAsTyped(gURIFixup.createFixupURI(aURI, 0));
553
 
  },
554
 
 
555
 
  showAutoComplete : function showAutoComplete() {
556
 
    if (this.isAutoCompleteOpen())
557
 
      return;
558
 
 
559
 
    BrowserSearch.updateSearchButtons();
560
 
 
561
 
    this._edit.showHistoryPopup();
562
 
  },
563
 
 
564
 
  closeAutoComplete: function closeAutoComplete(aResetInput) {
565
 
    if (!this.isAutoCompleteOpen())
566
 
      return;
567
 
 
568
 
    if (aResetInput)
569
 
      this._edit.popup.close();
570
 
    else
 
598
    let postData = {};
 
599
    aURI = Browser.getShortcutOrURI(aURI, postData);
 
600
    Browser.loadURI(aURI, { flags: Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP, postData: postData });
 
601
 
 
602
    // Delay doing the fixup so the raw URI is passed to loadURIWithFlags
 
603
    // and the proper third-party fixup can be done
 
604
    let fixupFlags = Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP;
 
605
    let uri = gURIFixup.createFixupURI(aURI, fixupFlags);
 
606
    gHistSvc.markPageAsTyped(uri);
 
607
 
 
608
    this._titleChanged(Browser.selectedBrowser);
 
609
  },
 
610
 
 
611
  showAutoComplete: function showAutoComplete() {
 
612
    if (this.isAutoCompleteOpen())
 
613
      return;
 
614
 
 
615
    this.hidePanel();
 
616
    this._hidePopup();
 
617
    this.activePanel = AllPagesList;
 
618
  },
 
619
 
 
620
  closeAutoComplete: function closeAutoComplete() {
 
621
    if (this.isAutoCompleteOpen())
571
622
      this._edit.popup.closePopup();
 
623
 
 
624
    this.activePanel = null;
572
625
  },
573
626
 
574
627
  isAutoCompleteOpen: function isAutoCompleteOpen() {
575
 
    return this._edit.popup.popupOpen;
 
628
    return this.activePanel == AllPagesList;
576
629
  },
577
630
 
578
 
  doButtonSearch : function(button) {
579
 
    if (!("engine" in button) || !button.engine)
580
 
      return;
581
 
 
582
 
    // We don't want the button to look pressed for now
583
 
    button.parentNode.selectedItem = null;
 
631
  doOpenSearch: function doOpenSearch(aName) {
 
632
    // save the current value of the urlbar
 
633
    let searchValue = this._edit.value;
584
634
 
585
635
    // Give the new page lots of room
586
636
    Browser.hideSidebars();
587
 
    this.closeAutoComplete(false);
 
637
    this.closeAutoComplete();
588
638
 
589
639
    // Make sure we're online before attempting to load
590
640
    Util.forceOnline();
591
641
 
592
 
    let submission = button.engine.getSubmission(this._edit.value, null);
593
 
    let flags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
594
 
    getBrowser().loadURIWithFlags(submission.uri.spec, flags, null, null, submission.postData);
595
 
  },
596
 
 
597
 
  updateStar : function() {
 
642
    let engine = Services.search.getEngineByName(aName);
 
643
    let submission = engine.getSubmission(searchValue, null);
 
644
    Browser.loadURI(submission.uri.spec, { postData: submission.postData });
 
645
  },
 
646
 
 
647
  updateUIFocus: function _updateUIFocus() {
 
648
    if (Elements.contentShowing.getAttribute("disabled") == "true")
 
649
      Browser.selectedBrowser.messageManager.sendAsyncMessage("Browser:Blur", { });
 
650
  },
 
651
 
 
652
  updateStar: function() {
598
653
    if (PlacesUtils.getMostRecentBookmarkForURI(Browser.selectedBrowser.currentURI) != -1)
599
654
      this.starButton.setAttribute("starred", "true");
600
655
    else
601
656
      this.starButton.removeAttribute("starred");
602
657
  },
603
658
 
604
 
  newTab : function newTab(aURI) {
 
659
  newTab: function newTab(aURI, aOwner) {
605
660
    aURI = aURI || "about:blank";
606
 
    let tab = Browser.addTab(aURI, true);
 
661
    let tab = Browser.addTab(aURI, true, aOwner);
607
662
 
608
663
    this.hidePanel();
609
664
 
610
665
    if (aURI == "about:blank") {
611
666
      // Display awesomebar UI
612
 
      this.showToolbar(true);
613
667
      this.showAutoComplete();
614
668
    }
615
669
    else {
616
670
      // Give the new page lots of room
617
671
      Browser.hideSidebars();
618
 
      this.closeAutoComplete(true);
 
672
      this.closeAutoComplete();
619
673
    }
620
674
 
621
675
    return tab;
622
676
  },
623
677
 
624
 
  closeTab : function closeTab(aTab) {
 
678
  newOrSelectTab: function newOrSelectTab(aURI, aOwner) {
 
679
    let tabs = Browser.tabs;
 
680
    for (let i = 0; i < tabs.length; i++) {
 
681
      if (tabs[i].browser.currentURI.spec == aURI) {
 
682
        Browser.selectedTab = tabs[i];
 
683
        return;
 
684
      }
 
685
    }
 
686
    this.newTab(aURI, aOwner);
 
687
  },
 
688
 
 
689
  closeTab: function closeTab(aTab) {
625
690
    // If no tab is passed in, assume the current tab
626
691
    Browser.closeTab(aTab || Browser.selectedTab);
627
692
  },
628
693
 
629
 
  selectTab : function selectTab(aTab) {
 
694
  selectTab: function selectTab(aTab) {
 
695
    this.activePanel = null;
630
696
    Browser.selectedTab = aTab;
631
697
  },
632
698
 
 
699
  undoCloseTab: function undoCloseTab(aIndex) {
 
700
    let tab = null;
 
701
    let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
 
702
    if (ss.getClosedTabCount(window) > (aIndex || 0)) {
 
703
      let chromeTab = ss.undoCloseTab(window, aIndex || 0);
 
704
      tab = Browser.getTabFromChrome(chromeTab);
 
705
    }
 
706
    return tab;
 
707
  },
 
708
 
633
709
  isTabsVisible: function isTabsVisible() {
634
710
    // The _1, _2 and _3 are to make the js2 emacs mode happy
635
711
    let [leftvis,_1,_2,_3] = Browser.computeSidebarVisibility();
637
713
  },
638
714
 
639
715
  showPanel: function showPanel(aPage) {
 
716
    if (this.activePanel)
 
717
      this.activePanel = null;
 
718
 
640
719
    Elements.panelUI.left = 0;
641
720
    Elements.panelUI.hidden = false;
642
721
    Elements.contentShowing.setAttribute("disabled", "true");
646
725
  },
647
726
 
648
727
  hidePanel: function hidePanel() {
 
728
    if (!this.isPanelVisible())
 
729
      return;
649
730
    Elements.panelUI.hidden = true;
650
731
    Elements.contentShowing.removeAttribute("disabled");
 
732
    this.blurFocusedElement();
651
733
  },
652
734
 
653
735
  isPanelVisible: function isPanelVisible() {
654
736
    return (!Elements.panelUI.hidden && Elements.panelUI.left == 0);
655
737
  },
656
738
 
 
739
  blurFocusedElement: function blurFocusedElement() {
 
740
    let focusedElement = document.commandDispatcher.focusedElement;
 
741
    if (focusedElement)
 
742
      focusedElement.blur();
 
743
  },
 
744
 
657
745
  switchTask: function switchTask() {
658
746
    try {
659
747
      let phone = Cc["@mozilla.org/phone/support;1"].createInstance(Ci.nsIPhoneSupport);
661
749
    } catch(e) { }
662
750
  },
663
751
 
664
 
#ifdef WINCE
665
 
  updateDefaultBrowser: function updateDefaultBrowser(aSet) {
666
 
    try {
667
 
      let phone = Cc["@mozilla.org/phone/support;1"].getService(Ci.nsIPhoneSupport);
668
 
      if (aSet)
669
 
        phone.setDefaultBrowser();
670
 
      else
671
 
        phone.restoreDefaultBrowser();
672
 
    } catch(e) { }
673
 
  },
 
752
  handleEscape: function (aEvent) {
 
753
    aEvent.stopPropagation();
 
754
 
 
755
    // Check open popups
 
756
    if (this._popup) {
 
757
      this._hidePopup();
 
758
      return;
 
759
    }
 
760
 
 
761
    // Check active panel
 
762
    if (this.activePanel) {
 
763
      this.activePanel = null;
 
764
      return;
 
765
    }
 
766
 
 
767
    // Check open dialogs
 
768
    let dialog = this.activeDialog;
 
769
    if (dialog) {
 
770
      dialog.close();
 
771
      return;
 
772
    }
 
773
 
 
774
    // Check open modal elements
 
775
    let modalElementsLength = document.getElementsByClassName("modal-block").length;
 
776
    if (modalElementsLength > 0)
 
777
      return;
 
778
 
 
779
    // Check open panel
 
780
    if (this.isPanelVisible()) {
 
781
      this.hidePanel();
 
782
      return;
 
783
    }
 
784
 
 
785
    // Check content helper
 
786
    let contentHelper = document.getElementById("content-navigator");
 
787
    if (contentHelper.isActive) {
 
788
      contentHelper.model.hide();
 
789
      return;
 
790
    }
 
791
 
 
792
    // Only if there are no dialogs, popups, or panels open
 
793
    let tab = Browser.selectedTab;
 
794
    let browser = tab.browser;
 
795
 
 
796
    if (browser.canGoBack) {
 
797
      browser.goBack();
 
798
    } else if (tab.owner) {
 
799
      this.closeTab(tab);
 
800
    }
 
801
#ifdef ANDROID
 
802
    else {
 
803
      window.QueryInterface(Ci.nsIDOMChromeWindow).minimize();
 
804
      if (tab.closeOnExit)
 
805
        this.closeTab(tab);
 
806
    }
674
807
#endif
 
808
  },
675
809
 
676
 
  handleEvent: function (aEvent) {
 
810
  handleEvent: function handleEvent(aEvent) {
677
811
    switch (aEvent.type) {
678
812
      // Browser events
679
 
      case "DOMWillOpenModalDialog":
680
 
        this._domWillOpenModalDialog(aEvent);
681
 
        break;
682
 
      case "DOMTitleChanged":
683
 
        this._titleChanged(aEvent.target);
684
 
        break;
685
 
      case "DOMLinkAdded":
686
 
        this._linkAdded(aEvent);
687
 
        break;
688
 
      case "DOMWindowClose":
689
 
        this._domWindowClose(aEvent);
690
 
        break;
691
813
      case "TabSelect":
692
814
        this._tabSelect(aEvent);
693
815
        break;
694
816
      case "TabOpen":
695
817
      {
696
 
        let [tabsVisibility,,,] = Browser.computeSidebarVisibility();
697
 
        if (!(tabsVisibility == 1.0) && Browser.selectedTab.chromeTab != aEvent.target)
698
 
          NewTabPopup.show(aEvent.target);
699
 
 
700
818
        // Workaround to hide the tabstrip if it is partially visible
701
819
        // See bug 524469
 
820
        let [tabsVisibility,,,] = Browser.computeSidebarVisibility();
702
821
        if (tabsVisibility > 0.0 && tabsVisibility < 1.0)
703
822
          Browser.hideSidebars();
704
823
 
705
824
        break;
706
825
      }
 
826
      case "PanFinished":
 
827
        let [tabsVisibility,,,] = Browser.computeSidebarVisibility();
 
828
        if (tabsVisibility == 0.0)
 
829
          document.getElementById("tabs").removeClosedTab();
 
830
        break;
707
831
      // Window events
708
832
      case "keypress":
709
 
        if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
710
 
          let dialog = this.activeDialog;
711
 
          if (dialog)
712
 
            dialog.close();
 
833
        if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
 
834
          this.handleEscape(aEvent);
 
835
        break;
 
836
      case "AppCommand":
 
837
        aEvent.stopPropagation();
 
838
        switch (aEvent.command) {
 
839
          case "Menu":
 
840
            this.doCommand("cmd_menu");
 
841
            break;
 
842
          case "Search":
 
843
            if (!this.activePanel)
 
844
              AllPagesList.doCommand();
 
845
            else
 
846
              this.doCommand("cmd_opensearch");
 
847
            break;
 
848
          default:
 
849
            break;
713
850
        }
714
851
        break;
715
852
      // URL textbox events
716
 
      case "mouseup":
717
 
        if (aEvent.detail < 2 && aEvent.button == 0 && gFocusManager.focusedElement == this._edit.inputField) {
718
 
          this.doCommand("cmd_openLocation");
719
 
        }
 
853
      case "click":
 
854
        if (this._edit.readOnly)
 
855
          this._edit.readOnly = false;
720
856
        break;
721
857
      case "mousedown":
722
858
        if (!this._isEventInsidePopup(aEvent))
723
859
          this._hidePopup();
724
860
 
725
 
        let selectAll = gPrefService.getBoolPref("browser.urlbar.doubleClickSelectsAll");
 
861
        let selectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
726
862
        if (aEvent.detail == 2 && aEvent.button == 0 && selectAll && aEvent.target == this._edit) {
727
863
          this._edit.editor.selectAll();
728
864
          aEvent.preventDefault();
732
868
      case "error":
733
869
        this._favicon.src = "";
734
870
        break;
 
871
      // Awesome popup event
 
872
      case "NavigationPanelShown":
 
873
        this._edit.collapsed = false;
 
874
        this._title.collapsed = true;
 
875
 
 
876
        if (!this._edit.readOnly)
 
877
          this._edit.focus();
 
878
 
 
879
        // Disabled the search button if no search engines are available
 
880
        let button = document.getElementById("urlbar-icons");
 
881
        if (BrowserSearch.engines.length)
 
882
          button.removeAttribute("disabled");
 
883
        else
 
884
          button.setAttribute("disabled", "true");
 
885
 
 
886
        break;
 
887
      case "NavigationPanelHidden": {
 
888
        this._edit.collapsed = true;
 
889
        this._title.collapsed = false;
 
890
        this._updateToolbar();
 
891
 
 
892
        let button = document.getElementById("urlbar-icons");
 
893
        button.removeAttribute("open");
 
894
        button.removeAttribute("disabled");
 
895
        break;
 
896
      }
 
897
    }
 
898
  },
 
899
 
 
900
  receiveMessage: function receiveMessage(aMessage) {
 
901
    let browser = aMessage.target;
 
902
    let json = aMessage.json;
 
903
    switch (aMessage.name) {
 
904
      case "DOMTitleChanged":
 
905
        this._titleChanged(browser);
 
906
        break;
 
907
      case "DOMWillOpenModalDialog":
 
908
        return this._domWillOpenModalDialog(browser);
 
909
        break;
 
910
      case "DOMWindowClose":
 
911
        return this._domWindowClose(browser);
 
912
        break;
 
913
      case "DOMLinkAdded":
 
914
        if (Browser.selectedBrowser == browser)
 
915
          this._updateIcon(Browser.selectedBrowser.mIconURL);
 
916
        break;
 
917
      case "Browser:SaveAs:Return":
 
918
        if (json.type != Ci.nsIPrintSettings.kOutputFormatPDF)
 
919
          return;
 
920
 
 
921
        let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
 
922
        let db = dm.DBConnection;
 
923
        let stmt = db.createStatement("UPDATE moz_downloads SET endTime = :endTime, state = :state WHERE id = :id");
 
924
        stmt.params.endTime = Date.now() * 1000;
 
925
        stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_FINISHED;
 
926
        stmt.params.id = json.id;
 
927
        stmt.execute();
 
928
        stmt.finalize();
 
929
 
 
930
        let download = dm.getDownload(json.id);
 
931
#ifdef ANDROID
 
932
        // since our content process doesn't have write permissions to the
 
933
        // downloads dir, we save it to the tmp dir and then move it here
 
934
        let dlFile = download.targetFile;
 
935
        if (!dlFile.exists())
 
936
          dlFile.create(file.NORMAL_FILE_TYPE, 0x666);
 
937
        let tmpDir = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);  
 
938
        let tmpFile = tmpDir.clone();
 
939
        tmpFile.append(dlFile.leafName);
 
940
 
 
941
        // we sometimes race with the content process, so make sure its finished
 
942
        // creating/writing the file
 
943
        while (!tmpFile.exists());
 
944
        tmpFile.moveTo(dlFile.parent, dlFile.leafName);
 
945
#endif
 
946
        try {
 
947
          DownloadsView.downloadCompleted(download);
 
948
          let element = DownloadsView.getElementForDownload(json.id);
 
949
          element.setAttribute("state", Ci.nsIDownloadManager.DOWNLOAD_FINISHED);
 
950
          element.setAttribute("endTime", Date.now());
 
951
          element.setAttribute("referrer", json.referrer);
 
952
          DownloadsView._updateTime(element);
 
953
          DownloadsView._updateStatus(element);
 
954
        }
 
955
        catch(e) {}
 
956
        Services.obs.notifyObservers(download, "dl-done", null);
 
957
        break;
 
958
 
 
959
      case "Browser:OpenURI":
 
960
        let referrerURI = null;
 
961
        if (json.referrer)
 
962
          referrerURI = Services.io.newURI(json.referrer, null, null);
 
963
        Browser.addTab(json.uri, json.bringFront, Browser.selectedTab, { referrerURI: referrerURI });
 
964
        break;
735
965
    }
736
966
  },
737
967
 
746
976
      case "cmd_go":
747
977
      case "cmd_openLocation":
748
978
      case "cmd_star":
 
979
      case "cmd_opensearch":
749
980
      case "cmd_bookmarks":
 
981
      case "cmd_history":
 
982
      case "cmd_remoteTabs":
750
983
      case "cmd_quit":
751
984
      case "cmd_close":
752
985
      case "cmd_menu":
753
986
      case "cmd_newTab":
754
987
      case "cmd_closeTab":
 
988
      case "cmd_undoCloseTab":
755
989
      case "cmd_actions":
756
990
      case "cmd_panel":
757
991
      case "cmd_sanitize":
770
1004
  },
771
1005
 
772
1006
  isCommandEnabled : function(cmd) {
 
1007
    let elem = document.getElementById(cmd);
 
1008
    if (elem && (elem.getAttribute("disabled") == "true"))
 
1009
      return false;
773
1010
    return true;
774
1011
  },
775
1012
 
776
1013
  doCommand : function(cmd) {
777
 
    var browser = getBrowser();
 
1014
    if (!this.isCommandEnabled(cmd))
 
1015
      return;
 
1016
    let browser = getBrowser();
778
1017
    switch (cmd) {
779
1018
      case "cmd_back":
780
1019
        browser.goBack();
787
1026
        break;
788
1027
      case "cmd_forceReload":
789
1028
      {
 
1029
        // Simulate a new page
 
1030
        browser.lastLocation = null;
 
1031
 
790
1032
        const reloadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
791
1033
                            Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
792
1034
        browser.reloadWithFlags(reloadFlags);
799
1041
        this.goToURI();
800
1042
        break;
801
1043
      case "cmd_openLocation":
802
 
        this.showToolbar(true);
803
1044
        this.showAutoComplete();
804
1045
        break;
805
1046
      case "cmd_star":
806
1047
      {
807
 
        var bookmarkURI = browser.currentURI;
808
 
        var bookmarkTitle = browser.contentDocument.title || bookmarkURI.spec;
809
 
 
 
1048
        let bookmarkURI = browser.currentURI;
810
1049
        let autoClose = false;
811
1050
 
812
1051
        if (PlacesUtils.getMostRecentBookmarkForURI(bookmarkURI) == -1) {
813
 
          let bmsvc = PlacesUtils.bookmarks;
814
 
          let bookmarkId = bmsvc.insertBookmark(BookmarkList.mobileRoot, bookmarkURI,
815
 
                                                bmsvc.DEFAULT_INDEX,
816
 
                                                bookmarkTitle);
 
1052
          let bookmarkTitle = browser.contentTitle || bookmarkURI.spec;
 
1053
          let bookmarkService = PlacesUtils.bookmarks;
 
1054
          let bookmarkId = bookmarkService.insertBookmark(BookmarkList.panel.mobileRoot, bookmarkURI,
 
1055
                                                          bookmarkService.DEFAULT_INDEX,
 
1056
                                                          bookmarkTitle);
817
1057
          this.updateStar();
818
1058
 
819
1059
          // autoclose the bookmark popup
824
1064
        BookmarkPopup.toggle(autoClose);
825
1065
        break;
826
1066
      }
 
1067
      case "cmd_opensearch":
 
1068
        this.blurFocusedElement();
 
1069
        BrowserSearch.toggle();
 
1070
        break;
827
1071
      case "cmd_bookmarks":
828
 
        BookmarkList.show();
 
1072
        this.activePanel = BookmarkList;
 
1073
        break;
 
1074
      case "cmd_history":
 
1075
        this.activePanel = HistoryList;
 
1076
        break;
 
1077
      case "cmd_remoteTabs":
 
1078
        this.activePanel = RemoteTabsList;
829
1079
        break;
830
1080
      case "cmd_quit":
831
1081
        goQuitApplication();
834
1084
        this._closeOrQuit();
835
1085
        break;
836
1086
      case "cmd_menu":
 
1087
        AppMenu.toggle();
837
1088
        break;
838
1089
      case "cmd_newTab":
839
1090
        this.newTab();
841
1092
      case "cmd_closeTab":
842
1093
        this.closeTab();
843
1094
        break;
 
1095
      case "cmd_undoCloseTab":
 
1096
        this.undoCloseTab();
 
1097
        break;
844
1098
      case "cmd_sanitize":
845
1099
      {
846
1100
        // disable the button temporarily to indicate something happened
875
1129
        break;
876
1130
      case "cmd_lockscreen":
877
1131
      {
878
 
        let locked = gPrefService.getBoolPref("toolkit.screen.lock");
879
 
        gPrefService.setBoolPref("toolkit.screen.lock", !locked);
 
1132
        let locked = Services.prefs.getBoolPref("toolkit.screen.lock");
 
1133
        Services.prefs.setBoolPref("toolkit.screen.lock", !locked);
 
1134
 
 
1135
        let strings = Elements.browserBundle;
 
1136
        let alerts = Cc["@mozilla.org/alerts-service;1"].getService(Ci.nsIAlertsService);
 
1137
        alerts.showAlertNotification(null, strings.getString("alertLockScreen"),
 
1138
                                     strings.getString("alertLockScreen." + (!locked ? "locked" : "unlocked")), false, "", null);
880
1139
        break;
881
1140
      }
882
1141
    }
883
1142
  }
884
1143
};
885
1144
 
 
1145
var TapHighlightHelper = {
 
1146
  get _overlay() {
 
1147
    if (Browser.selectedTab)
 
1148
      return Browser.selectedTab.overlay;
 
1149
    return null;
 
1150
  },
 
1151
 
 
1152
  show: function show(aRects) {
 
1153
    let overlay = this._overlay;
 
1154
    if (!overlay)
 
1155
      return;
 
1156
 
 
1157
    let browser = getBrowser();
 
1158
    let scroll = browser.getPosition();
 
1159
 
 
1160
    let canvasArea = aRects.reduce(function(a, b) {
 
1161
      return a.expandToContain(b);
 
1162
    }, new Rect(0, 0, 0, 0)).map(function(val) val * browser.scale)
 
1163
                            .translate(-scroll.x, -scroll.y);
 
1164
 
 
1165
    overlay.setAttribute("width", canvasArea.width);
 
1166
    overlay.setAttribute("height", canvasArea.height);
 
1167
 
 
1168
    let ctx = overlay.getContext("2d");
 
1169
    ctx.save();
 
1170
    ctx.translate(-canvasArea.left, -canvasArea.top);
 
1171
    ctx.scale(browser.scale, browser.scale);
 
1172
 
 
1173
    overlay.setAttribute("left", canvasArea.left);
 
1174
    overlay.setAttribute("top", canvasArea.top);
 
1175
    ctx.clearRect(0, 0, canvasArea.width, canvasArea.height);
 
1176
    ctx.fillStyle = "rgba(0, 145, 255, .5)";
 
1177
    for (let i = aRects.length - 1; i >= 0; i--) {
 
1178
      let rect = aRects[i];
 
1179
      ctx.fillRect(rect.left - scroll.x / browser.scale, rect.top - scroll.y / browser.scale, rect.width, rect.height);
 
1180
    }
 
1181
    ctx.restore();
 
1182
    overlay.style.display = "block";
 
1183
 
 
1184
    addEventListener("MozBeforePaint", this, false);
 
1185
    mozRequestAnimationFrame();
 
1186
  },
 
1187
 
 
1188
  /**
 
1189
   * Hide the highlight. aGuaranteeShowMsecs specifies how many milliseconds the
 
1190
   * highlight should be shown before it disappears.
 
1191
   */
 
1192
  hide: function hide(aGuaranteeShowMsecs) {
 
1193
    if (!this._overlay || this._overlay.style.display == "none")
 
1194
      return;
 
1195
 
 
1196
    this._guaranteeShow = Math.max(0, aGuaranteeShowMsecs);
 
1197
    if (this._guaranteeShow) {
 
1198
      // _shownAt is set once highlight has been painted
 
1199
      if (this._shownAt)
 
1200
        setTimeout(this._hide.bind(this),
 
1201
                   Math.max(0, this._guaranteeShow - (mozAnimationStartTime - this._shownAt)));
 
1202
    } else {
 
1203
      this._hide();
 
1204
    }
 
1205
  },
 
1206
 
 
1207
  /** Helper function that hides popup immediately. */
 
1208
  _hide: function _hide() {
 
1209
    this._shownAt = 0;
 
1210
    this._guaranteeShow = 0;
 
1211
    this._overlay.style.display = "none";
 
1212
  },
 
1213
 
 
1214
  handleEvent: function handleEvent(ev) {
 
1215
    removeEventListener("MozBeforePaint", this, false);
 
1216
    this._shownAt = ev.timeStamp;
 
1217
    // hide has been called, so hide the tap highlight after it has
 
1218
    // been shown for a moment.
 
1219
    if (this._guaranteeShow)
 
1220
      this.hide(this._guaranteeShow);
 
1221
  }
 
1222
};
 
1223
 
886
1224
var PageActions = {
887
 
  get _permissionManager() {
888
 
    delete this._permissionManager;
889
 
    return this._permissionManager = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
 
1225
  init: function init() {
 
1226
    document.getElementById("pageactions-container").addEventListener("click", this, false);
 
1227
 
 
1228
    this.register("pageaction-reset", this.updatePagePermissions, this);
 
1229
    this.register("pageaction-password", this.updateForgetPassword, this);
 
1230
#ifdef NS_PRINTING
 
1231
    this.register("pageaction-saveas", this.updatePageSaveAs, this);
 
1232
#endif
 
1233
    this.register("pageaction-share", this.updateShare, this);
 
1234
    this.register("pageaction-search", BrowserSearch.updatePageSearchEngines, BrowserSearch);
 
1235
  },
 
1236
 
 
1237
  handleEvent: function handleEvent(aEvent) {
 
1238
    switch (aEvent.type) {
 
1239
      case "click":
 
1240
        getIdentityHandler().hide();
 
1241
        break;
 
1242
    }
 
1243
  },
 
1244
 
 
1245
  /**
 
1246
   * @param aId id of a pageaction element
 
1247
   * @param aCallback function that takes an element and returns true if it should be visible
 
1248
   * @param aThisObj (optional) scope object for aCallback
 
1249
   */
 
1250
  register: function register(aId, aCallback, aThisObj) {
 
1251
    this._handlers.push({id: aId, callback: aCallback, obj: aThisObj});
 
1252
  },
 
1253
 
 
1254
  _handlers: [],
 
1255
 
 
1256
  updateSiteMenu: function updateSiteMenu() {
 
1257
    this._handlers.forEach(function(action) {
 
1258
      let node = document.getElementById(action.id);
 
1259
      node.hidden = !action.callback.call(action.obj, node);
 
1260
    });
 
1261
    this._updateAttributes();
890
1262
  },
891
1263
 
892
1264
  get _loginManager() {
898
1270
  _permissions: ["popup", "offline-app", "geo"],
899
1271
 
900
1272
  _forEachPermissions: function _forEachPermissions(aHost, aCallback) {
901
 
    let pm = this._permissionManager;
 
1273
    let pm = Services.perms;
902
1274
    for (let i = 0; i < this._permissions.length; i++) {
903
1275
      let type = this._permissions[i];
904
1276
      if (!pm.testPermission(aHost, type))
913
1285
    }
914
1286
  },
915
1287
 
916
 
  updatePagePermissions: function updatePagePermissions() {
917
 
    this.removeItems("preferences");
918
 
 
 
1288
  updatePagePermissions: function updatePagePermissions(aNode) {
919
1289
    let host = Browser.selectedBrowser.currentURI;
920
1290
    let permissions = [];
921
1291
 
922
1292
    this._forEachPermissions(host, function(aType) {
923
 
      permissions.push(aType);
 
1293
      permissions.push("pageactions." + aType);
924
1294
    });
925
1295
 
 
1296
    if (!this._loginManager.getLoginSavingEnabled(host.prePath)) {
 
1297
      // If rememberSignons is false, then getLoginSavingEnabled returns false
 
1298
      // for all pages, so we should just ignore it (Bug 601163).
 
1299
      if (Services.prefs.getBoolPref("signon.rememberSignons"))
 
1300
        permissions.push("pageactions.password");
 
1301
    }
 
1302
 
 
1303
    let descriptions = permissions.map(function(s) Elements.browserBundle.getString(s));
 
1304
    aNode.setAttribute("description", descriptions.join(", "));
 
1305
 
 
1306
    return (permissions.length > 0);
 
1307
  },
 
1308
 
 
1309
  updateForgetPassword: function updateForgetPassword(aNode) {
 
1310
    let host = Browser.selectedBrowser.currentURI;
 
1311
    let logins = this._loginManager.findLogins({}, host.prePath, "", "");
 
1312
 
 
1313
    return logins.some(function(login) login.hostname == host.prePath);
 
1314
  },
 
1315
 
 
1316
  forgetPassword: function forgetPassword(aEvent) {
 
1317
    let host = Browser.selectedBrowser.currentURI;
926
1318
    let lm = this._loginManager;
927
 
    if (!lm.getLoginSavingEnabled(host.prePath)) {
928
 
      permissions.push("password");
929
 
    }
930
 
 
931
 
    // Show the clear site preferences button if needed
932
 
    if (permissions.length) {
933
 
      let title = Elements.browserBundle.getString("pageactions.reset");
934
 
      let description = [];
935
 
      for each(permission in permissions)
936
 
        description.push(Elements.browserBundle.getString("pageactions." + permission));
937
 
 
938
 
      let node = this.appendItem("preferences", title, description.join(", "));
939
 
      node.addEventListener("click", function(event) {
940
 
          PageActions.clearPagePermissions();
941
 
          PageActions.removeItem(node);
942
 
        },
943
 
        false);
944
 
    }
945
 
 
946
 
    let siteLogins = [];
947
 
    let allLogins = lm.findLogins({}, host.prePath, "", null);
948
 
    for (let i = 0; i < allLogins.length; i++) {
949
 
      let login = allLogins[i];
950
 
      if (login.hostname != host.prePath)
951
 
        continue;
952
 
 
953
 
      siteLogins.push(login);
954
 
    }
955
 
 
956
 
    // Show only 1 password button for all the saved logins
957
 
    if (siteLogins.length) {
958
 
      let title = Elements.browserBundle.getString("pageactions.password.forget");
959
 
      let node = this.appendItem("preferences", title, "");
960
 
      node.addEventListener("click", function(event) {
961
 
          for (let i = 0; i < siteLogins.length; i++)
962
 
            lm.removeLogin(siteLogins[i]);
963
 
  
964
 
          PageActions.removeItem(node);
965
 
        },
966
 
        false);
967
 
    }
 
1319
 
 
1320
    lm.findLogins({}, host.prePath, "", "").forEach(function(login) {
 
1321
      if (login.hostname == host.prePath)
 
1322
        lm.removeLogin(login);
 
1323
    });
 
1324
 
 
1325
    this.hideItem(aEvent.target);
 
1326
    aEvent.stopPropagation(); // Don't hide the site menu.
968
1327
  },
969
1328
 
970
 
  clearPagePermissions: function clearPagePermissions() {
971
 
    let pm = this._permissionManager;
 
1329
  clearPagePermissions: function clearPagePermissions(aEvent) {
 
1330
    let pm = Services.perms;
972
1331
    let host = Browser.selectedBrowser.currentURI;
973
1332
    this._forEachPermissions(host, function(aType) {
974
1333
      pm.remove(host.asciiHost, aType);
977
1336
    let lm = this._loginManager;
978
1337
    if (!lm.getLoginSavingEnabled(host.prePath))
979
1338
      lm.setLoginSavingEnabled(host.prePath, true);
 
1339
 
 
1340
    this.hideItem(aEvent.target);
 
1341
    aEvent.stopPropagation(); // Don't hide the site menu.
980
1342
  },
981
1343
 
982
 
  _savePageAsPDF: function saveAsPDF() {
983
 
    let contentWindow = Browser.selectedBrowser.contentWindow;
 
1344
  savePageAsPDF: function saveAsPDF() {
 
1345
    let browser = Browser.selectedBrowser;
 
1346
    let fileName = getDefaultFileName(browser.contentTitle, browser.documentURI, null, null);
 
1347
    fileName = fileName.trim() + ".pdf";
 
1348
#ifdef MOZ_PLATFORM_MAEMO
 
1349
    fileName = fileName.replace(/[\*\:\?]+/g, " ");
 
1350
#endif
 
1351
 
 
1352
    let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
 
1353
    let downloadsDir = dm.defaultDownloadsDirectory;
 
1354
 
 
1355
#ifdef ANDROID
 
1356
    // Create the final destination file location
 
1357
    let file = downloadsDir.clone();
 
1358
    file.append(fileName);
 
1359
    // The filename is used below to save the file to a temp location in 
 
1360
    // the content process. Make sure it's up to date.
 
1361
    file.createUnique(file.NORMAL_FILE_TYPE, 0x666);
 
1362
    fileName = file.leafName;
 
1363
#else
984
1364
    let strings = Elements.browserBundle;
985
1365
    let picker = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
986
1366
    picker.init(window, strings.getString("pageactions.saveas.pdf"), Ci.nsIFilePicker.modeSave);
987
1367
    picker.appendFilter("PDF", "*.pdf");
988
1368
    picker.defaultExtension = "pdf";
989
1369
 
990
 
    let fileName = getDefaultFileName(null, null, contentWindow.document, null);
991
 
#ifdef MOZ_PLATFORM_MAEMO
992
 
    fileName = fileName.replace(/[\*\:\?]+/g, " ");
993
 
#endif
994
 
    picker.defaultString = fileName + ".pdf";
995
 
 
996
 
    let dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
997
 
    picker.displayDirectory = dnldMgr.defaultDownloadsDirectory;
998
 
 
 
1370
    picker.defaultString = fileName;
 
1371
 
 
1372
    picker.displayDirectory = downloadsDir;
999
1373
    let rv = picker.show();
1000
1374
    if (rv == Ci.nsIFilePicker.returnCancel)
1001
1375
      return;
1002
1376
 
1003
 
    let printSettings = Cc["@mozilla.org/gfx/printsettings-service;1"]
1004
 
                          .getService(Ci.nsIPrintSettingsService)
1005
 
                          .newPrintSettings;
1006
 
    printSettings.printSilent = true;
1007
 
    printSettings.showPrintProgress = false;
1008
 
    printSettings.printBGImages = true;
1009
 
    printSettings.printBGColors = true;
1010
 
    printSettings.printToFile = true;
1011
 
    printSettings.toFileName = picker.file.path;
1012
 
    printSettings.printFrameType = Ci.nsIPrintSettings.kFramesAsIs;
1013
 
    printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
1014
 
 
1015
 
    //XXX we probably need a preference here, the header can be useful
1016
 
    printSettings.footerStrCenter = '';
1017
 
    printSettings.footerStrLeft   = '';
1018
 
    printSettings.footerStrRight  = '';
1019
 
    printSettings.headerStrCenter = '';
1020
 
    printSettings.headerStrLeft   = '';
1021
 
    printSettings.headerStrRight  = '';
1022
 
 
1023
 
    let webBrowserPrint = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
1024
 
                                       .getInterface(Ci.nsIWebBrowserPrint);
1025
 
    webBrowserPrint.print(printSettings, null);
1026
 
  },
1027
 
 
1028
 
  updatePageSaveAs: function updatePageSaveAs() {
1029
 
    this.removeItems("saveas");
1030
 
    if (Browser.selectedBrowser.contentDocument instanceof XULDocument)
1031
 
      return;
1032
 
 
1033
 
    let strings = Elements.browserBundle;
1034
 
    let node = this.appendItem("saveas", strings.getString("pageactions.saveas.pdf"), "");
1035
 
    node.onclick = function(event) {
1036
 
      PageActions._savePageAsPDF();
1037
 
    }
1038
 
  },
1039
 
 
1040
 
  appendItem: function appendItem(aType, aTitle, aDesc) {
1041
 
    let container = document.getElementById("pageactions-container");
1042
 
    let item = document.createElement("pageaction");
1043
 
    item.setAttribute("title", aTitle);
1044
 
    item.setAttribute("description", aDesc);
1045
 
    item.setAttribute("type", aType);
1046
 
    container.appendChild(item);
1047
 
 
1048
 
    let identityContainer = document.getElementById("identity-container");
1049
 
    identityContainer.setAttribute("hasmenu", "true");
1050
 
    return item;
1051
 
  },
1052
 
 
1053
 
  removeItem: function removeItem(aItem) {
1054
 
    let container = document.getElementById("pageactions-container");
1055
 
    container.removeChild(aItem);
1056
 
 
1057
 
    let identityContainer = document.getElementById("identity-container");
1058
 
    identityContainer.setAttribute("hasmenu", container.hasChildNodes() ? "true" : "false");
1059
 
  },
1060
 
 
1061
 
  removeItems: function removeItems(aType) {
1062
 
    let container = document.getElementById("pageactions-container");
1063
 
    let count = container.childNodes.length;
1064
 
    for (let i = count - 1; i >= 0; i--) {
1065
 
      if (aType == "" || container.childNodes[i].getAttribute("type") == aType)
1066
 
        this.removeItem(container.childNodes[i]);
1067
 
    }
 
1377
    let file = picker.file;
 
1378
#endif
 
1379
 
 
1380
    // We must manually add this to the download system
 
1381
    let db = dm.DBConnection;
 
1382
 
 
1383
    let stmt = db.createStatement(
 
1384
      "INSERT INTO moz_downloads (name, source, target, startTime, endTime, state, referrer) " +
 
1385
      "VALUES (:name, :source, :target, :startTime, :endTime, :state, :referrer)"
 
1386
    );
 
1387
 
 
1388
    let current = browser.currentURI.spec;
 
1389
    stmt.params.name = file.leafName;
 
1390
    stmt.params.source = current;
 
1391
    stmt.params.target = Services.io.newFileURI(file).spec;
 
1392
    stmt.params.startTime = Date.now() * 1000;
 
1393
    stmt.params.endTime = Date.now() * 1000;
 
1394
    stmt.params.state = Ci.nsIDownloadManager.DOWNLOAD_NOTSTARTED;
 
1395
    stmt.params.referrer = current;
 
1396
    stmt.execute();
 
1397
    stmt.finalize();
 
1398
 
 
1399
    let newItemId = db.lastInsertRowID;
 
1400
    let download = dm.getDownload(newItemId);
 
1401
    try {
 
1402
      DownloadsView.downloadStarted(download);
 
1403
    }
 
1404
    catch(e) {}
 
1405
    Services.obs.notifyObservers(download, "dl-start", null);
 
1406
 
 
1407
#ifdef ANDROID
 
1408
    let tmpDir = Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("TmpD", Ci.nsIFile);  
 
1409
    
 
1410
    file = tmpDir.clone();
 
1411
    file.append(fileName);
 
1412
 
 
1413
#endif
 
1414
 
 
1415
    let data = {
 
1416
      type: Ci.nsIPrintSettings.kOutputFormatPDF,
 
1417
      id: newItemId,
 
1418
      referrer: current,
 
1419
      filePath: file.path
 
1420
    };
 
1421
 
 
1422
    Browser.selectedBrowser.messageManager.sendAsyncMessage("Browser:SaveAs", data);
 
1423
  },
 
1424
 
 
1425
  updatePageSaveAs: function updatePageSaveAs(aNode) {
 
1426
    // Check for local XUL content
 
1427
    let contentWindow = Browser.selectedBrowser.contentWindow;
 
1428
    return !(contentWindow && contentWindow.document instanceof XULDocument);
 
1429
  },
 
1430
 
 
1431
  updateShare: function updateShare(aNode) {
 
1432
    return Util.isShareableScheme(Browser.selectedBrowser.currentURI.scheme);
 
1433
  },
 
1434
 
 
1435
  hideItem: function hideItem(aNode) {
 
1436
    aNode.hidden = true;
 
1437
    this._updateAttributes();
 
1438
  },
 
1439
 
 
1440
  _updateAttributes: function _updateAttributes() {
 
1441
    let container = document.getElementById("pageactions-container");
 
1442
    let visibleNodes = container.querySelectorAll("pageaction:not([hidden=true])");
 
1443
    let visibleCount = visibleNodes.length;
 
1444
 
 
1445
    for (let i = 0; i < visibleCount; i++)
 
1446
      visibleNodes[i].classList.remove("odd-last-child");
 
1447
 
 
1448
    if (visibleCount % 2)
 
1449
      visibleNodes[visibleCount - 1].classList.add("odd-last-child");
1068
1450
  }
1069
 
}
 
1451
};
1070
1452
 
1071
1453
var NewTabPopup = {
1072
1454
  _timeout: 0,
1074
1456
 
1075
1457
  get box() {
1076
1458
    delete this.box;
1077
 
    return this.box = document.getElementById("newtab-popup");
 
1459
    let box = document.getElementById("newtab-popup");
 
1460
 
 
1461
    // Move the popup on the other side if we are in RTL
 
1462
    let [leftSidebar, rightSidebar] = [Elements.tabs.getBoundingClientRect(), Elements.controls.getBoundingClientRect()];
 
1463
    if (leftSidebar.left > rightSidebar.left) {
 
1464
      let margin = box.getAttribute("left");
 
1465
      box.removeAttribute("left");
 
1466
      box.setAttribute("right", margin);
 
1467
    }
 
1468
 
 
1469
    return this.box = box;
1078
1470
  },
1079
1471
 
1080
 
  _updateLabel: function() {
 
1472
  _updateLabel: function nt_updateLabel() {
1081
1473
    let newtabStrings = Elements.browserBundle.getString("newtabpopup.opened");
1082
1474
    let label = PluralForm.get(this._tabs.length, newtabStrings).replace("#1", this._tabs.length);
1083
1475
 
1084
1476
    this.box.firstChild.setAttribute("value", label);
1085
1477
  },
1086
1478
 
1087
 
  hide: function() {
 
1479
  hide: function nt_hide() {
1088
1480
    if (this._timeout) {
1089
1481
      clearTimeout(this._timeout);
1090
1482
      this._timeout = 0;
1092
1484
 
1093
1485
    this._tabs = [];
1094
1486
    this.box.hidden = true;
1095
 
    BrowserUI.popPopup();
 
1487
    BrowserUI.popPopup(this);
1096
1488
  },
1097
1489
 
1098
 
  show: function(aTab) {
 
1490
  show: function nt_show(aTab) {
1099
1491
    BrowserUI.pushPopup(this, this.box);
1100
1492
 
1101
1493
    this._tabs.push(aTab);
1112
1504
    }, 2000, this);
1113
1505
  },
1114
1506
 
1115
 
  selectTab: function() {
 
1507
  selectTab: function nt_selectTab() {
1116
1508
    BrowserUI.selectTab(this._tabs.pop());
1117
1509
    this.hide();
1118
 
  }
1119
 
}
 
1510
  },
 
1511
 
 
1512
  handleEvent: function nt_handleEvent(aEvent) {
 
1513
    // Bail early and fast
 
1514
    if (!aEvent.detail)
 
1515
      return;
 
1516
 
 
1517
    let [tabsVisibility,,,] = Browser.computeSidebarVisibility();
 
1518
    if (tabsVisibility != 1.0)
 
1519
      this.show(aEvent.originalTarget);
 
1520
  }
 
1521
};
 
1522
 
 
1523
var AwesomePanel = function(aElementId, aCommandId) {
 
1524
  let command = document.getElementById(aCommandId);
 
1525
 
 
1526
  this.panel = document.getElementById(aElementId),
 
1527
 
 
1528
  this.open = function aw_open() {
 
1529
    command.setAttribute("checked", "true");
 
1530
    this.panel.hidden = false;
 
1531
 
 
1532
    if (this.panel.hasAttribute("onshow")) {
 
1533
      let func = new Function("panel", this.panel.getAttribute("onshow"));
 
1534
      func.call(this.panel);
 
1535
    }
 
1536
 
 
1537
    if (this.panel.open)
 
1538
      this.panel.open();
 
1539
  },
 
1540
 
 
1541
  this.close = function aw_close() {
 
1542
    if (this.panel.hasAttribute("onhide")) {
 
1543
      let func = new Function("panel", this.panel.getAttribute("onhide"));
 
1544
      func.call(this.panel);
 
1545
    }
 
1546
 
 
1547
    if (this.panel.close)
 
1548
      this.panel.close();
 
1549
 
 
1550
    this.panel.hidden = true;
 
1551
    command.removeAttribute("checked", "true");
 
1552
  },
 
1553
 
 
1554
  this.doCommand = function aw_doCommand() {
 
1555
    BrowserUI.doCommand(aCommandId);
 
1556
  },
 
1557
 
 
1558
  this.openLink = function aw_openLink(aEvent) {
 
1559
    let item = aEvent.originalTarget;
 
1560
    let uri = item.getAttribute("url") || item.getAttribute("uri");
 
1561
    if (uri != "") {
 
1562
      Browser.selectedBrowser.userTypedValue = uri;
 
1563
      BrowserUI.goToURI(uri);
 
1564
    }
 
1565
  }
 
1566
};
1120
1567
 
1121
1568
var BookmarkPopup = {
1122
1569
  get box() {
1123
1570
    delete this.box;
1124
 
    return this.box = document.getElementById("bookmark-popup");
 
1571
    this.box = document.getElementById("bookmark-popup");
 
1572
 
 
1573
    let [tabsSidebar, controlsSidebar] = [Elements.tabs.getBoundingClientRect(), Elements.controls.getBoundingClientRect()];
 
1574
    this.box.setAttribute(tabsSidebar.left < controlsSidebar.left ? "right" : "left", controlsSidebar.width - this.box.offset);
 
1575
    this.box.top = BrowserUI.starButton.getBoundingClientRect().top - this.box.offset;
 
1576
 
 
1577
    // Hide the popup if there is any new page loading
 
1578
    let self = this;
 
1579
    messageManager.addMessageListener("pagehide", function(aMessage) {
 
1580
      self.hide();
 
1581
    });
 
1582
 
 
1583
    return this.box;
1125
1584
  },
1126
1585
 
1127
1586
  _bookmarkPopupTimeout: -1,
1132
1591
      this._bookmarkPopupTimeout = -1;
1133
1592
    }
1134
1593
    this.box.hidden = true;
1135
 
    BrowserUI.popPopup();
 
1594
    BrowserUI.popPopup(this);
1136
1595
  },
1137
1596
 
1138
1597
  show : function show(aAutoClose) {
1139
 
    const margin = 10;
1140
 
 
1141
1598
    this.box.hidden = false;
1142
 
 
1143
 
    let [,,,controlsW] = Browser.computeSidebarVisibility();
1144
 
    this.box.left = window.innerWidth - (this.box.getBoundingClientRect().width + controlsW + margin);
1145
 
    this.box.top  = BrowserUI.starButton.getBoundingClientRect().top + margin;
 
1599
    this.box.anchorTo(BrowserUI.starButton);
1146
1600
 
1147
1601
    if (aAutoClose) {
1148
1602
      this._bookmarkPopupTimeout = setTimeout(function (self) {
1161
1615
    else
1162
1616
      this.hide();
1163
1617
  }
1164
 
}
 
1618
};
1165
1619
 
1166
1620
var BookmarkHelper = {
1167
1621
  _panel: null,
1200
1654
    BrowserUI.pushPopup(this, this._panel);
1201
1655
 
1202
1656
    let self = this;
 
1657
    BrowserUI.lockToolbar();
1203
1658
    Browser.forceChromeReflow();
1204
1659
    self._editor.startEditing();
1205
1660
  },
1209
1664
  },
1210
1665
 
1211
1666
  hide: function BH_hide() {
 
1667
    BrowserUI.unlockToolbar();
1212
1668
    BrowserUI.updateStar();
1213
1669
 
1214
1670
    // Note: the _editor will have already saved the data, if needed, by the time
1217
1673
    this._editor = null;
1218
1674
 
1219
1675
    this._panel.hidden = true;
1220
 
    BrowserUI.popPopup();
1221
 
  },
1222
 
};
1223
 
 
1224
 
var BookmarkList = {
1225
 
  _panel: null,
1226
 
  _bookmarks: null,
1227
 
  _manageButtton: null,
1228
 
 
1229
 
  get mobileRoot() {
1230
 
    let items = PlacesUtils.annotations.getItemsWithAnnotation("mobile/bookmarksRoot", {});
1231
 
    if (!items.length)
1232
 
      throw "Couldn't find mobile bookmarks root!";
1233
 
 
1234
 
    delete this.mobileRoot;
1235
 
    return this.mobileRoot = items[0];
1236
 
  },
1237
 
 
1238
 
  show: function() {
1239
 
    this._panel = document.getElementById("bookmarklist-container");
1240
 
    this._panel.width = window.innerWidth;
1241
 
    this._panel.height = window.innerHeight;
1242
 
    this._panel.hidden = false;
1243
 
    BrowserUI.pushDialog(this);
1244
 
 
1245
 
    this._bookmarks = document.getElementById("bookmark-items");
1246
 
    this._bookmarks.addEventListener("BookmarkRemove", this, true);
1247
 
    this._bookmarks.manageUI = false;
1248
 
    this._bookmarks.openFolder();
1249
 
 
1250
 
    this._manageButton = document.getElementById("tool-bookmarks-manage");
1251
 
    this._manageButton.disabled = (this._bookmarks.items.length == 0);
1252
 
  },
1253
 
 
1254
 
  close: function() {
 
1676
    BrowserUI.popPopup(this);
 
1677
  },
 
1678
 
 
1679
  removeBookmarksForURI: function BH_removeBookmarksForURI(aURI) {
 
1680
    //XXX blargle xpconnect! might not matter, but a method on
 
1681
    // nsINavBookmarksService that takes an array of items to
 
1682
    // delete would be faster. better yet, a method that takes a URI!
 
1683
    let itemIds = PlacesUtils.getBookmarksForURI(aURI);
 
1684
    itemIds.forEach(PlacesUtils.bookmarks.removeItem);
 
1685
 
1255
1686
    BrowserUI.updateStar();
1256
 
 
1257
 
    if (this._bookmarks.manageUI)
1258
 
      this.toggleManage();
1259
 
    this._bookmarks.blur();
1260
 
    this._bookmarks.removeEventListener("BookmarkRemove", this, true);
1261
 
 
1262
 
    this._panel.hidden = true;
1263
 
    BrowserUI.popDialog();
1264
 
  },
1265
 
 
1266
 
  toggleManage: function() {
1267
 
    this._bookmarks.manageUI = !(this._bookmarks.manageUI);
1268
 
    this._manageButton.checked = this._bookmarks.manageUI;
1269
 
  },
1270
 
 
1271
 
  openBookmark: function() {
1272
 
    let item = this._bookmarks.activeItem;
1273
 
    if (item.spec) {
1274
 
      this.close();
1275
 
      BrowserUI.goToURI(item.spec);
1276
 
    }
1277
 
  },
1278
 
 
1279
 
  handleEvent: function(aEvent) {
1280
 
    if (aEvent.type == "BookmarkRemove") {
1281
 
      if (this._bookmarks.isRootFolder && this._bookmarks.items.length == 1) {
1282
 
        this._manageButton.disabled = true;
1283
 
        this.toggleManage();
1284
 
      }
1285
 
    }
1286
1687
  }
1287
1688
};
1288
1689
 
1289
 
var FormHelper = {
 
1690
var FindHelperUI = {
 
1691
  type: "find",
 
1692
  commands: {
 
1693
    next: "cmd_findNext",
 
1694
    previous: "cmd_findPrevious",
 
1695
    close: "cmd_findClose"
 
1696
  },
 
1697
 
1290
1698
  _open: false,
1291
 
  _utils: null,
1292
 
  _nodes: null,
1293
 
  get _container() {
1294
 
    delete this._container;
1295
 
    return this._container = document.getElementById("form-helper-container");
1296
 
  },
1297
 
 
1298
 
  get _helperSpacer() {
1299
 
    delete this._helperSpacer;
1300
 
    return this._helperSpacer = document.getElementById("form-helper-spacer");
1301
 
  },
1302
 
 
1303
 
  get _selectContainer() {
1304
 
    delete this._selectContainer;
1305
 
    return this._selectContainer = document.getElementById("select-container");
1306
 
  },
1307
 
 
1308
 
  get _autofillContainer() {
1309
 
    delete this._autofillContainer;
1310
 
    return this._autofillContainer = document.getElementById("form-helper-autofill");
1311
 
  },
1312
 
 
1313
 
  _getRectForElement: function formHelper_getRectForElement(aElement) {
1314
 
    const kDistanceMax = 100;
1315
 
    let elRect = Browser.getBoundingContentRect(aElement);
1316
 
    let bv = Browser._browserView;
1317
 
 
1318
 
    let labels = this.getLabelsFor(aElement);
1319
 
    for (let i=0; i<labels.length; i++) {
1320
 
      let labelRect = Browser.getBoundingContentRect(labels[i]);
1321
 
      if (labelRect.left < elRect.left) {
1322
 
        let isClose = Math.abs(labelRect.left - elRect.left) - labelRect.width < kDistanceMax &&
1323
 
                      Math.abs(labelRect.top - elRect.top) - labelRect.height < kDistanceMax;
1324
 
        if (isClose) {
1325
 
          let width = labelRect.width + elRect.width + (elRect.left - labelRect.left - labelRect.width);
1326
 
          return new Rect(labelRect.left, labelRect.top, width, elRect.height).expandToIntegers();
1327
 
        }
1328
 
      }
1329
 
    }
1330
 
    return elRect;
1331
 
  },
1332
 
 
1333
 
  _update: function(aPreviousElement, aNewElement) {
1334
 
    this._updateSelect(aPreviousElement, aNewElement);
1335
 
 
1336
 
    // Setup autofill UI
1337
 
    if (aNewElement instanceof HTMLInputElement && aNewElement.type == "text") {
1338
 
      let suggestions = this._getSuggestions();
1339
 
      this._setSuggestions(suggestions);
 
1699
  _status: null,
 
1700
 
 
1701
  get status() {
 
1702
    return this._status;
 
1703
  },
 
1704
 
 
1705
  set status(val) {
 
1706
    if (val != this._status) {
 
1707
      this._status = val;
 
1708
      this._textbox.setAttribute("status", val);
 
1709
      this.updateCommands(this._textbox.value);
 
1710
    }
 
1711
  },
 
1712
 
 
1713
  init: function findHelperInit() {
 
1714
    this._textbox = document.getElementById("find-helper-textbox");
 
1715
    this._container = document.getElementById("content-navigator");
 
1716
 
 
1717
    this._cmdPrevious = document.getElementById(this.commands.previous);
 
1718
    this._cmdNext = document.getElementById(this.commands.next);
 
1719
 
 
1720
    // Listen for form assistant messages from content
 
1721
    messageManager.addMessageListener("FindAssist:Show", this);
 
1722
    messageManager.addMessageListener("FindAssist:Hide", this);
 
1723
 
 
1724
    // Listen for events where form assistant should be closed
 
1725
    document.getElementById("tabs").addEventListener("TabSelect", this, true);
 
1726
    Elements.browsers.addEventListener("URLChanged", this, true);
 
1727
  },
 
1728
 
 
1729
  receiveMessage: function findHelperReceiveMessage(aMessage) {
 
1730
    let json = aMessage.json;
 
1731
    switch(aMessage.name) {
 
1732
      case "FindAssist:Show":
 
1733
        this.status = json.result;
 
1734
        if (json.rect)
 
1735
          this._zoom(Rect.fromRect(json.rect));
 
1736
        break;
 
1737
 
 
1738
      case "FindAssist:Hide":
 
1739
        if (this._container.getAttribute("type") == this.type)
 
1740
          this.hide();
 
1741
        break;
 
1742
    }
 
1743
  },
 
1744
 
 
1745
  handleEvent: function findHelperHandleEvent(aEvent) {
 
1746
    if (aEvent.type == "TabSelect" || (aEvent.type == "URLChanged" && aEvent.target == Browser.selectedBrowser))
 
1747
      this.hide();
 
1748
  },
 
1749
 
 
1750
  show: function findHelperShow() {
 
1751
    this._container.show(this);
 
1752
    this.search(this._textbox.value);
 
1753
    this._textbox.select();
 
1754
    this._textbox.focus();
 
1755
    this._open = true;
 
1756
 
 
1757
    // Prevent the view to scroll automatically while searching
 
1758
    Browser.selectedBrowser.scrollSync = false;
 
1759
  },
 
1760
 
 
1761
  hide: function findHelperHide() {
 
1762
    if (!this._open)
 
1763
      return;
 
1764
 
 
1765
    this._textbox.value = "";
 
1766
    this.status = null;
 
1767
    this._textbox.blur();
 
1768
    this._container.hide(this);
 
1769
    this._open = false;
 
1770
 
 
1771
    // Restore the scroll synchronisation
 
1772
    Browser.selectedBrowser.scrollSync = true;
 
1773
  },
 
1774
 
 
1775
  goToPrevious: function findHelperGoToPrevious() {
 
1776
    Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Previous", { });
 
1777
  },
 
1778
 
 
1779
  goToNext: function findHelperGoToNext() {
 
1780
    Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Next", { });
 
1781
  },
 
1782
 
 
1783
  search: function findHelperSearch(aValue) {
 
1784
    this.updateCommands(aValue);
 
1785
 
 
1786
    // Don't bother searching if the value is empty
 
1787
    if (aValue == "")
 
1788
      return;
 
1789
 
 
1790
    Browser.selectedBrowser.messageManager.sendAsyncMessage("FindAssist:Find", { searchString: aValue });
 
1791
  },
 
1792
 
 
1793
  updateCommands: function findHelperUpdateCommands(aValue) {
 
1794
    let disabled = (this._status == Ci.nsITypeAheadFind.FIND_NOTFOUND) || (aValue == "");
 
1795
    this._cmdPrevious.setAttribute("disabled", disabled);
 
1796
    this._cmdNext.setAttribute("disabled", disabled);
 
1797
  },
 
1798
 
 
1799
  _zoom: function _findHelperZoom(aElementRect) {
 
1800
    // Zoom to a specified Rect
 
1801
    if (aElementRect && Browser.selectedTab.allowZoom && Services.prefs.getBoolPref("findhelper.autozoom")) {
 
1802
      let zoomLevel = Browser._getZoomLevelForRect(aElementRect);
 
1803
      zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax);
 
1804
      zoomLevel = Browser.selectedTab.clampZoomLevel(zoomLevel);
 
1805
 
 
1806
      let zoomRect = Browser._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
 
1807
      Browser.animatedZoomTo(zoomRect);
 
1808
    }
 
1809
  }
 
1810
};
 
1811
 
 
1812
/**
 
1813
 * Responsible for navigating forms and filling in information.
 
1814
 *  - Navigating forms is handled by next and previous commands.
 
1815
 *  - When an element is focused, the browser view zooms in to the control.
 
1816
 *  - The caret positionning and the view are sync to keep the type
 
1817
 *    in text into view for input fields (text/textarea).
 
1818
 *  - Provides autocomplete box for input fields.
 
1819
 */
 
1820
var FormHelperUI = {
 
1821
  type: "form",
 
1822
  commands: {
 
1823
    next: "cmd_formNext",
 
1824
    previous: "cmd_formPrevious",
 
1825
    close: "cmd_formClose"
 
1826
  },
 
1827
 
 
1828
  get enabled() {
 
1829
    return Services.prefs.getBoolPref("formhelper.enabled");
 
1830
  },
 
1831
  
 
1832
  _visibleScreenArea: null,
 
1833
  get visibleScreenArea() {
 
1834
    let visibleRect = Rect.fromRect(this._currentBrowser.getBoundingClientRect());
 
1835
    let visibleScreenArea = visibleRect;
 
1836
    if (this._visibleScreenArea) {
 
1837
      visibleScreenArea = this._visibleScreenArea.clone();
 
1838
      visibleScreenArea.x = visibleRect.x;
 
1839
      visibleScreenArea.y = visibleRect.y;
 
1840
      visibleScreenArea.width = visibleRect.width;
 
1841
      visibleScreenArea.height = visibleRect.height - this._container.getBoundingClientRect().height;
 
1842
    }
 
1843
    return visibleScreenArea;
 
1844
  },
 
1845
 
 
1846
  init: function formHelperInit() {
 
1847
    this._container = document.getElementById("content-navigator");
 
1848
    this._autofillContainer = document.getElementById("form-helper-autofill");
 
1849
    this._cmdPrevious = document.getElementById(this.commands.previous);
 
1850
    this._cmdNext = document.getElementById(this.commands.next);
 
1851
    this._visibleScreenArea = new Rect(0, 0, 0, 0);
 
1852
 
 
1853
    // Listen for form assistant messages from content
 
1854
    messageManager.addMessageListener("FormAssist:Show", this);
 
1855
    messageManager.addMessageListener("FormAssist:Hide", this);
 
1856
    messageManager.addMessageListener("FormAssist:Update", this);
 
1857
    messageManager.addMessageListener("FormAssist:AutoComplete", this);
 
1858
 
 
1859
    // Listen for events where form assistant should be closed or updated
 
1860
    let tabs = document.getElementById("tabs");
 
1861
    tabs.addEventListener("TabSelect", this, true);
 
1862
    tabs.addEventListener("TabClose", this, true);
 
1863
    Elements.browsers.addEventListener("URLChanged", this, true);
 
1864
    window.addEventListener("resize", this, true);
 
1865
 
 
1866
    // Listen for modal dialog to show/hide the UI
 
1867
    messageManager.addMessageListener("DOMWillOpenModalDialog", this);
 
1868
    messageManager.addMessageListener("DOMModalDialogClosed", this);
 
1869
 
 
1870
    Services.obs.addObserver(this, "softkb-change", false);
 
1871
  },
 
1872
 
 
1873
  uninit: function formHelperUninit() {
 
1874
    Services.obs.removeObserver(this, "softkb-change");
 
1875
  },
 
1876
 
 
1877
  _currentBrowser: null,
 
1878
  show: function formHelperShow(aElement, aHasPrevious, aHasNext) {
 
1879
    this._currentBrowser = Browser.selectedBrowser;
 
1880
 
 
1881
    // Update the next/previous commands
 
1882
    this._cmdPrevious.setAttribute("disabled", !aHasPrevious);
 
1883
    this._cmdNext.setAttribute("disabled", !aHasNext);
 
1884
    this._open = true;
 
1885
 
 
1886
    let lastElement = this._currentElement || null;
 
1887
    this._currentElement = {
 
1888
      id: aElement.id,
 
1889
      name: aElement.name,
 
1890
      value: aElement.value,
 
1891
      maxLength: aElement.maxLength,
 
1892
      type: aElement.type,
 
1893
      isAutocomplete: aElement.isAutocomplete,
 
1894
      list: aElement.choices
 
1895
    }
 
1896
 
 
1897
    this._updateContainer(lastElement, this._currentElement);
 
1898
    this._zoom(Rect.fromRect(aElement.rect), Rect.fromRect(aElement.caretRect));
 
1899
 
 
1900
    // Prevent the view to scroll automatically while typing
 
1901
    this._currentBrowser.scrollSync = false;
 
1902
  },
 
1903
 
 
1904
  hide: function formHelperHide() {
 
1905
    if (!this._open)
 
1906
      return;
 
1907
 
 
1908
    // Restore the scroll synchonisation
 
1909
    this._currentBrowser.scrollSync = true;
 
1910
 
 
1911
    // reset current Element and Caret Rect
 
1912
    this._currentElementRect = null;
 
1913
    this._currentCaretRect = null;
 
1914
 
 
1915
    this._updateContainerForSelect(this._currentElement, null);
 
1916
 
 
1917
    this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Closed", { });
 
1918
    this._open = false;
 
1919
  },
 
1920
 
 
1921
  handleEvent: function formHelperHandleEvent(aEvent) {
 
1922
    if (!this._open)
 
1923
      return;
 
1924
 
 
1925
    switch (aEvent.type) {
 
1926
      case "TabSelect":
 
1927
      case "TabClose":
 
1928
        this.hide();
 
1929
        break;
 
1930
 
 
1931
      case "URLChanged":
 
1932
        if (aEvent.target == Browser.selectedBrowser)
 
1933
          this.hide();
 
1934
        break;
 
1935
 
 
1936
      case "resize":
 
1937
        setTimeout(function(self) {
 
1938
          SelectHelperUI.resize();
 
1939
          self._container.contentHasChanged();
 
1940
        }, 0, this);
 
1941
        break;
 
1942
    }
 
1943
  },
 
1944
 
 
1945
  receiveMessage: function formHelperReceiveMessage(aMessage) {
 
1946
    let json = aMessage.json;
 
1947
    switch (aMessage.name) {
 
1948
      case "FormAssist:Show":
 
1949
        // if the user has manually disabled the Form Assistant UI we still
 
1950
        // want to show a UI for <select /> element but not managed by
 
1951
        // FormHelperUI
 
1952
        this.enabled ? this.show(json.current, json.hasPrevious, json.hasNext)
 
1953
                     : SelectHelperUI.show(json.current.choices);
 
1954
        break;
 
1955
 
 
1956
      case "FormAssist:Hide":
 
1957
        this.hide();
 
1958
        break;
 
1959
 
 
1960
      case "FormAssist:AutoComplete":
 
1961
        this._updateAutocompleteFor(json.current);
 
1962
        this._container.contentHasChanged();
 
1963
        break;
 
1964
 
 
1965
       case "FormAssist:Update":
 
1966
        Browser.hideSidebars();
 
1967
        Browser.hideTitlebar();
 
1968
        this._zoom(null, Rect.fromRect(json.caretRect));
 
1969
        break;
 
1970
 
 
1971
      case "DOMWillOpenModalDialog":
 
1972
        if (this._open && aMessage.target == Browser.selectedBrowser) {
 
1973
          this._container.style.display = "none";
 
1974
          this._container._spacer.hidden = true;
 
1975
        }
 
1976
        break;
 
1977
 
 
1978
      case "DOMModalDialogClosed":
 
1979
        if (this._open && aMessage.target == Browser.selectedBrowser) {
 
1980
          this._container.style.display = "-moz-box";
 
1981
          this._container._spacer.hidden = false;
 
1982
        }
 
1983
        break;
 
1984
    }
 
1985
  },
 
1986
 
 
1987
  // for VKB that does not resize the window
 
1988
  _currentCaretRect: null,
 
1989
  _currentElementRect: null,
 
1990
  observe: function formHelperObserve(aSubject, aTopic, aData) {
 
1991
    let rect = Rect.fromRect(JSON.parse(aData));
 
1992
    rect.height = rect.bottom - rect.top;
 
1993
    rect.width  = rect.right - rect.left;
 
1994
 
 
1995
    this._visibleScreenArea = rect;
 
1996
    BrowserUI.sizeControls(rect.width, rect.height);
 
1997
    if (this.open)
 
1998
      this._zoom(this._currentElementRect, this._currentCaretRect);
 
1999
  },
 
2000
 
 
2001
  goToPrevious: function formHelperGoToPrevious() {
 
2002
    this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Previous", { });
 
2003
  },
 
2004
 
 
2005
  goToNext: function formHelperGoToNext() {
 
2006
    this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:Next", { });
 
2007
  },
 
2008
 
 
2009
  doAutoComplete: function formHelperDoAutoComplete(aElement) {
 
2010
    // Suggestions are only in <label>s. Ignore the rest.
 
2011
    if (aElement instanceof Ci.nsIDOMXULLabelElement)
 
2012
      this._currentBrowser.messageManager.sendAsyncMessage("FormAssist:AutoComplete", { value: aElement.value });
 
2013
  },
 
2014
 
 
2015
  get _open() {
 
2016
    return (this._container.getAttribute("type") == this.type);
 
2017
  },
 
2018
 
 
2019
  set _open(aVal) {
 
2020
    if (aVal == this._open)
 
2021
      return;
 
2022
 
 
2023
    this._container.hidden = !aVal;
 
2024
    this._container.contentHasChanged();
 
2025
 
 
2026
    if (aVal) {
 
2027
      this._zoomStart();
 
2028
      this._container.show(this);
1340
2029
    } else {
1341
 
      this._autofillContainer.collapsed = true;
1342
 
    }
1343
 
 
1344
 
    let height = Math.floor(this._container.getBoundingClientRect().height);
1345
 
    this._container.top = window.innerHeight - height;
1346
 
 
1347
 
    document.getElementById("form-helper-previous").disabled = !this._getPrevious();
1348
 
    document.getElementById("form-helper-next").disabled = !this._getNext();
1349
 
  },
1350
 
 
1351
 
  _updateSelect: function(aPreviousElement, aNewElement) {
1352
 
    let previousIsSelect = this._isValidSelectElement(aPreviousElement);
1353
 
    let currentIsSelect = this._isValidSelectElement(aNewElement);
1354
 
 
1355
 
    if (currentIsSelect && !previousIsSelect) {
1356
 
      this._selectContainer.style.maxHeight = (window.innerHeight / 1.8) + "px";
1357
 
 
1358
 
      let rootNode = this._container;
1359
 
      rootNode.insertBefore(this._selectContainer, rootNode.lastChild);
1360
 
 
1361
 
      SelectHelper.show(aNewElement);
1362
 
    }
1363
 
    else if (currentIsSelect && previousIsSelect) {
1364
 
      SelectHelper.reset();
1365
 
      SelectHelper.show(aNewElement);
1366
 
    }
1367
 
    else if (!currentIsSelect && previousIsSelect) {
1368
 
      let rootNode = this._container.parentNode;
1369
 
      rootNode.insertBefore(this._selectContainer, rootNode.lastChild);
1370
 
 
1371
 
      SelectHelper.close();
1372
 
    }
1373
 
  },
1374
 
 
1375
 
  _isValidElement: function(aElement) {
1376
 
    if (aElement.disabled)
1377
 
      return false;
1378
 
 
1379
 
    if (aElement.getAttribute("role") == "button" && aElement.hasAttribute("tabindex"))
1380
 
      return this._isElementVisible(aElement);
1381
 
 
1382
 
    if (this._isValidSelectElement(aElement) || aElement instanceof HTMLTextAreaElement)
1383
 
      return this._isElementVisible(aElement);
1384
 
 
1385
 
    if (aElement instanceof HTMLInputElement || aElement instanceof HTMLButtonElement) {
1386
 
      if (aElement.type == "hidden")
1387
 
        return false;
1388
 
      return this._isElementVisible(aElement);
1389
 
    }
1390
 
 
1391
 
    return false;
1392
 
  },
1393
 
 
1394
 
  _isValidSelectElement: function(aElement) {
1395
 
    return SelectHelper.canShowUIFor(aElement);
1396
 
  },
1397
 
 
1398
 
  _isElementVisible: function(aElement) {
1399
 
    let style = aElement.ownerDocument.defaultView.getComputedStyle(aElement, null);
1400
 
    if (!style)
1401
 
      return false;
1402
 
 
1403
 
    let isVisible = (style.getPropertyValue("visibility") != "hidden");
1404
 
    let isOpaque = (style.getPropertyValue("opacity") != 0);
1405
 
 
1406
 
    let rect = aElement.getBoundingClientRect();
1407
 
    return isVisible && isOpaque && (rect.height != 0 || rect.width != 0);
1408
 
  },
1409
 
 
1410
 
  _getAll: function() {
1411
 
    let elements = [];
1412
 
 
1413
 
    // get all the documents
1414
 
    let documents = [getBrowser().contentDocument];
1415
 
    let iframes = getBrowser().contentDocument.querySelectorAll("iframe, frame");
1416
 
    for (let i = 0; i < iframes.length; i++)
1417
 
      documents.push(iframes[i].contentDocument);
1418
 
 
1419
 
    for (let i = 0; i < documents.length; i++) {
1420
 
      let nodes = documents[i].querySelectorAll("input, button, select, textarea, [role=button]");
1421
 
      nodes = this._filterRadioButtons(nodes).filter(this._isValidElement, this);
1422
 
      elements = elements.concat(nodes);
1423
 
    }
1424
 
 
1425
 
    function orderByTabIndex(a, b) {
1426
 
      // for an explanation on tabbing navigation see
1427
 
      // http://www.w3.org/TR/html401/interact/forms.html#h-17.11.1
1428
 
      // In resume tab index navigation order is 1, 2, 3, ..., 32767, 0
1429
 
      if (a.tabIndex == 0 || b.tabIndex == 0)
1430
 
        return b.tabIndex;
1431
 
 
1432
 
      return a.tabIndex > b.tabIndex;
1433
 
    }
1434
 
    return elements.sort(orderByTabIndex);
1435
 
  },
1436
 
 
1437
 
  /**
1438
 
   * For each radio button group, remove all but the checked button
1439
 
   * if there is one, or the first button otherwise.
1440
 
   */
1441
 
  _filterRadioButtons: function(nodes) {
1442
 
    // First pass: Find the checked or first element in each group.
1443
 
    let chosenRadios = {};
1444
 
    for (let i=0; i < nodes.length; i++) {
1445
 
      let node = nodes[i];
1446
 
      if (node.type == "radio" && (!chosenRadios.hasOwnProperty(node.name) || node.checked))
1447
 
        chosenRadios[node.name] = node;
1448
 
    }
1449
 
 
1450
 
    // Second pass: Exclude all other radio buttons from the list.
1451
 
    var result = [];
1452
 
    for (let i=0; i < nodes.length; i++) {
1453
 
      let node = nodes[i];
1454
 
      if (node.type == "radio" && chosenRadios[node.name] != node)
1455
 
        continue;
1456
 
      result.push(node);
1457
 
    }
1458
 
    return result;
1459
 
  },
1460
 
 
1461
 
  _getPrevious: function() {
1462
 
    let index = this._nodes.indexOf(this._currentElement);
1463
 
    let node = (index != -1 ? this._nodes[--index] : null);
1464
 
    while (node && !this._isElementVisible(node))
1465
 
      node = this._nodes[--index];
1466
 
    return node;
1467
 
  },
1468
 
 
1469
 
  _getNext: function() {
1470
 
    let index = this._nodes.indexOf(this._currentElement);
1471
 
    let node = (index != -1 ? this._nodes[++index] : null);
1472
 
    while (node && !this._isElementVisible(node))
1473
 
      node = this._nodes[++index];
1474
 
    return node;
1475
 
  },
1476
 
 
1477
 
  _fac: Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete),
1478
 
  _getSuggestions: function() {
1479
 
    let suggestions = [];
1480
 
    let currentValue = this._currentElement.value;
1481
 
    let results = this._fac.autoCompleteSearch(this._currentElement.name, currentValue, this._currentElement, null);
1482
 
    if (results.matchCount > 0) {
1483
 
      for (let i = 0; i < results.matchCount; i++) {
1484
 
        let value = results.getValueAt(i);
1485
 
        suggestions.push(value);
1486
 
      }
1487
 
    }
1488
 
 
1489
 
    return suggestions;
1490
 
  },
1491
 
 
1492
 
  _setSuggestions: function(aSuggestions) {
 
2030
      this._zoomFinish();
 
2031
      this._currentElement = null;
 
2032
      this._container.hide(this);
 
2033
    }
 
2034
 
 
2035
    let evt = document.createEvent("UIEvents");
 
2036
    evt.initUIEvent("FormUI", true, true, window, aVal);
 
2037
    this._container.dispatchEvent(evt);
 
2038
  },
 
2039
 
 
2040
  _updateAutocompleteFor: function _formHelperUpdateAutocompleteFor(aElement) {
 
2041
    let suggestions = this._getAutocompleteSuggestions(aElement);
 
2042
    this._displaySuggestions(suggestions);
 
2043
  },
 
2044
 
 
2045
  _displaySuggestions: function _formHelperDisplaySuggestions(aSuggestions) {
1493
2046
    let autofill = this._autofillContainer;
1494
2047
    while (autofill.hasChildNodes())
1495
2048
      autofill.removeChild(autofill.lastChild);
1499
2052
      let value = aSuggestions[i];
1500
2053
      let button = document.createElement("label");
1501
2054
      button.setAttribute("value", value);
 
2055
      button.className = "form-helper-autofill-label";
1502
2056
      fragment.appendChild(button);
1503
2057
    }
1504
2058
    autofill.appendChild(fragment);
1505
2059
    autofill.collapsed = !aSuggestions.length;
1506
2060
  },
1507
2061
 
1508
 
  doAutoFill: function formHelperDoAutoFill(aElement) {
1509
 
    if (!this._currentElement)
1510
 
     return;
1511
 
 
1512
 
    // Suggestions are only in <label>s. Ignore the rest.
1513
 
    if (aElement instanceof Ci.nsIDOMXULLabelElement)
1514
 
      this._currentElement.value = aElement.value;
1515
 
  },
1516
 
 
1517
 
  getLabelsFor: function(aElement) {
1518
 
    let associatedLabels = [];
1519
 
    if (this._isValidElement(aElement)) {
1520
 
      let labels = aElement.ownerDocument.getElementsByTagName("label");
1521
 
      for (let i=0; i<labels.length; i++) {
1522
 
        if (labels[i].getAttribute("for") == aElement.id)
1523
 
          associatedLabels.push(labels[i]);
1524
 
      }
1525
 
    }
1526
 
 
1527
 
    if (aElement.parentNode instanceof HTMLLabelElement)
1528
 
      associatedLabels.push(aElement.parentNode);
1529
 
 
1530
 
    return associatedLabels.filter(this._isElementVisible);
1531
 
  },
1532
 
 
1533
 
  _currentElement: null,
1534
 
  getCurrentElement: function() {
1535
 
    return this._currentElement;
1536
 
  },
1537
 
 
1538
 
  setCurrentElement: function(aElement) {
1539
 
    if (!aElement)
1540
 
      return;
1541
 
 
1542
 
    let previousElement = this._currentElement;
1543
 
    this._currentElement = aElement;
1544
 
    this._utils = aElement.ownerDocument.defaultView
1545
 
                                        .QueryInterface(Ci.nsIInterfaceRequestor)
1546
 
                                        .getInterface(Ci.nsIDOMWindowUtils);
1547
 
    try {
1548
 
      this._utils.QueryInterface(Ci.nsIDOMWindowUtils_1_9_2);
1549
 
    }
1550
 
    catch(e) {}
1551
 
 
1552
 
    this._update(previousElement, aElement);
1553
 
 
1554
 
    let containerHeight = this._container.getBoundingClientRect().height;
1555
 
    this._helperSpacer.setAttribute("height", containerHeight);
1556
 
 
1557
 
    this.zoom(aElement);
1558
 
    gFocusManager.setFocus(aElement, Ci.nsIFocusManager.FLAG_NOSCROLL);
1559
 
  },
1560
 
 
1561
 
  goToPrevious: function formHelperGoToPrevious() {
1562
 
    let previous = this._getPrevious();
1563
 
    this.setCurrentElement(previous);
1564
 
  },
1565
 
 
1566
 
  goToNext: function formHelperGoToNext() {
1567
 
    let next = this._getNext();
1568
 
    this.setCurrentElement(next);
1569
 
  },
1570
 
 
1571
 
  open: function formHelperOpen(aElement) {
1572
 
    if (this._open == true && aElement == this._currentElement &&
1573
 
        gFocusManager.focusedElement == this._currentElement)
1574
 
      return false;
1575
 
 
1576
 
    this._open = true;
1577
 
    this._restore = { zoom: Browser._browserView.getZoomLevel(),
1578
 
                      scroll: Browser.getScrollboxPosition(Browser.contentScrollboxScroller)
1579
 
                    };
1580
 
 
1581
 
    window.addEventListener("keyup", this, false);
1582
 
    let bv = Browser._browserView;
1583
 
    bv.ignorePageScroll(true);
1584
 
 
1585
 
    this._container.hidden = false;
1586
 
    this._helperSpacer.hidden = false;
1587
 
 
1588
 
    this._nodes = this._getAll();
1589
 
    this.setCurrentElement(aElement);
1590
 
 
1591
 
    let evt = document.createEvent("UIEvents");
1592
 
    evt.initUIEvent("FormUI", true, true, window, this._open);
1593
 
    this._container.dispatchEvent(evt);
1594
 
 
1595
 
    return true;
1596
 
  },
1597
 
 
1598
 
  close: function formHelperHide() {
1599
 
    if (!this._open)
1600
 
      return;
1601
 
 
1602
 
    this._updateSelect(this._currentElement, null);
1603
 
 
1604
 
    this._helperSpacer.hidden = true;
1605
 
 
1606
 
    // give the form spacer area back to the content
1607
 
    let bv = Browser._browserView;
1608
 
    Browser.forceChromeReflow();
1609
 
    Browser.contentScrollboxScroller.scrollBy(0, 0);
1610
 
    bv.onAfterVisibleMove();
1611
 
 
1612
 
    bv.ignorePageScroll(false);
1613
 
 
1614
 
    window.removeEventListener("keyup", this, false);
1615
 
    this._container.hidden = true;
1616
 
    this._currentElement = null;
1617
 
    this._utils = null;
1618
 
 
1619
 
    if (gPrefService.getBoolPref("formhelper.restore") && this._restore) {
1620
 
      bv.setZoomLevel(this._restore.zoom);
1621
 
      Browser.contentScrollboxScroller.scrollTo(this._restore.scroll.x, this._restore.scroll.y);
1622
 
      bv.onAfterVisibleMove();
1623
 
      this._restore = null;
1624
 
    }
1625
 
 
1626
 
    this._open = false;
1627
 
 
1628
 
    let evt = document.createEvent("UIEvents");
1629
 
    evt.initUIEvent("FormUI", true, true, window, this._open);
1630
 
    this._container.dispatchEvent(evt);
1631
 
  },
1632
 
 
1633
 
  handleEvent: function formHelperHandleEvent(aEvent) {
1634
 
    let isChromeFocused = gFocusManager.getFocusedElementForWindow(window, false, {}) == gFocusManager.focusedElement;
1635
 
    if (isChromeFocused)
1636
 
      return;
1637
 
 
1638
 
    let currentElement = this.getCurrentElement();
1639
 
    switch (aEvent.keyCode) {
1640
 
      case aEvent.DOM_VK_DOWN:
1641
 
        if (currentElement instanceof HTMLTextAreaElement) {
1642
 
          let existSelection = currentElement.selectionEnd - currentElement.selectionStart;
1643
 
          let isEnd = (currentElement.textLength == currentElement.selectionEnd);
1644
 
          if (!isEnd || existSelection)
1645
 
            return;
1646
 
        }
1647
 
 
1648
 
        this.goToNext();
1649
 
        break;
1650
 
 
1651
 
      case aEvent.DOM_VK_UP:
1652
 
        if (currentElement instanceof HTMLTextAreaElement) {
1653
 
          let existSelection = currentElement.selectionEnd - currentElement.selectionStart;
1654
 
          let isStart = (currentElement.selectionEnd == 0);
1655
 
          if (!isStart || existSelection)
1656
 
            return;
1657
 
        }
1658
 
 
1659
 
        this.goToPrevious();
1660
 
        break;
1661
 
 
1662
 
      case aEvent.DOM_VK_RETURN:
1663
 
        break;
1664
 
 
1665
 
      default:
1666
 
        let caret = this._getRectForCaret();
1667
 
        if (caret) {
1668
 
          let bv = Browser._browserView;
1669
 
 
1670
 
          // If the caret is not into view we need to scroll to it
1671
 
          let visible = bv.getVisibleRect();
1672
 
          caret = bv.browserToViewportRect(caret);
1673
 
    
1674
 
          let [deltaX, deltaY] = this._getOffsetForCaret(caret, visible);
1675
 
    
1676
 
          // Scroll by the delta if we need to
1677
 
          if (deltaX != 0 || deltaY != 0) {
1678
 
            Browser.contentScrollboxScroller.scrollBy(deltaX, deltaY);
1679
 
            bv.onAfterVisibleMove();
1680
 
          }
1681
 
        }
1682
 
 
1683
 
        let target = aEvent.target;
1684
 
        if (currentElement instanceof HTMLInputElement && currentElement.type == "text") {
1685
 
          let suggestions = this._getSuggestions();
1686
 
          this._setSuggestions(suggestions);
1687
 
 
1688
 
          let height = Math.floor(this._container.getBoundingClientRect().height);
1689
 
          this._container.top = window.innerHeight - height;
1690
 
          this._helperSpacer.setAttribute("height", height);
1691
 
 
1692
 
          // XXX if we are at the bottom of the page we need to give back the content
1693
 
          // area by refreshing it
1694
 
          if (suggestions.length == 0) {
1695
 
            let bv = Browser._browserView;
1696
 
            Browser.forceChromeReflow();
1697
 
            Browser.contentScrollboxScroller.scrollBy(0, 0);
1698
 
            bv.onAfterVisibleMove();
1699
 
          }
1700
 
        } else if (currentElement == target && this._isValidSelectElement(target)) {
1701
 
          SelectHelper.unselectAll();
1702
 
          SelectHelper.selectByIndex(target.selectedIndex);
1703
 
        }
1704
 
        break;
1705
 
    }
1706
 
  },
1707
 
 
1708
 
  zoom: function formHelperZoom(aElement) {
1709
 
    let bv = Browser._browserView;
1710
 
    if (!bv.allowZoom)
1711
 
      return;
1712
 
 
1713
 
    let zoomLevel = bv.getZoomLevel();
1714
 
    if (gPrefService.getBoolPref("formhelper.autozoom")) {
1715
 
      zoomLevel = Browser._getZoomLevelForElement(aElement);
1716
 
      zoomLevel = Math.min(Math.max(kBrowserFormZoomLevelMin, zoomLevel), kBrowserFormZoomLevelMax);
1717
 
    }
1718
 
 
1719
 
    let elRect = this._getRectForElement(aElement);
1720
 
    let zoomRect = Browser._getZoomRectForPoint(elRect.center().x, elRect.y, zoomLevel);
1721
 
 
1722
 
    let caretRect = this._getRectForCaret();
1723
 
    if (caretRect) {
1724
 
      caretRect = Browser._browserView.browserToViewportRect(caretRect);
1725
 
      if (!zoomRect.contains(caretRect)) {
1726
 
        let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
1727
 
        zoomRect.translate(deltaX, deltaY);
1728
 
      }
1729
 
    }
1730
 
 
1731
 
    Browser.setVisibleRect(zoomRect);
1732
 
  },
1733
 
  
1734
 
  _getRectForCaret: function formHelper_getRectForCaret() {
1735
 
    let currentElement = this.getCurrentElement();
1736
 
    if ((currentElement instanceof HTMLTextAreaElement ||
1737
 
        (currentElement instanceof HTMLInputElement && currentElement.type == "text")) &&
1738
 
        gFocusManager.focusedElement == this._currentElement) {
1739
 
 
1740
 
      let rect = this._utils.sendQueryContentEvent(this._utils.QUERY_CARET_RECT, currentElement.selectionEnd, 0, 0, 0);
1741
 
      if (!rect)
1742
 
        return null;
1743
 
 
1744
 
      let bv = Browser._browserView;
1745
 
      let scroll = BrowserView.Util.getContentScrollOffset(Browser.selectedBrowser);
1746
 
      let caret = new Rect(scroll.x + rect.left, scroll.y + rect.top, rect.width, rect.height);
1747
 
      return caret;
1748
 
    }
1749
 
    
1750
 
    return null;
1751
 
  },
1752
 
  
1753
 
  _getOffsetForCaret: function formHelper_getOffsetForCaret(aCaretRect, aRect) {
 
2062
  /** Retrieve the autocomplete list from the autocomplete service for an element */
 
2063
  _getAutocompleteSuggestions: function _formHelperGetAutocompleteSuggestions(aElement) {
 
2064
    if (!aElement.isAutocomplete)
 
2065
      return [];
 
2066
 
 
2067
    let suggestions = [];
 
2068
 
 
2069
    let autocompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"].getService(Ci.nsIFormAutoComplete);
 
2070
    let results = autocompleteService.autoCompleteSearch(aElement.name, aElement.value, aElement, null);
 
2071
    if (results.matchCount > 0) {
 
2072
      for (let i = 0; i < results.matchCount; i++) {
 
2073
        let value = results.getValueAt(i);
 
2074
        suggestions.push(value);
 
2075
      }
 
2076
    }
 
2077
 
 
2078
    return suggestions;
 
2079
  },
 
2080
 
 
2081
  /** Update the form helper container to reflect new element user is editing. */
 
2082
  _updateContainer: function _formHelperUpdateContainer(aLastElement, aCurrentElement) {
 
2083
    this._updateContainerForSelect(aLastElement, aCurrentElement);
 
2084
 
 
2085
    // Setup autofill UI
 
2086
    this._updateAutocompleteFor(aCurrentElement);
 
2087
    this._container.contentHasChanged();
 
2088
  },
 
2089
 
 
2090
  /** Helper for _updateContainer that handles the case where the new element is a select. */
 
2091
  _updateContainerForSelect: function _formHelperUpdateContainerForSelect(aLastElement, aCurrentElement) {
 
2092
    let lastHasChoices = aLastElement && (aLastElement.list != null);
 
2093
    let currentHasChoices = aCurrentElement && (aCurrentElement.list != null);
 
2094
 
 
2095
    if (!lastHasChoices && currentHasChoices) {
 
2096
      SelectHelperUI.dock(this._container);
 
2097
      SelectHelperUI.show(aCurrentElement.list);
 
2098
    } else if (lastHasChoices && currentHasChoices) {
 
2099
      SelectHelperUI.reset();
 
2100
      SelectHelperUI.show(aCurrentElement.list);
 
2101
    } else if (lastHasChoices && !currentHasChoices) {
 
2102
      SelectHelperUI.hide();
 
2103
    }
 
2104
  },
 
2105
 
 
2106
  /** Zoom and move viewport so that element is legible and touchable. */
 
2107
  _zoom: function _formHelperZoom(aElementRect, aCaretRect) {
 
2108
    let zoomRect = this.visibleScreenArea;
 
2109
    let browser = getBrowser();
 
2110
 
 
2111
    // Zoom to a specified Rect
 
2112
    if (aElementRect && Browser.selectedTab.allowZoom && Services.prefs.getBoolPref("formhelper.autozoom")) {
 
2113
      this._currentElementRect = aElementRect;
 
2114
      // Zoom to an element by keeping the caret into view
 
2115
      let zoomLevel = Browser.selectedTab.clampZoomLevel(this._getZoomLevelForRect(aElementRect));
 
2116
 
 
2117
      zoomRect = this._getZoomRectForPoint(aElementRect.center().x, aElementRect.y, zoomLevel);
 
2118
      Browser.animatedZoomTo(zoomRect);
 
2119
    }
 
2120
 
 
2121
    this._ensureCaretVisible(aCaretRect);
 
2122
  },
 
2123
 
 
2124
  _ensureCaretVisible: function _ensureCaretVisible(aCaretRect) {
 
2125
    if (!aCaretRect)
 
2126
      return;
 
2127
 
 
2128
    // the scrollX/scrollY position can change because of the animated zoom so
 
2129
    // delay the caret adjustment
 
2130
    if (AnimatedZoom.isZooming()) {
 
2131
      let self = this;
 
2132
      window.addEventListener("AnimatedZoomEnd", function() {
 
2133
        window.removeEventListener("AnimatedZoomEnd", arguments.callee, true);
 
2134
          self._ensureCaretVisible(aCaretRect);
 
2135
      }, true);
 
2136
      return;
 
2137
    }
 
2138
 
 
2139
    let browser = getBrowser();
 
2140
    let zoomRect = this.visibleScreenArea;
 
2141
 
 
2142
    this._currentCaretRect = aCaretRect;
 
2143
    let caretRect = aCaretRect.scale(browser.scale, browser.scale);
 
2144
 
 
2145
    let scroll = browser.getPosition();
 
2146
    zoomRect = new Rect(scroll.x, scroll.y, zoomRect.width, zoomRect.height);
 
2147
    if (zoomRect.contains(caretRect))
 
2148
      return;
 
2149
 
 
2150
    let [deltaX, deltaY] = this._getOffsetForCaret(caretRect, zoomRect);
 
2151
    if (deltaX != 0 || deltaY != 0)
 
2152
      browser.scrollBy(deltaX, deltaY);
 
2153
  },
 
2154
 
 
2155
  /* Store the current zoom level, and scroll positions to restore them if needed */
 
2156
  _zoomStart: function _formHelperZoomStart() {
 
2157
    if (!Services.prefs.getBoolPref("formhelper.restore"))
 
2158
      return;
 
2159
 
 
2160
    this._restore = {
 
2161
      scale: getBrowser().scale,
 
2162
      contentScrollOffset: Browser.getScrollboxPosition(Browser.contentScrollboxScroller),
 
2163
      pageScrollOffset: Browser.getScrollboxPosition(Browser.pageScrollboxScroller)
 
2164
    };
 
2165
  },
 
2166
 
 
2167
  /** Element is no longer selected. Restore zoom level if setting is enabled. */
 
2168
  _zoomFinish: function _formHelperZoomFinish() {
 
2169
    if(!Services.prefs.getBoolPref("formhelper.restore"))
 
2170
      return;
 
2171
 
 
2172
    let restore = this._restore;
 
2173
    getBrowser().scale = restore.scale;
 
2174
    Browser.contentScrollboxScroller.scrollTo(restore.contentScrollOffset.x, restore.contentScrollOffset.y);
 
2175
    Browser.pageScrollboxScroller.scrollTo(restore.pageScrollOffset.x, restore.pageScrollOffset.y);
 
2176
  },
 
2177
 
 
2178
  _getZoomRectForPoint: function _getZoomRectForPoint(x, y, zoomLevel) {
 
2179
    let browser = getBrowser();
 
2180
    x = x * browser.scale;
 
2181
    y = y * browser.scale;
 
2182
 
 
2183
    let vis = this.visibleScreenArea
 
2184
    zoomLevel = Math.min(ZoomManager.MAX, zoomLevel);
 
2185
    let oldScale = browser.scale;
 
2186
    let zoomRatio = zoomLevel / oldScale;
 
2187
    let newVisW = vis.width / zoomRatio, newVisH = vis.height / zoomRatio;
 
2188
    let result = new Rect(x - newVisW / 2, y - newVisH / 2, newVisW, newVisH);
 
2189
 
 
2190
    // Make sure rectangle doesn't poke out of viewport
 
2191
    return result.translateInside(new Rect(0, 0, browser.contentDocumentWidth * oldScale,
 
2192
                                                 browser.contentDocumentHeight * oldScale));
 
2193
  },
 
2194
 
 
2195
  _getZoomLevelForRect: function _getZoomLevelForRect(aRect) {
 
2196
    const margin = 30;
 
2197
    let zoomLevel = this.visibleScreenArea.width / (aRect.width + margin);
 
2198
    return Util.clamp(zoomLevel, kBrowserFormZoomLevelMin, kBrowserFormZoomLevelMax);
 
2199
  },
 
2200
 
 
2201
  _getOffsetForCaret: function _formHelperGetOffsetForCaret(aCaretRect, aRect) {
1754
2202
    // Determine if we need to move left or right to bring the caret into view
1755
2203
    let deltaX = 0;
1756
2204
    if (aCaretRect.right > aRect.right)
1757
2205
      deltaX = aCaretRect.right - aRect.right;
1758
2206
    if (aCaretRect.left < aRect.left)
1759
2207
      deltaX = aCaretRect.left - aRect.left;
1760
 
      
 
2208
 
1761
2209
    // Determine if we need to move up or down to bring the caret into view
1762
2210
    let deltaY = 0;
1763
2211
    if (aCaretRect.bottom > aRect.bottom)
1766
2214
      deltaY = aCaretRect.top - aRect.top;
1767
2215
 
1768
2216
    return [deltaX, deltaY];
1769
 
  },
1770
 
 
1771
 
  canShowUIFor: function(aElement) {
1772
 
    if (!aElement)
1773
 
      return false;
1774
 
 
1775
 
    // Some forms elements are valid in the sense that we want the Form
1776
 
    // Assistant to stop on it, but we don't want it to display when
1777
 
    // the user clicks on it
1778
 
    let formExceptions = {button: true, checkbox: true, file: true, image: true, radio: true, reset: true, submit: true};
1779
 
    if (aElement instanceof HTMLInputElement && formExceptions[aElement.type])
1780
 
      return false;
1781
 
 
1782
 
    if (aElement instanceof HTMLButtonElement || (aElement.getAttribute("role") == "button" && aElement.hasAttribute("tabindex")))
1783
 
      return false;
1784
 
 
1785
 
    return this._isValidElement(aElement);
1786
 
  }
1787
 
};
1788
 
 
1789
 
function SelectWrapper(aControl) {
1790
 
  this._control = aControl;
1791
 
}
1792
 
 
1793
 
SelectWrapper.prototype = {
1794
 
  get selectedIndex() { return this._control.selectedIndex; },
1795
 
  get multiple() { return this._control.multiple; },
1796
 
  get options() { return this._control.options; },
1797
 
  get children() { return this._control.children; },
1798
 
 
1799
 
  getText: function(aChild) { return aChild.text; },
1800
 
  isOption: function(aChild) { return aChild instanceof HTMLOptionElement; },
1801
 
  isGroup: function(aChild) { return aChild instanceof HTMLOptGroupElement; },
1802
 
  select: function(aIndex, aSelected, aClearAll) {
1803
 
    let selectElement = this._control.QueryInterface(Ci.nsISelectElement);
1804
 
    selectElement.setOptionsSelectedByIndex(aIndex, aIndex, aSelected, aClearAll, false, true);
1805
 
  },
1806
 
  focus: function() { this._control.focus(); },
1807
 
  fireOnChange: function() {
1808
 
    let control = this._control;
1809
 
    let evt = document.createEvent("Events");
1810
 
    evt.initEvent("change", true, true, window, 0,
1811
 
                  false, false,
1812
 
                  false, false, null);
1813
 
    setTimeout(function() {
1814
 
      control.dispatchEvent(evt)
1815
 
    }, 0);
1816
 
  }
1817
 
};
1818
 
 
1819
 
function MenulistWrapper(aControl) {
1820
 
  this._control = aControl;
1821
 
}
1822
 
 
1823
 
// Use wrappedJSObject when control is in content for extra protection
1824
 
// See bug 559792
1825
 
 
1826
 
MenulistWrapper.prototype = {
1827
 
  get selectedIndex() {
1828
 
    let control = this._control.wrappedJSObject || this._control;
1829
 
    let result = control.selectedIndex;
1830
 
    return (typeof result == "number" && !isNaN(result) ? result : -1);
1831
 
  },
1832
 
  get multiple() { return false; },
1833
 
  get options() {
1834
 
    let control = this._control.wrappedJSObject || this._control;
1835
 
    return control.menupopup.children;
1836
 
  },
1837
 
  get children() {
1838
 
    let control = this._control.wrappedJSObject || this._control;
1839
 
    return control.menupopup.children;
1840
 
  },
1841
 
 
1842
 
  getText: function(aChild) { return aChild.label; },
1843
 
  isOption: function(aChild) { return aChild instanceof Ci.nsIDOMXULSelectControlItemElement; },
1844
 
  isGroup: function(aChild) { return false },
1845
 
  select: function(aIndex, aSelected, aClearAll) {
1846
 
    let control = this._control.wrappedJSObject || this._control;
1847
 
    control.selectedIndex = aIndex;
1848
 
  },
1849
 
  focus: function() { this._control.focus(); },
1850
 
  fireOnChange: function() {
1851
 
    let control = this._control;
1852
 
    let evt = document.createEvent("XULCommandEvent");
1853
 
    evt.initCommandEvent("command", true, true, window, 0,
1854
 
                         false, false,
1855
 
                         false, false, null);
1856
 
    setTimeout(function() {
1857
 
      control.dispatchEvent(evt)
1858
 
    }, 0);
1859
 
  }
1860
 
};
1861
 
 
1862
 
var SelectHelper = {
1863
 
  _panel: null,
 
2217
  }
 
2218
};
 
2219
 
 
2220
/**
 
2221
 * SelectHelperUI: Provides an interface for making a choice in a list.
 
2222
 *   Supports simultaneous selection of choices and group headers.
 
2223
 */
 
2224
var SelectHelperUI = {
1864
2225
  _list: null,
1865
 
  _control: null,
1866
 
  _selectedIndexes: [],
 
2226
  _selectedIndexes: null,
 
2227
 
 
2228
  get _panel() {
 
2229
    delete this._panel;
 
2230
    return this._panel = document.getElementById("select-container");
 
2231
  },
 
2232
 
 
2233
  get _textbox() {
 
2234
    delete this._textbox;
 
2235
    return this._textbox = document.getElementById("select-helper-textbox");
 
2236
  },
 
2237
 
 
2238
  show: function(aList) {
 
2239
    this.showFilter = false;
 
2240
    this._textbox.blur();
 
2241
    this._list = aList;
 
2242
 
 
2243
    this._container = document.getElementById("select-list");
 
2244
    this._container.setAttribute("multiple", aList.multiple ? "true" : "false");
 
2245
 
 
2246
    this._selectedIndexes = this._getSelectedIndexes();
 
2247
    let firstSelected = null;
 
2248
 
 
2249
    let choices = aList.choices;
 
2250
    for (let i = 0; i < choices.length; i++) {
 
2251
      let choice = choices[i];
 
2252
      let item = document.createElement("option");
 
2253
      item.className = "chrome-select-option";
 
2254
      item.setAttribute("label", choice.text);
 
2255
      choice.disabled ? item.setAttribute("disabled", choice.disabled)
 
2256
                      : item.removeAttribute("disabled");
 
2257
      this._container.appendChild(item);
 
2258
 
 
2259
      if (choice.group) {
 
2260
        item.classList.add("optgroup");
 
2261
        continue;
 
2262
      }
 
2263
 
 
2264
      item.optionIndex = choice.optionIndex;
 
2265
      item.choiceIndex = i;
 
2266
 
 
2267
      if (choice.inGroup)
 
2268
        item.classList.add("in-optgroup");
 
2269
 
 
2270
      if (choice.selected) {
 
2271
        item.setAttribute("selected", "true");
 
2272
        firstSelected = firstSelected || item;
 
2273
      }
 
2274
    }
 
2275
 
 
2276
    this._panel.hidden = false;
 
2277
    this._panel.height = this._panel.getBoundingClientRect().height;
 
2278
 
 
2279
    if (!this._docked)
 
2280
      BrowserUI.pushPopup(this, this._panel);
 
2281
 
 
2282
    this._scrollElementIntoView(firstSelected);
 
2283
 
 
2284
    this._container.addEventListener("click", this, false);
 
2285
    this._panel.addEventListener("overflow", this, true);
 
2286
  },
 
2287
 
 
2288
  _showFilter: false,
 
2289
  get showFilter() {
 
2290
    return this._showFilter;
 
2291
  },
 
2292
 
 
2293
  set showFilter(val) {
 
2294
    this._showFilter = val;
 
2295
    if (!this._panel.hidden)
 
2296
      this._textbox.hidden = !val;
 
2297
  },
 
2298
 
 
2299
  dock: function dock(aContainer) {
 
2300
    aContainer.insertBefore(this._panel, aContainer.lastChild);
 
2301
    this.resize();
 
2302
    this._docked = true;
 
2303
  },
 
2304
 
 
2305
  undock: function undock() {
 
2306
    let rootNode = Elements.stack;
 
2307
    rootNode.insertBefore(this._panel, rootNode.lastChild);
 
2308
    this._panel.style.maxHeight = "";
 
2309
    this._docked = false;
 
2310
  },
 
2311
 
 
2312
  reset: function() {
 
2313
    this._updateControl();
 
2314
    let empty = this._container.cloneNode(false);
 
2315
    this._container.parentNode.replaceChild(empty, this._container);
 
2316
    this._container = empty;
 
2317
    this._list = null;
 
2318
    this._selectedIndexes = null;
 
2319
    this._panel.height = "";
 
2320
    this._textbox.value = "";
 
2321
  },
 
2322
 
 
2323
  resize: function resize() {
 
2324
    this._panel.style.maxHeight = (window.innerHeight / 1.8) + "px";
 
2325
  },
 
2326
 
 
2327
  hide: function() {
 
2328
    this.showFilter = false;
 
2329
    this._container.removeEventListener("click", this, false);
 
2330
    this._panel.removeEventListener("overflow", this, true);
 
2331
 
 
2332
    this._panel.hidden = true;
 
2333
 
 
2334
    if (this._docked)
 
2335
      this.undock();
 
2336
    else
 
2337
      BrowserUI.popPopup(this);
 
2338
 
 
2339
    this.reset();
 
2340
  },
 
2341
 
 
2342
  filter: function(aValue) {
 
2343
    let reg = new RegExp(aValue, "gi");
 
2344
    let options = this._container.childNodes;
 
2345
    for (let i = 0; i < options.length; i++) {
 
2346
      let option = options[i];
 
2347
      option.getAttribute("label").match(reg) ? option.removeAttribute("filtered")
 
2348
                                              : option.setAttribute("filtered", "true");
 
2349
    }
 
2350
  },
 
2351
 
 
2352
  unselectAll: function() {
 
2353
    if (!this._list)
 
2354
      return;
 
2355
 
 
2356
    let choices = this._list.choices;
 
2357
    this._forEachOption(function(aItem, aIndex) {
 
2358
      aItem.selected = false;
 
2359
      choices[aIndex].selected = false;
 
2360
    });
 
2361
  },
 
2362
 
 
2363
  selectByIndex: function(aIndex) {
 
2364
    if (!this._list)
 
2365
      return;
 
2366
 
 
2367
    let choices = this._list.choices;
 
2368
    for (let i = 0; i < this._container.childNodes.length; i++) {
 
2369
      let option = this._container.childNodes[i];
 
2370
      if (option.optionIndex == aIndex) {
 
2371
        option.selected = true;
 
2372
        this._choices[i].selected = true;
 
2373
        this._scrollElementIntoView(option);
 
2374
        break;
 
2375
      }
 
2376
    }
 
2377
  },
1867
2378
 
1868
2379
  _getSelectedIndexes: function() {
1869
2380
    let indexes = [];
1870
 
    let control = this._control;
1871
 
 
1872
 
    if (control.multiple) {
1873
 
      for (let i = 0; i < control.options.length; i++) {
1874
 
        if (control.options[i].selected)
1875
 
          indexes.push(i);
1876
 
      }
1877
 
    }
1878
 
    else {
1879
 
      indexes.push(control.selectedIndex);
1880
 
    }
1881
 
 
 
2381
    if (!this._list)
 
2382
      return indexes;
 
2383
 
 
2384
    let choices = this._list.choices;
 
2385
    let choiceLength = choices.length;
 
2386
    for (let i = 0; i < choiceLength; i++) {
 
2387
      let choice = choices[i];
 
2388
      if (choice.selected)
 
2389
        indexes.push(choice.optionIndex);
 
2390
    }
1882
2391
    return indexes;
1883
2392
  },
1884
2393
 
1885
 
  show: function(aControl) {
1886
 
    if (!aControl)
1887
 
      return;
1888
 
 
1889
 
    if (aControl instanceof HTMLSelectElement)
1890
 
      this._control = new SelectWrapper(aControl);
1891
 
    else if (aControl instanceof Ci.nsIDOMXULMenuListElement)
1892
 
      this._control = new MenulistWrapper(aControl);
1893
 
    else
1894
 
      throw "Unknown list element";
1895
 
 
1896
 
    this._selectedIndexes = this._getSelectedIndexes();
1897
 
 
1898
 
    this._list = document.getElementById("select-list");
1899
 
    this._list.setAttribute("multiple", this._control.multiple ? "true" : "false");
1900
 
 
1901
 
    let firstSelected = null;
1902
 
 
1903
 
    let optionIndex = 0;
1904
 
    let children = this._control.children;
1905
 
    for (let i=0; i<children.length; i++) {
1906
 
      let child = children[i];
1907
 
      if (this._control.isGroup(child)) {
1908
 
        let group = document.createElement("option");
1909
 
        group.setAttribute("label", child.label);
1910
 
        this._list.appendChild(group);
1911
 
        group.className = "optgroup";
1912
 
 
1913
 
        let subchildren = child.children;
1914
 
        for (let ii=0; ii<subchildren.length; ii++) {
1915
 
          let subchild = subchildren[ii];
1916
 
          let item = document.createElement("option");
1917
 
          item.setAttribute("label", this._control.getText(subchild));
1918
 
          this._list.appendChild(item);
1919
 
          item.className = "in-optgroup";
1920
 
          item.optionIndex = optionIndex++;
1921
 
          if (subchild.selected) {
1922
 
            item.setAttribute("selected", "true");
1923
 
            firstSelected = firstSelected ? firstSelected : item;
1924
 
          }
1925
 
        }
1926
 
      } else if (this._control.isOption(child)) {
1927
 
        let item = document.createElement("option");
1928
 
        item.setAttribute("label", this._control.getText(child));
1929
 
        this._list.appendChild(item);
1930
 
        item.optionIndex = optionIndex++;
1931
 
        if (child.selected) {
1932
 
          item.setAttribute("selected", "true");
1933
 
          firstSelected = firstSelected ? firstSelected : item;
1934
 
        }
1935
 
      }
1936
 
    }
1937
 
 
1938
 
    this._panel = document.getElementById("select-container");
1939
 
    this._panel.hidden = false;
1940
 
 
1941
 
    this._scrollElementIntoView(firstSelected);
1942
 
 
1943
 
    this._list.addEventListener("click", this, false);
1944
 
  },
1945
 
 
1946
2394
  _scrollElementIntoView: function(aElement) {
1947
2395
    if (!aElement)
1948
2396
      return;
1958
2406
    if (index == -1)
1959
2407
      return;
1960
2408
 
1961
 
    let scrollBoxObject = this._list.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
 
2409
    let scrollBoxObject = this._container.boxObject.QueryInterface(Ci.nsIScrollBoxObject);
1962
2410
    let itemHeight = aElement.getBoundingClientRect().height;
1963
 
    let visibleItemsCount = this._list.boxObject.height / itemHeight;
 
2411
    let visibleItemsCount = this._container.boxObject.height / itemHeight;
1964
2412
    if ((index + 1) > visibleItemsCount) {
1965
2413
      let delta = Math.ceil(visibleItemsCount / 2);
1966
2414
      scrollBoxObject.scrollTo(0, ((index + 1) - delta) * itemHeight);
1971
2419
  },
1972
2420
 
1973
2421
  _forEachOption: function(aCallback) {
1974
 
      let children = this._list.children;
1975
 
      for (let i = 0; i < children.length; i++) {
1976
 
        let item = children[i];
1977
 
        if (!item.hasOwnProperty("optionIndex"))
1978
 
          continue;
1979
 
        aCallback(item, i);
1980
 
      }
 
2422
    let children = this._container.children;
 
2423
    for (let i = 0; i < children.length; i++) {
 
2424
      let item = children[i];
 
2425
      if (!item.hasOwnProperty("optionIndex"))
 
2426
        continue;
 
2427
      aCallback(item, i);
 
2428
    }
1981
2429
  },
1982
2430
 
1983
2431
  _updateControl: function() {
1984
2432
    let currentSelectedIndexes = this._getSelectedIndexes();
1985
2433
 
1986
 
    let isIdentical = currentSelectedIndexes.length == this._selectedIndexes.length;
 
2434
    let isIdentical = (this._selectedIndexes && this._selectedIndexes.length == currentSelectedIndexes.length);
1987
2435
    if (isIdentical) {
1988
2436
      for (let i = 0; i < currentSelectedIndexes.length; i++) {
1989
2437
        if (currentSelectedIndexes[i] != this._selectedIndexes[i]) {
1993
2441
      }
1994
2442
    }
1995
2443
 
1996
 
    if (!isIdentical)
1997
 
      this._control.fireOnChange();
1998
 
  },
1999
 
 
2000
 
  _isValidElement: function(aElement) {
2001
 
    if (!aElement || aElement.disabled)
2002
 
      return false;
2003
 
 
2004
 
    return (aElement instanceof HTMLSelectElement) || (aElement instanceof Ci.nsIDOMXULMenuListElement);
2005
 
  },
2006
 
 
2007
 
  reset: function() {
2008
 
    this._updateControl();
2009
 
    let empty = this._list.cloneNode(false);
2010
 
    this._list.parentNode.replaceChild(empty, this._list);
2011
 
    this._list = empty;
2012
 
  },
2013
 
 
2014
 
  close: function() {
2015
 
    this._list.removeEventListener("click", this, false);
2016
 
    this._panel.hidden = true;
2017
 
 
2018
 
    this.reset();
2019
 
  },
2020
 
 
2021
 
  canShowUIFor: function(aElement) {
2022
 
    return this._isValidElement(aElement);
2023
 
  },
2024
 
 
2025
 
  unselectAll: function() {
2026
 
    this._forEachOption(function(aItem, aIndex) aItem.selected = false);
2027
 
  },
2028
 
 
2029
 
  selectByIndex: function(aIndex) {
2030
 
    for (let i = 0; i < this._list.childNodes.length; i++) {
2031
 
      let option = this._list.childNodes[i];
2032
 
      if (option.optionIndex == aIndex) {
2033
 
        option.selected = true;
2034
 
        this._scrollElementIntoView(option);
2035
 
        break;
2036
 
      }
2037
 
    }
 
2444
    if (isIdentical)
 
2445
      return;
 
2446
 
 
2447
    Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceChange", { });
2038
2448
  },
2039
2449
 
2040
2450
  handleEvent: function(aEvent) {
2042
2452
      case "click":
2043
2453
        let item = aEvent.target;
2044
2454
        if (item && item.hasOwnProperty("optionIndex")) {
2045
 
          if (this._control.multiple) {
 
2455
          if (this._list.multiple) {
2046
2456
            // Toggle the item state
2047
2457
            item.selected = !item.selected;
2048
 
            this._control.select(item.optionIndex, item.selected, false);
2049
2458
          }
2050
2459
          else {
2051
2460
            this.unselectAll();
2052
2461
 
2053
2462
            // Select the new one and update the control
2054
2463
            item.selected = true;
2055
 
            this._control.select(item.optionIndex, true, true);
2056
2464
          }
 
2465
          this.onSelect(item.optionIndex, item.selected, !this._list.multiple);
2057
2466
        }
2058
2467
        break;
2059
 
    }
 
2468
      case "overflow":
 
2469
        if (!this._textbox.value)
 
2470
          this.showFilter = true;
 
2471
        break;
 
2472
    }
 
2473
  },
 
2474
 
 
2475
  onSelect: function(aIndex, aSelected, aClearAll) {
 
2476
    let json = {
 
2477
      index: aIndex,
 
2478
      selected: aSelected,
 
2479
      clearAll: aClearAll
 
2480
    };
 
2481
    Browser.selectedBrowser.messageManager.sendAsyncMessage("FormAssist:ChoiceSelect", json);
 
2482
 
 
2483
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-button-element.html#the-select-element
 
2484
    if (!this._list.multiple) {
 
2485
      this._updateControl();
 
2486
      // Update the selectedIndex so the field will fire a new change event if
 
2487
      // needed
 
2488
      this._selectedIndexes = [aIndex];
 
2489
    }
 
2490
 
2060
2491
  }
2061
2492
};
2062
2493
 
2063
 
const kXLinkNamespace = "http://www.w3.org/1999/xlink";
 
2494
var MenuListHelperUI = {
 
2495
  get _container() {
 
2496
    delete this._container;
 
2497
    return this._container = document.getElementById("menulist-container");
 
2498
  },
 
2499
 
 
2500
  get _popup() {
 
2501
    delete this._popup;
 
2502
    return this._popup = document.getElementById("menulist-popup");
 
2503
  },
 
2504
 
 
2505
  get _title() {
 
2506
    delete this._title;
 
2507
    return this._title = document.getElementById("menulist-title");
 
2508
  },
 
2509
 
 
2510
  _firePopupEvent: function firePopupEvent(aEventName) {
 
2511
    let menupopup = this._currentList.menupopup;
 
2512
    if (menupopup.hasAttribute(aEventName)) {
 
2513
      let func = new Function("event", menupopup.getAttribute(aEventName));
 
2514
      func.call(this);
 
2515
    }
 
2516
  },
 
2517
 
 
2518
  _currentList: null,
 
2519
  show: function mn_show(aMenulist) {
 
2520
    this._currentList = aMenulist;
 
2521
    this._container.setAttribute("for", aMenulist.id);
 
2522
    this._title.value = aMenulist.title || "";
 
2523
    this._firePopupEvent("onpopupshowing");
 
2524
 
 
2525
    let container = this._container;
 
2526
    let listbox = this._popup.lastChild;
 
2527
    while (listbox.firstChild)
 
2528
      listbox.removeChild(listbox.firstChild);
 
2529
 
 
2530
    let children = this._currentList.menupopup.children;
 
2531
    for (let i = 0; i < children.length; i++) {
 
2532
      let child = children[i];
 
2533
      let item = document.createElement("richlistitem");
 
2534
      if (child.disabled)
 
2535
        item.setAttribute("disabled", "true");
 
2536
      if (child.hidden)
 
2537
        item.setAttribute("hidden", "true");
 
2538
 
 
2539
      // Add selected as a class name instead of an attribute to not being overidden
 
2540
      // by the richlistbox behavior (it sets the "current" and "selected" attribute
 
2541
      item.setAttribute("class", "menulist-command prompt-button" + (child.selected ? " selected" : ""));
 
2542
 
 
2543
      let image = document.createElement("image");
 
2544
      image.setAttribute("src", child.image || "");
 
2545
      item.appendChild(image);
 
2546
 
 
2547
      let label = document.createElement("label");
 
2548
      label.setAttribute("value", child.label);
 
2549
      item.appendChild(label);
 
2550
 
 
2551
      listbox.appendChild(item);
 
2552
    }
 
2553
 
 
2554
    window.addEventListener("resize", this, true);
 
2555
    container.hidden = false;
 
2556
    this.sizeToContent();
 
2557
    BrowserUI.pushPopup(this, [this._popup]);
 
2558
  },
 
2559
 
 
2560
  hide: function mn_hide() {
 
2561
    this._currentList = null;
 
2562
    this._container.removeAttribute("for");
 
2563
    this._container.hidden = true;
 
2564
    window.removeEventListener("resize", this, true);
 
2565
    BrowserUI.popPopup(this);
 
2566
  },
 
2567
 
 
2568
  selectByIndex: function mn_selectByIndex(aIndex) {
 
2569
    this._currentList.selectedIndex = aIndex;
 
2570
 
 
2571
    // Dispatch a xul command event to the attached menulist
 
2572
    if (this._currentList.dispatchEvent) {
 
2573
      let evt = document.createEvent("XULCommandEvent");
 
2574
      evt.initCommandEvent("command", true, true, window, 0, false, false, false, false, null);
 
2575
      this._currentList.dispatchEvent(evt);
 
2576
    }
 
2577
 
 
2578
    this.hide();
 
2579
  },
 
2580
 
 
2581
  sizeToContent: function sizeToContent() {
 
2582
    this._popup.maxWidth = window.innerWidth * 0.75;
 
2583
  },
 
2584
 
 
2585
  handleEvent: function handleEvent(aEvent) {
 
2586
    this.sizeToContent();
 
2587
  }
 
2588
}
2064
2589
 
2065
2590
var ContextHelper = {
2066
 
  popupNode: null,
2067
 
  onLink: false,
2068
 
  onSaveableLink: false,
2069
 
  onVoiceLink: false,
2070
 
  onImage: false,
2071
 
  onLoadedImage: false,
2072
 
  linkURL: "",
2073
 
  linkProtocol: null,
2074
 
  mediaURL: "",
2075
 
 
2076
 
  _clearState: function ch_clearState() {
2077
 
    this.popupNode = null;
2078
 
    this.onLink = false;
2079
 
    this.onSaveableLink = false;
2080
 
    this.onVoiceLink = false;
2081
 
    this.onImage = false;
2082
 
    this.onLoadedImage = false;
2083
 
    this.linkURL = "";
2084
 
    this.linkProtocol = null;
2085
 
    this.mediaURL = "";
2086
 
  },
2087
 
 
2088
 
  _getLinkURL: function ch_getLinkURL(aLink) {
2089
 
    let href = aLink.href;  
2090
 
    if (href)
2091
 
      return href;
2092
 
 
2093
 
    href = aLink.getAttributeNS(kXLinkNamespace, "href");
2094
 
    if (!href || !href.match(/\S/)) {
2095
 
      // Without this we try to save as the current doc,
2096
 
      // for example, HTML case also throws if empty
2097
 
      throw "Empty href";
2098
 
    }
2099
 
 
2100
 
    return Util.makeURLAbsolute(aLink.baseURI, href);
2101
 
  },
2102
 
 
2103
 
  _getURI: function ch_getURI(aURL) {
2104
 
    try {
2105
 
      return makeURI(aURL);
2106
 
    } catch (ex) { }
2107
 
 
2108
 
    return null;
2109
 
  },
2110
 
  
2111
 
  _getProtocol: function ch_getProtocol(aURI) {
2112
 
    if (aURI)
2113
 
      return aURI.scheme;
2114
 
    return null;
2115
 
  },
2116
 
  
2117
 
  _isSaveable: function ch_isSaveable(aProtocol) {
2118
 
    // We don't do the Right Thing for news/snews yet, so turn them off until we do
2119
 
    return aProtocol && !(aProtocol == "mailto" || aProtocol == "javascript" || aProtocol == "news" || aProtocol == "snews");
2120
 
  },
2121
 
 
2122
 
  _isVoice: function ch_isVoice(aProtocol) {
2123
 
    // Collection of protocols related to voice or data links
2124
 
    return aProtocol && (aProtocol == "tel" || aProtocol == "callto" || aProtocol == "sip" || aProtocol == "voipto");
2125
 
  },
2126
 
 
2127
 
  handleEvent: function ch_handleEvent(aEvent) {
2128
 
    this._clearState();
2129
 
 
2130
 
    let [elementX, elementY] = Browser.transformClientToBrowser(aEvent.clientX, aEvent.clientY);
2131
 
    this.popupNode = Browser.elementFromPoint(elementX, elementY);
2132
 
 
2133
 
    // Do checks for nodes that never have children.
2134
 
    if (this.popupNode.nodeType == Node.ELEMENT_NODE) {
2135
 
      // See if the user clicked on an image.
2136
 
      if (this.popupNode instanceof Ci.nsIImageLoadingContent && this.popupNode.currentURI) {
2137
 
        this.onImage = true;
2138
 
        this.mediaURL = this.popupNode.currentURI.spec;
2139
 
 
2140
 
        let request = this.popupNode.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
2141
 
        if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
2142
 
          this.onLoadedImage = true;
2143
 
      }
2144
 
    }
2145
 
 
2146
 
    let elem = this.popupNode;
2147
 
    while (elem) {
2148
 
      if (elem.nodeType == Node.ELEMENT_NODE) {
2149
 
        // Link?
2150
 
        if (!this.onLink &&
2151
 
             ((elem instanceof HTMLAnchorElement && elem.href) ||
2152
 
              (elem instanceof HTMLAreaElement && elem.href) ||
2153
 
              elem instanceof HTMLLinkElement ||
2154
 
              elem.getAttributeNS(kXLinkNamespace, "type") == "simple")) {
2155
 
 
2156
 
          // Target is a link or a descendant of a link.
2157
 
          this.linkURL = this._getLinkURL(elem);
2158
 
          this.linkProtocol = this._getProtocol(this._getURI(this.linkURL));
2159
 
          this.onLink = true;
2160
 
          this.onSaveableLink = this._isSaveable(this.linkProtocol);
2161
 
          this.onVoiceLink = this._isVoice(this.linkProtocol);
2162
 
        }
2163
 
      }
2164
 
 
2165
 
      elem = elem.parentNode;
2166
 
    }
2167
 
 
2168
 
    let first = last = null;
 
2591
  popupState: null,
 
2592
 
 
2593
  get _panel() {
 
2594
    delete this._panel;
 
2595
    return this._panel = document.getElementById("context-container");
 
2596
  },
 
2597
 
 
2598
  get _popup() {
 
2599
    delete this._popup;
 
2600
    return this._popup = document.getElementById("context-popup");
 
2601
  },
 
2602
 
 
2603
  showPopup: function ch_showPopup(aMessage) {
 
2604
    this.popupState = aMessage.json;
 
2605
    this.popupState.target = aMessage.target;
 
2606
 
 
2607
    let first = null;
 
2608
    let last = null;
2169
2609
    let commands = document.getElementById("context-commands");
2170
2610
    for (let i=0; i<commands.childElementCount; i++) {
2171
2611
      let command = commands.children[i];
2172
 
      let types = command.getAttribute("type").split(/\s+/);
2173
2612
      command.removeAttribute("selector");
2174
 
      if (types.indexOf("image") != -1 && this.onImage) {
2175
 
        first = (first ? first : command);
2176
 
        last = command;
2177
 
        command.hidden = false;
2178
 
        continue;
2179
 
      } else if (types.indexOf("image-loaded") != -1 && this.onLoadedImage) {
2180
 
        first = (first ? first : command);
2181
 
        last = command;
2182
 
        command.hidden = false;
2183
 
        continue;
2184
 
      } else if (types.indexOf("link") != -1 && this.onSaveableLink) {
2185
 
        first = (first ? first : command);
2186
 
        last = command;
2187
 
        command.hidden = false;
2188
 
        continue;
2189
 
      } else if (types.indexOf("callto") != -1 && this.onVoiceLink) {
2190
 
        first = (first ? first : command);
2191
 
        last = command;
2192
 
        command.hidden = false;
2193
 
        continue;
2194
 
      } else if (types.indexOf("mailto") != -1 && this.onLink && this.linkProtocol == "mailto") {
2195
 
        first = (first ? first : command);
2196
 
        last = command;
2197
 
        command.hidden = false;
2198
 
        continue;
2199
 
      }
2200
2613
      command.hidden = true;
 
2614
 
 
2615
      let types = command.getAttribute("type").split(/\s+/);
 
2616
      for (let i=0; i<types.length; i++) {
 
2617
        if (this.popupState.types.indexOf(types[i]) != -1) {
 
2618
          first = first || command;
 
2619
          last = command;
 
2620
          command.hidden = false;
 
2621
          break;
 
2622
        }
 
2623
      }
2201
2624
    }
2202
2625
 
2203
2626
    if (!first) {
2204
 
      this._clearState();
2205
 
      return;
 
2627
      this.popupState = null;
 
2628
      return false;
2206
2629
    }
2207
 
    
 
2630
 
 
2631
    // Allow the first and last *non-hidden* elements to be selected in CSS.
2208
2632
    first.setAttribute("selector", "first-child");
2209
2633
    last.setAttribute("selector", "last-child");
2210
2634
 
2211
2635
    let label = document.getElementById("context-hint");
2212
 
    if (this.onImage)
2213
 
      label.value = this.mediaURL;
2214
 
    if (this.onLink)
2215
 
      label.value = this.linkURL;
2216
 
 
2217
 
    let container = document.getElementById("context-popup");
2218
 
    container.hidden = false;
2219
 
 
2220
 
    // Make sure the container is at least sized to the content
2221
 
    let preferredHeight = 0;
2222
 
    for (let i=0; i<container.childElementCount; i++) {
2223
 
      preferredHeight += container.children[i].getBoundingClientRect().height;
2224
 
    }
2225
 
 
2226
 
    let rect = container.getBoundingClientRect();
2227
 
    let height = Math.min(preferredHeight, 0.75 * window.innerWidth);
2228
 
    let width = Math.min(rect.width, 0.75 * window.innerWidth);
2229
 
 
2230
 
    container.height = height;
2231
 
    container.width = width;
2232
 
    container.top = (window.innerHeight - height) / 2;
2233
 
    container.left = (window.innerWidth - width) / 2;
2234
 
 
2235
 
    BrowserUI.pushPopup(this, [container]);
 
2636
    label.value = this.popupState.label || "";
 
2637
 
 
2638
    this._panel.hidden = false;
 
2639
    window.addEventListener("resize", this, true);
 
2640
    window.addEventListener("keypress", this, true);
 
2641
 
 
2642
    this.sizeToContent();
 
2643
    BrowserUI.pushPopup(this, [this._popup]);
 
2644
    return true;
2236
2645
  },
2237
 
  
 
2646
 
2238
2647
  hide: function ch_hide() {
2239
 
    this._clearState();
2240
 
    
2241
 
    let container = document.getElementById("context-popup");
2242
 
    container.hidden = true;
2243
 
 
2244
 
    BrowserUI.popPopup();
 
2648
    if (this._panel.hidden)
 
2649
      return;
 
2650
    this.popupState = null;
 
2651
    this._panel.hidden = true;
 
2652
    window.removeEventListener("resize", this, true);
 
2653
    window.removeEventListener("keypress", this, true);
 
2654
 
 
2655
    BrowserUI.popPopup(this);
 
2656
  },
 
2657
 
 
2658
  sizeToContent: function sizeToContent() {
 
2659
    this._popup.maxWidth = window.innerWidth * 0.75;
 
2660
  },
 
2661
 
 
2662
  handleEvent: function handleEvent(aEvent) {
 
2663
    switch (aEvent.type) {
 
2664
      case "resize":
 
2665
        this.sizeToContent();
 
2666
        break;
 
2667
      case "keypress":
 
2668
        // Hide the context menu so you can't type behind it.
 
2669
        aEvent.stopPropagation();
 
2670
        aEvent.preventDefault();
 
2671
        if (aEvent.keyCode != aEvent.DOM_VK_ESCAPE)
 
2672
          this.hide();
 
2673
        break;
 
2674
    }
2245
2675
  }
2246
2676
};
2247
2677
 
2248
2678
var ContextCommands = {
2249
 
  openInNewTab: function cc_openInNewTab(aEvent) {
2250
 
    Browser.addTab(ContextHelper.linkURL, false);
2251
 
  },
2252
 
 
2253
 
  saveImage: function cc_saveImage(aEvent) {
2254
 
    let doc = ContextHelper.popupNode.ownerDocument;
2255
 
    saveImageURL(ContextHelper.mediaURL, null, "SaveImageTitle", false, false, doc.documentURIObject);
2256
 
  }
2257
 
}
2258
 
 
2259
 
function removeBookmarksForURI(aURI) {
2260
 
  //XXX blargle xpconnect! might not matter, but a method on
2261
 
  // nsINavBookmarksService that takes an array of items to
2262
 
  // delete would be faster. better yet, a method that takes a URI!
2263
 
  let itemIds = PlacesUtils.getBookmarksForURI(aURI);
2264
 
  itemIds.forEach(PlacesUtils.bookmarks.removeItem);
2265
 
 
2266
 
  BrowserUI.updateStar();
2267
 
}
 
2679
  copy: function cc_copy() {
 
2680
    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
 
2681
    clipboard.copyString(ContextHelper.popupState.string);
 
2682
 
 
2683
    let target = ContextHelper.popupState.target;
 
2684
    if (target)
 
2685
      target.focus();
 
2686
  },
 
2687
 
 
2688
#ifdef ANDROID
 
2689
  selectInput: function cc_selectInput() {
 
2690
    let imePicker = Cc["@mozilla.org/imepicker;1"].getService(Ci.nsIIMEPicker);
 
2691
    imePicker.show();
 
2692
  },
 
2693
#endif
 
2694
 
 
2695
  paste: function cc_paste() {
 
2696
    let data = ContextHelper.popupState.data;
 
2697
    let target = ContextHelper.popupState.target;
 
2698
    target.editor.paste(Ci.nsIClipboard.kGlobalClipboard);
 
2699
    target.focus();
 
2700
  },
 
2701
 
 
2702
  selectAll: function cc_selectAll() {
 
2703
    let target = ContextHelper.popupState.target;
 
2704
    target.editor.selectAll();
 
2705
    target.focus();
 
2706
  },
 
2707
 
 
2708
  openInNewTab: function cc_openInNewTab() {
 
2709
    Browser.addTab(ContextHelper.popupState.linkURL, false, Browser.selectedTab);
 
2710
  },
 
2711
 
 
2712
  saveLink: function cc_saveLink() {
 
2713
    let browser = ContextHelper.popupState.target;
 
2714
    saveURL(ContextHelper.popupState.linkURL, null, "SaveLinkTitle", false, true, browser.documentURI);
 
2715
  },
 
2716
 
 
2717
  saveImage: function cc_saveImage() {
 
2718
    let browser = ContextHelper.popupState.target;
 
2719
    saveImageURL(ContextHelper.popupState.mediaURL, null, "SaveImageTitle", false, true, browser.documentURI);
 
2720
  },
 
2721
 
 
2722
  shareLink: function cc_shareLink() {
 
2723
    let state = ContextHelper.popupState;
 
2724
    SharingUI.show(state.linkURL, state.linkTitle);
 
2725
  },
 
2726
 
 
2727
  shareMedia: function cc_shareMedia() {
 
2728
    SharingUI.show(ContextHelper.popupState.mediaURL, null);
 
2729
  },
 
2730
 
 
2731
  sendCommand: function cc_playVideo(aCommand) {
 
2732
    let browser = ContextHelper.popupState.target;
 
2733
    browser.messageManager.sendAsyncMessage("Browser:ContextCommand", { command: aCommand });
 
2734
  },
 
2735
 
 
2736
  editBookmark: function cc_editBookmark() {
 
2737
    let target = ContextHelper.popupState.target;
 
2738
    target.startEditing();
 
2739
  },
 
2740
 
 
2741
  removeBookmark: function cc_removeBookmark() {
 
2742
    let target = ContextHelper.popupState.target;
 
2743
    target.remove();
 
2744
  }
 
2745
}
 
2746
 
 
2747
var SharingUI = {
 
2748
  _dialog: null,
 
2749
 
 
2750
  show: function show(aURL, aTitle) {
 
2751
    try {
 
2752
      this.showSharingUI(aURL, aTitle);
 
2753
    } catch (ex) {
 
2754
      this.showFallback(aURL, aTitle);
 
2755
    }
 
2756
  },
 
2757
 
 
2758
  showSharingUI: function showSharingUI(aURL, aTitle) {
 
2759
    let sharingSvc = Cc["@mozilla.org/uriloader/external-sharing-app-service;1"].getService(Ci.nsIExternalSharingAppService);
 
2760
    sharingSvc.shareWithDefault(aURL, "text/plain", aTitle);
 
2761
  },
 
2762
 
 
2763
  showFallback: function showFallback(aURL, aTitle) {
 
2764
    this._dialog = importDialog(window, "chrome://browser/content/share.xul", null);
 
2765
    document.getElementById("share-title").value = aTitle || aURL;
 
2766
 
 
2767
    BrowserUI.pushPopup(this, this._dialog);
 
2768
 
 
2769
    let bbox = document.getElementById("share-buttons-box");
 
2770
    this._handlers.forEach(function(handler) {
 
2771
      let button = document.createElement("button");
 
2772
      button.className = "prompt-button";
 
2773
      button.setAttribute("label", handler.name);
 
2774
      button.addEventListener("command", function() {
 
2775
        SharingUI.hide();
 
2776
        handler.callback(aURL || "", aTitle || "");
 
2777
      }, false);
 
2778
      bbox.appendChild(button);
 
2779
    });
 
2780
    this._dialog.waitForClose();
 
2781
    BrowserUI.popPopup(this);
 
2782
  },
 
2783
 
 
2784
  hide: function hide() {
 
2785
    this._dialog.close();
 
2786
    this._dialog = null;
 
2787
  },
 
2788
 
 
2789
  _handlers: [
 
2790
    {
 
2791
      name: "Email",
 
2792
      callback: function callback(aURL, aTitle) {
 
2793
        let url = "mailto:?subject=" + encodeURIComponent(aTitle) +
 
2794
                  "&body=" + encodeURIComponent(aURL);
 
2795
        let uri = Services.io.newURI(url, null, null);
 
2796
        let extProtocolSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"]
 
2797
                             .getService(Ci.nsIExternalProtocolService);
 
2798
        extProtocolSvc.loadUrl(uri);
 
2799
      }
 
2800
    },
 
2801
    {
 
2802
      name: "Twitter",
 
2803
      callback: function callback(aURL, aTitle) {
 
2804
        let url = "http://twitter.com/home?status=" + encodeURIComponent((aTitle ? aTitle+": " : "")+aURL);
 
2805
        BrowserUI.newTab(url, Browser.selectedTab);
 
2806
      }
 
2807
    },
 
2808
    {
 
2809
      name: "Google Reader",
 
2810
      callback: function callback(aURL, aTitle) {
 
2811
        let url = "http://www.google.com/reader/link?url=" + encodeURIComponent(aURL) +
 
2812
                  "&title=" + encodeURIComponent(aTitle);
 
2813
        BrowserUI.addTab(url, Browser.selectedTab);
 
2814
      }
 
2815
    },
 
2816
    {
 
2817
      name: "Facebook",
 
2818
      callback: function callback(aURL, aTitle) {
 
2819
        let url = "http://www.facebook.com/share.php?u=" + encodeURIComponent(aURL);
 
2820
        BrowserUI.newTab(url, Browser.selectedTab);
 
2821
      }
 
2822
    }
 
2823
  ]
 
2824
};
 
2825
 
 
2826
var BadgeHandlers = {
 
2827
  _handlers: [
 
2828
    {
 
2829
      _lastUpdate: 0,
 
2830
      _lastCount: 0,
 
2831
      url: "https://mail.google.com/mail",
 
2832
      updateBadge: function(aBadge) {
 
2833
        // Use the cache if possible
 
2834
        let now = Date.now();
 
2835
        if (this._lastCount && this._lastUpdate > now - 1000) {
 
2836
          aBadge.set(this._lastCount);
 
2837
          return;
 
2838
        }
 
2839
 
 
2840
        this._lastUpdate = now;
 
2841
 
 
2842
        // Use any saved username and password. If we don't have any login and we are not
 
2843
        // currently logged into Gmail, we won't get any count.
 
2844
        let login = BadgeHandlers.getLogin("https://www.google.com");
 
2845
 
 
2846
        // Get the feed and read the count, passing any saved username and password
 
2847
        // but do not show any security dialogs if we fail
 
2848
        let req = new XMLHttpRequest();
 
2849
        req.mozBackgroundRequest = true;
 
2850
        req.open("GET", "https://mail.google.com/mail/feed/atom", true, login.username, login.password);
 
2851
        req.onreadystatechange = function(aEvent) {
 
2852
          if (req.readyState == 4) {
 
2853
            if (req.status == 200 && req.responseXML) {
 
2854
              let count = req.responseXML.getElementsByTagName("fullcount");
 
2855
              this._lastCount = count ? count[0].childNodes[0].nodeValue : 0;
 
2856
            } else {
 
2857
              this._lastCount = 0;
 
2858
            }
 
2859
            this._lastCount = BadgeHandlers.setNumberBadge(aBadge, this._lastCount);
 
2860
          }
 
2861
        };
 
2862
        req.send(null);
 
2863
      }
 
2864
    }
 
2865
  ],
 
2866
 
 
2867
  register: function(aPopup) {
 
2868
    let handlers = this._handlers;
 
2869
    for (let i = 0; i < handlers.length; i++)
 
2870
      aPopup.registerBadgeHandler(handlers[i].url, handlers[i]);
 
2871
  },
 
2872
 
 
2873
  getLogin: function(aURL) {
 
2874
    let lm = Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
 
2875
    let logins = lm.findLogins({}, aURL, aURL, null);
 
2876
    let username = logins.length > 0 ? logins[0].username : "";
 
2877
    let password = logins.length > 0 ? logins[0].password : "";
 
2878
    return { username: username, password: password };
 
2879
  },
 
2880
 
 
2881
  clampBadge: function(aValue) {
 
2882
    if (aValue > 100)
 
2883
      aValue = "99+";
 
2884
    return aValue;
 
2885
  },
 
2886
 
 
2887
  setNumberBadge: function(aBadge, aValue) {
 
2888
    if (parseInt(aValue) != 0) {
 
2889
      aValue = this.clampBadge(aValue);
 
2890
      aBadge.set(aValue);
 
2891
    } else {
 
2892
      aBadge.set("");
 
2893
    }
 
2894
    return aValue;
 
2895
  }
 
2896
};
 
2897
 
 
2898
var FullScreenVideo = {
 
2899
  browser: null,
 
2900
 
 
2901
  init: function fsv_init() {
 
2902
    messageManager.addMessageListener("Browser:FullScreenVideo:Start", this.show.bind(this));
 
2903
    messageManager.addMessageListener("Browser:FullScreenVideo:Close", this.hide.bind(this));
 
2904
  },
 
2905
 
 
2906
  show: function fsv_show() {
 
2907
    this.createBrowser();
 
2908
    window.fullScreen = true;
 
2909
    BrowserUI.pushPopup(this, this.browser);
 
2910
  },
 
2911
 
 
2912
  hide: function fsv_hide() {
 
2913
    this.destroyBrowser();
 
2914
    window.fullScreen = false;
 
2915
    BrowserUI.popPopup(this);
 
2916
  },
 
2917
 
 
2918
  createBrowser: function fsv_createBrowser() {
 
2919
    let browser = this.browser = document.createElement("browser");
 
2920
    browser.className = "window-width window-height full-screen";
 
2921
    browser.setAttribute("type", "content");
 
2922
    browser.setAttribute("remote", "true");
 
2923
    browser.setAttribute("src", "chrome://browser/content/fullscreen-video.xhtml");
 
2924
    document.getElementById("main-window").appendChild(browser);
 
2925
 
 
2926
    let mm = browser.messageManager;
 
2927
    mm.loadFrameScript("chrome://browser/content/fullscreen-video.js", true);
 
2928
 
 
2929
    browser.addEventListener("TapDown", this, true);
 
2930
    browser.addEventListener("TapSingle", this, false);
 
2931
 
 
2932
    return browser;
 
2933
  },
 
2934
 
 
2935
  destroyBrowser: function fsv_destroyBrowser() {
 
2936
    let browser = this.browser;
 
2937
    browser.removeEventListener("TapDown", this, false);
 
2938
    browser.removeEventListener("TapSingle", this, false);
 
2939
    browser.parentNode.removeChild(browser);
 
2940
    this.browser = null;
 
2941
  },
 
2942
 
 
2943
  handleEvent: function fsv_handleEvent(aEvent) {
 
2944
    switch (aEvent.type) {
 
2945
      case "TapDown":
 
2946
        this._dispatchMouseEvent("Browser:MouseDown", aEvent.clientX, aEvent.clientY);
 
2947
        break;
 
2948
      case "TapSingle":
 
2949
        this._dispatchMouseEvent("Browser:MouseUp", aEvent.clientX, aEvent.clientY);
 
2950
        break;
 
2951
    }
 
2952
  },
 
2953
 
 
2954
  _dispatchMouseEvent: function fsv_dispatchMouseEvent(aName, aX, aY) {
 
2955
    let pos = this.browser.transformClientToBrowser(aX, aY);
 
2956
    this.browser.messageManager.sendAsyncMessage(aName, {
 
2957
      x: pos.x,
 
2958
      y: pos.y,
 
2959
      messageId: null
 
2960
    });
 
2961
  }
 
2962
};
 
2963
 
 
2964
var AppMenu = {
 
2965
  get panel() {
 
2966
    delete this.panel;
 
2967
    return this.panel = document.getElementById("appmenu");
 
2968
  },
 
2969
 
 
2970
  show: function show() {
 
2971
    if (BrowserUI.activePanel || BrowserUI.isPanelVisible())
 
2972
      return;
 
2973
    this.panel.setAttribute("count", this.panel.childNodes.length);
 
2974
    this.panel.collapsed = false;
 
2975
 
 
2976
    addEventListener("keypress", this, true);
 
2977
 
 
2978
    BrowserUI.lockToolbar();
 
2979
    BrowserUI.pushPopup(this, [this.panel, Elements.toolbarContainer]);
 
2980
  },
 
2981
 
 
2982
  hide: function hide() {
 
2983
    this.panel.collapsed = true;
 
2984
 
 
2985
    removeEventListener("keypress", this, true);
 
2986
 
 
2987
    BrowserUI.unlockToolbar();
 
2988
    BrowserUI.popPopup(this);
 
2989
  },
 
2990
 
 
2991
  toggle: function toggle() {
 
2992
    this.panel.collapsed ? this.show() : this.hide();
 
2993
  },
 
2994
 
 
2995
  handleEvent: function handleEvent(aEvent) {
 
2996
    this.hide();
 
2997
  }
 
2998
};