browser/components/customizableui/content/panelUI.js

Wed, 31 Dec 2014 07:53:36 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:53:36 +0100
branch
TOR_BUG_3246
changeset 5
4ab42b5ab56c
permissions
-rw-r--r--

Correct small whitespace inconsistency, lost while renaming variables.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
michael@0 6 "resource:///modules/CustomizableUI.jsm");
michael@0 7 XPCOMUtils.defineLazyModuleGetter(this, "ScrollbarSampler",
michael@0 8 "resource:///modules/ScrollbarSampler.jsm");
michael@0 9 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
michael@0 10 "resource://gre/modules/Promise.jsm");
michael@0 11 XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
michael@0 12 "resource://gre/modules/ShortcutUtils.jsm");
michael@0 13 /**
michael@0 14 * Maintains the state and dispatches events for the main menu panel.
michael@0 15 */
michael@0 16
michael@0 17 const PanelUI = {
michael@0 18 /** Panel events that we listen for. **/
michael@0 19 get kEvents() ["popupshowing", "popupshown", "popuphiding", "popuphidden"],
michael@0 20 /**
michael@0 21 * Used for lazily getting and memoizing elements from the document. Lazy
michael@0 22 * getters are set in init, and memoizing happens after the first retrieval.
michael@0 23 */
michael@0 24 get kElements() {
michael@0 25 return {
michael@0 26 contents: "PanelUI-contents",
michael@0 27 mainView: "PanelUI-mainView",
michael@0 28 multiView: "PanelUI-multiView",
michael@0 29 helpView: "PanelUI-helpView",
michael@0 30 menuButton: "PanelUI-menu-button",
michael@0 31 panel: "PanelUI-popup",
michael@0 32 scroller: "PanelUI-contents-scroller"
michael@0 33 };
michael@0 34 },
michael@0 35
michael@0 36 _initialized: false,
michael@0 37 init: function() {
michael@0 38 for (let [k, v] of Iterator(this.kElements)) {
michael@0 39 // Need to do fresh let-bindings per iteration
michael@0 40 let getKey = k;
michael@0 41 let id = v;
michael@0 42 this.__defineGetter__(getKey, function() {
michael@0 43 delete this[getKey];
michael@0 44 return this[getKey] = document.getElementById(id);
michael@0 45 });
michael@0 46 }
michael@0 47
michael@0 48 this.menuButton.addEventListener("mousedown", this);
michael@0 49 this.menuButton.addEventListener("keypress", this);
michael@0 50 this._overlayScrollListenerBoundFn = this._overlayScrollListener.bind(this);
michael@0 51 window.matchMedia("(-moz-overlay-scrollbars)").addListener(this._overlayScrollListenerBoundFn);
michael@0 52 CustomizableUI.addListener(this);
michael@0 53 this._initialized = true;
michael@0 54 },
michael@0 55
michael@0 56 _eventListenersAdded: false,
michael@0 57 _ensureEventListenersAdded: function() {
michael@0 58 if (this._eventListenersAdded)
michael@0 59 return;
michael@0 60 this._addEventListeners();
michael@0 61 },
michael@0 62
michael@0 63 _addEventListeners: function() {
michael@0 64 for (let event of this.kEvents) {
michael@0 65 this.panel.addEventListener(event, this);
michael@0 66 }
michael@0 67
michael@0 68 this.helpView.addEventListener("ViewShowing", this._onHelpViewShow, false);
michael@0 69 this._eventListenersAdded = true;
michael@0 70 },
michael@0 71
michael@0 72 uninit: function() {
michael@0 73 for (let event of this.kEvents) {
michael@0 74 this.panel.removeEventListener(event, this);
michael@0 75 }
michael@0 76 this.helpView.removeEventListener("ViewShowing", this._onHelpViewShow);
michael@0 77 this.menuButton.removeEventListener("mousedown", this);
michael@0 78 this.menuButton.removeEventListener("keypress", this);
michael@0 79 window.matchMedia("(-moz-overlay-scrollbars)").removeListener(this._overlayScrollListenerBoundFn);
michael@0 80 CustomizableUI.removeListener(this);
michael@0 81 this._overlayScrollListenerBoundFn = null;
michael@0 82 },
michael@0 83
michael@0 84 /**
michael@0 85 * Customize mode extracts the mainView and puts it somewhere else while the
michael@0 86 * user customizes. Upon completion, this function can be called to put the
michael@0 87 * panel back to where it belongs in normal browsing mode.
michael@0 88 *
michael@0 89 * @param aMainView
michael@0 90 * The mainView node to put back into place.
michael@0 91 */
michael@0 92 setMainView: function(aMainView) {
michael@0 93 this._ensureEventListenersAdded();
michael@0 94 this.multiView.setMainView(aMainView);
michael@0 95 },
michael@0 96
michael@0 97 /**
michael@0 98 * Opens the menu panel if it's closed, or closes it if it's
michael@0 99 * open.
michael@0 100 *
michael@0 101 * @param aEvent the event that triggers the toggle.
michael@0 102 */
michael@0 103 toggle: function(aEvent) {
michael@0 104 // Don't show the panel if the window is in customization mode,
michael@0 105 // since this button doubles as an exit path for the user in this case.
michael@0 106 if (document.documentElement.hasAttribute("customizing")) {
michael@0 107 return;
michael@0 108 }
michael@0 109 this._ensureEventListenersAdded();
michael@0 110 if (this.panel.state == "open") {
michael@0 111 this.hide();
michael@0 112 } else if (this.panel.state == "closed") {
michael@0 113 this.show(aEvent);
michael@0 114 }
michael@0 115 },
michael@0 116
michael@0 117 /**
michael@0 118 * Opens the menu panel. If the event target has a child with the
michael@0 119 * toolbarbutton-icon attribute, the panel will be anchored on that child.
michael@0 120 * Otherwise, the panel is anchored on the event target itself.
michael@0 121 *
michael@0 122 * @param aEvent the event (if any) that triggers showing the menu.
michael@0 123 */
michael@0 124 show: function(aEvent) {
michael@0 125 let deferred = Promise.defer();
michael@0 126
michael@0 127 this.ensureReady().then(() => {
michael@0 128 if (this.panel.state == "open" ||
michael@0 129 document.documentElement.hasAttribute("customizing")) {
michael@0 130 deferred.resolve();
michael@0 131 return;
michael@0 132 }
michael@0 133
michael@0 134 let editControlPlacement = CustomizableUI.getPlacementOfWidget("edit-controls");
michael@0 135 if (editControlPlacement && editControlPlacement.area == CustomizableUI.AREA_PANEL) {
michael@0 136 updateEditUIVisibility();
michael@0 137 }
michael@0 138
michael@0 139 let personalBookmarksPlacement = CustomizableUI.getPlacementOfWidget("personal-bookmarks");
michael@0 140 if (personalBookmarksPlacement &&
michael@0 141 personalBookmarksPlacement.area == CustomizableUI.AREA_PANEL) {
michael@0 142 PlacesToolbarHelper.customizeChange();
michael@0 143 }
michael@0 144
michael@0 145 let anchor;
michael@0 146 if (!aEvent ||
michael@0 147 aEvent.type == "command") {
michael@0 148 anchor = this.menuButton;
michael@0 149 } else {
michael@0 150 anchor = aEvent.target;
michael@0 151 }
michael@0 152
michael@0 153 this.panel.addEventListener("popupshown", function onPopupShown() {
michael@0 154 this.removeEventListener("popupshown", onPopupShown);
michael@0 155 // As an optimization for the customize mode transition, we preload
michael@0 156 // about:customizing in the background once the menu panel is first
michael@0 157 // shown.
michael@0 158 gCustomizationTabPreloader.ensurePreloading();
michael@0 159 deferred.resolve();
michael@0 160 });
michael@0 161
michael@0 162 let iconAnchor =
michael@0 163 document.getAnonymousElementByAttribute(anchor, "class",
michael@0 164 "toolbarbutton-icon");
michael@0 165 this.panel.openPopup(iconAnchor || anchor);
michael@0 166 });
michael@0 167
michael@0 168 return deferred.promise;
michael@0 169 },
michael@0 170
michael@0 171 /**
michael@0 172 * If the menu panel is being shown, hide it.
michael@0 173 */
michael@0 174 hide: function() {
michael@0 175 if (document.documentElement.hasAttribute("customizing")) {
michael@0 176 return;
michael@0 177 }
michael@0 178
michael@0 179 this.panel.hidePopup();
michael@0 180 },
michael@0 181
michael@0 182 handleEvent: function(aEvent) {
michael@0 183 switch (aEvent.type) {
michael@0 184 case "popupshowing":
michael@0 185 this._adjustLabelsForAutoHyphens();
michael@0 186 // Fall through
michael@0 187 case "popupshown":
michael@0 188 // Fall through
michael@0 189 case "popuphiding":
michael@0 190 // Fall through
michael@0 191 case "popuphidden":
michael@0 192 this._updatePanelButton(aEvent.target);
michael@0 193 break;
michael@0 194 case "mousedown":
michael@0 195 if (aEvent.button == 0)
michael@0 196 this.toggle(aEvent);
michael@0 197 break;
michael@0 198 case "keypress":
michael@0 199 this.toggle(aEvent);
michael@0 200 break;
michael@0 201 }
michael@0 202 },
michael@0 203
michael@0 204 isReady: function() {
michael@0 205 return !!this._isReady;
michael@0 206 },
michael@0 207
michael@0 208 /**
michael@0 209 * Registering the menu panel is done lazily for performance reasons. This
michael@0 210 * method is exposed so that CustomizationMode can force panel-readyness in the
michael@0 211 * event that customization mode is started before the panel has been opened
michael@0 212 * by the user.
michael@0 213 *
michael@0 214 * @param aCustomizing (optional) set to true if this was called while entering
michael@0 215 * customization mode. If that's the case, we trust that customization
michael@0 216 * mode will handle calling beginBatchUpdate and endBatchUpdate.
michael@0 217 *
michael@0 218 * @return a Promise that resolves once the panel is ready to roll.
michael@0 219 */
michael@0 220 ensureReady: function(aCustomizing=false) {
michael@0 221 if (this._readyPromise) {
michael@0 222 return this._readyPromise;
michael@0 223 }
michael@0 224 this._readyPromise = Task.spawn(function() {
michael@0 225 if (!this._initialized) {
michael@0 226 let delayedStartupDeferred = Promise.defer();
michael@0 227 let delayedStartupObserver = (aSubject, aTopic, aData) => {
michael@0 228 if (aSubject == window) {
michael@0 229 Services.obs.removeObserver(delayedStartupObserver, "browser-delayed-startup-finished");
michael@0 230 delayedStartupDeferred.resolve();
michael@0 231 }
michael@0 232 };
michael@0 233 Services.obs.addObserver(delayedStartupObserver, "browser-delayed-startup-finished", false);
michael@0 234 yield delayedStartupDeferred.promise;
michael@0 235 }
michael@0 236
michael@0 237 this.contents.setAttributeNS("http://www.w3.org/XML/1998/namespace", "lang",
michael@0 238 getLocale());
michael@0 239 if (!this._scrollWidth) {
michael@0 240 // In order to properly center the contents of the panel, while ensuring
michael@0 241 // that we have enough space on either side to show a scrollbar, we have to
michael@0 242 // do a bit of hackery. In particular, we calculate a new width for the
michael@0 243 // scroller, based on the system scrollbar width.
michael@0 244 this._scrollWidth =
michael@0 245 (yield ScrollbarSampler.getSystemScrollbarWidth()) + "px";
michael@0 246 let cstyle = window.getComputedStyle(this.scroller);
michael@0 247 let widthStr = cstyle.width;
michael@0 248 // Get the calculated padding on the left and right sides of
michael@0 249 // the scroller too. We'll use that in our final calculation so
michael@0 250 // that if a scrollbar appears, we don't have the contents right
michael@0 251 // up against the edge of the scroller.
michael@0 252 let paddingLeft = cstyle.paddingLeft;
michael@0 253 let paddingRight = cstyle.paddingRight;
michael@0 254 let calcStr = [widthStr, this._scrollWidth,
michael@0 255 paddingLeft, paddingRight].join(" + ");
michael@0 256 this.scroller.style.width = "calc(" + calcStr + ")";
michael@0 257 }
michael@0 258
michael@0 259 if (aCustomizing) {
michael@0 260 CustomizableUI.registerMenuPanel(this.contents);
michael@0 261 } else {
michael@0 262 this.beginBatchUpdate();
michael@0 263 try {
michael@0 264 CustomizableUI.registerMenuPanel(this.contents);
michael@0 265 } finally {
michael@0 266 this.endBatchUpdate();
michael@0 267 }
michael@0 268 }
michael@0 269 this._updateQuitTooltip();
michael@0 270 this.panel.hidden = false;
michael@0 271 this._isReady = true;
michael@0 272 }.bind(this)).then(null, Cu.reportError);
michael@0 273
michael@0 274 return this._readyPromise;
michael@0 275 },
michael@0 276
michael@0 277 /**
michael@0 278 * Switch the panel to the main view if it's not already
michael@0 279 * in that view.
michael@0 280 */
michael@0 281 showMainView: function() {
michael@0 282 this._ensureEventListenersAdded();
michael@0 283 this.multiView.showMainView();
michael@0 284 },
michael@0 285
michael@0 286 /**
michael@0 287 * Switch the panel to the help view if it's not already
michael@0 288 * in that view.
michael@0 289 */
michael@0 290 showHelpView: function(aAnchor) {
michael@0 291 this._ensureEventListenersAdded();
michael@0 292 this.multiView.showSubView("PanelUI-helpView", aAnchor);
michael@0 293 },
michael@0 294
michael@0 295 /**
michael@0 296 * Shows a subview in the panel with a given ID.
michael@0 297 *
michael@0 298 * @param aViewId the ID of the subview to show.
michael@0 299 * @param aAnchor the element that spawned the subview.
michael@0 300 * @param aPlacementArea the CustomizableUI area that aAnchor is in.
michael@0 301 */
michael@0 302 showSubView: function(aViewId, aAnchor, aPlacementArea) {
michael@0 303 this._ensureEventListenersAdded();
michael@0 304 let viewNode = document.getElementById(aViewId);
michael@0 305 if (!viewNode) {
michael@0 306 Cu.reportError("Could not show panel subview with id: " + aViewId);
michael@0 307 return;
michael@0 308 }
michael@0 309
michael@0 310 if (!aAnchor) {
michael@0 311 Cu.reportError("Expected an anchor when opening subview with id: " + aViewId);
michael@0 312 return;
michael@0 313 }
michael@0 314
michael@0 315 if (aPlacementArea == CustomizableUI.AREA_PANEL) {
michael@0 316 this.multiView.showSubView(aViewId, aAnchor);
michael@0 317 } else if (!aAnchor.open) {
michael@0 318 aAnchor.open = true;
michael@0 319 // Emit the ViewShowing event so that the widget definition has a chance
michael@0 320 // to lazily populate the subview with things.
michael@0 321 let evt = document.createEvent("CustomEvent");
michael@0 322 evt.initCustomEvent("ViewShowing", true, true, viewNode);
michael@0 323 viewNode.dispatchEvent(evt);
michael@0 324 if (evt.defaultPrevented) {
michael@0 325 return;
michael@0 326 }
michael@0 327
michael@0 328 let tempPanel = document.createElement("panel");
michael@0 329 tempPanel.setAttribute("type", "arrow");
michael@0 330 tempPanel.setAttribute("id", "customizationui-widget-panel");
michael@0 331 tempPanel.setAttribute("class", "cui-widget-panel");
michael@0 332 tempPanel.setAttribute("context", "");
michael@0 333 document.getElementById(CustomizableUI.AREA_NAVBAR).appendChild(tempPanel);
michael@0 334 // If the view has a footer, set a convenience class on the panel.
michael@0 335 tempPanel.classList.toggle("cui-widget-panelWithFooter",
michael@0 336 viewNode.querySelector(".panel-subview-footer"));
michael@0 337
michael@0 338 let multiView = document.createElement("panelmultiview");
michael@0 339 multiView.setAttribute("nosubviews", "true");
michael@0 340 tempPanel.appendChild(multiView);
michael@0 341 multiView.setAttribute("mainViewIsSubView", "true");
michael@0 342 multiView.setMainView(viewNode);
michael@0 343 viewNode.classList.add("cui-widget-panelview");
michael@0 344 CustomizableUI.addPanelCloseListeners(tempPanel);
michael@0 345
michael@0 346 let panelRemover = function() {
michael@0 347 tempPanel.removeEventListener("popuphidden", panelRemover);
michael@0 348 viewNode.classList.remove("cui-widget-panelview");
michael@0 349 CustomizableUI.removePanelCloseListeners(tempPanel);
michael@0 350 let evt = new CustomEvent("ViewHiding", {detail: viewNode});
michael@0 351 viewNode.dispatchEvent(evt);
michael@0 352 aAnchor.open = false;
michael@0 353
michael@0 354 this.multiView.appendChild(viewNode);
michael@0 355 tempPanel.parentElement.removeChild(tempPanel);
michael@0 356 }.bind(this);
michael@0 357 tempPanel.addEventListener("popuphidden", panelRemover);
michael@0 358
michael@0 359 let iconAnchor =
michael@0 360 document.getAnonymousElementByAttribute(aAnchor, "class",
michael@0 361 "toolbarbutton-icon");
michael@0 362
michael@0 363 tempPanel.openPopup(iconAnchor || aAnchor, "bottomcenter topright");
michael@0 364 }
michael@0 365 },
michael@0 366
michael@0 367 /**
michael@0 368 * Open a dialog window that allow the user to customize listed character sets.
michael@0 369 */
michael@0 370 onCharsetCustomizeCommand: function() {
michael@0 371 this.hide();
michael@0 372 window.openDialog("chrome://global/content/customizeCharset.xul",
michael@0 373 "PrefWindow",
michael@0 374 "chrome,modal=yes,resizable=yes",
michael@0 375 "browser");
michael@0 376 },
michael@0 377
michael@0 378 onWidgetAfterDOMChange: function(aNode, aNextNode, aContainer, aWasRemoval) {
michael@0 379 if (aContainer != this.contents) {
michael@0 380 return;
michael@0 381 }
michael@0 382 if (aWasRemoval) {
michael@0 383 aNode.removeAttribute("auto-hyphens");
michael@0 384 }
michael@0 385 },
michael@0 386
michael@0 387 onWidgetBeforeDOMChange: function(aNode, aNextNode, aContainer, aIsRemoval) {
michael@0 388 if (aContainer != this.contents) {
michael@0 389 return;
michael@0 390 }
michael@0 391 if (!aIsRemoval &&
michael@0 392 (this.panel.state == "open" ||
michael@0 393 document.documentElement.hasAttribute("customizing"))) {
michael@0 394 this._adjustLabelsForAutoHyphens(aNode);
michael@0 395 }
michael@0 396 },
michael@0 397
michael@0 398 /**
michael@0 399 * Signal that we're about to make a lot of changes to the contents of the
michael@0 400 * panels all at once. For performance, we ignore the mutations.
michael@0 401 */
michael@0 402 beginBatchUpdate: function() {
michael@0 403 this._ensureEventListenersAdded();
michael@0 404 this.multiView.ignoreMutations = true;
michael@0 405 },
michael@0 406
michael@0 407 /**
michael@0 408 * Signal that we're done making bulk changes to the panel. We now pay
michael@0 409 * attention to mutations. This automatically synchronizes the multiview
michael@0 410 * container with whichever view is displayed if the panel is open.
michael@0 411 */
michael@0 412 endBatchUpdate: function(aReason) {
michael@0 413 this._ensureEventListenersAdded();
michael@0 414 this.multiView.ignoreMutations = false;
michael@0 415 },
michael@0 416
michael@0 417 _adjustLabelsForAutoHyphens: function(aNode) {
michael@0 418 let toolbarButtons = aNode ? [aNode] :
michael@0 419 this.contents.querySelectorAll(".toolbarbutton-1");
michael@0 420 for (let node of toolbarButtons) {
michael@0 421 let label = node.getAttribute("label");
michael@0 422 if (!label) {
michael@0 423 continue;
michael@0 424 }
michael@0 425 if (label.contains("\u00ad")) {
michael@0 426 node.setAttribute("auto-hyphens", "off");
michael@0 427 } else {
michael@0 428 node.removeAttribute("auto-hyphens");
michael@0 429 }
michael@0 430 }
michael@0 431 },
michael@0 432
michael@0 433 /**
michael@0 434 * Sets the anchor node into the open or closed state, depending
michael@0 435 * on the state of the panel.
michael@0 436 */
michael@0 437 _updatePanelButton: function() {
michael@0 438 this.menuButton.open = this.panel.state == "open" ||
michael@0 439 this.panel.state == "showing";
michael@0 440 },
michael@0 441
michael@0 442 _onHelpViewShow: function(aEvent) {
michael@0 443 // Call global menu setup function
michael@0 444 buildHelpMenu();
michael@0 445
michael@0 446 let helpMenu = document.getElementById("menu_HelpPopup");
michael@0 447 let items = this.getElementsByTagName("vbox")[0];
michael@0 448 let attrs = ["oncommand", "onclick", "label", "key", "disabled"];
michael@0 449 let NSXUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
michael@0 450
michael@0 451 // Remove all buttons from the view
michael@0 452 while (items.firstChild) {
michael@0 453 items.removeChild(items.firstChild);
michael@0 454 }
michael@0 455
michael@0 456 // Add the current set of menuitems of the Help menu to this view
michael@0 457 let menuItems = Array.prototype.slice.call(helpMenu.getElementsByTagName("menuitem"));
michael@0 458 let fragment = document.createDocumentFragment();
michael@0 459 for (let node of menuItems) {
michael@0 460 if (node.hidden)
michael@0 461 continue;
michael@0 462 let button = document.createElementNS(NSXUL, "toolbarbutton");
michael@0 463 // Copy specific attributes from a menuitem of the Help menu
michael@0 464 for (let attrName of attrs) {
michael@0 465 if (!node.hasAttribute(attrName))
michael@0 466 continue;
michael@0 467 button.setAttribute(attrName, node.getAttribute(attrName));
michael@0 468 }
michael@0 469 button.setAttribute("class", "subviewbutton");
michael@0 470 fragment.appendChild(button);
michael@0 471 }
michael@0 472 items.appendChild(fragment);
michael@0 473 },
michael@0 474
michael@0 475 _updateQuitTooltip: function() {
michael@0 476 #ifndef XP_WIN
michael@0 477 #ifdef XP_MACOSX
michael@0 478 let tooltipId = "quit-button.tooltiptext.mac";
michael@0 479 #else
michael@0 480 let tooltipId = "quit-button.tooltiptext.linux2";
michael@0 481 #endif
michael@0 482 let brands = Services.strings.createBundle("chrome://branding/locale/brand.properties");
michael@0 483 let stringArgs = [brands.GetStringFromName("brandShortName")];
michael@0 484
michael@0 485 let key = document.getElementById("key_quitApplication");
michael@0 486 stringArgs.push(ShortcutUtils.prettifyShortcut(key));
michael@0 487 let tooltipString = CustomizableUI.getLocalizedProperty({x: tooltipId}, "x", stringArgs);
michael@0 488 let quitButton = document.getElementById("PanelUI-quit");
michael@0 489 quitButton.setAttribute("tooltiptext", tooltipString);
michael@0 490 #endif
michael@0 491 },
michael@0 492
michael@0 493 _overlayScrollListenerBoundFn: null,
michael@0 494 _overlayScrollListener: function(aMQL) {
michael@0 495 ScrollbarSampler.resetSystemScrollbarWidth();
michael@0 496 this._scrollWidth = null;
michael@0 497 },
michael@0 498 };
michael@0 499
michael@0 500 /**
michael@0 501 * Gets the currently selected locale for display.
michael@0 502 * @return the selected locale or "en-US" if none is selected
michael@0 503 */
michael@0 504 function getLocale() {
michael@0 505 const PREF_SELECTED_LOCALE = "general.useragent.locale";
michael@0 506 try {
michael@0 507 let locale = Services.prefs.getComplexValue(PREF_SELECTED_LOCALE,
michael@0 508 Ci.nsIPrefLocalizedString);
michael@0 509 if (locale)
michael@0 510 return locale;
michael@0 511 }
michael@0 512 catch (e) { }
michael@0 513
michael@0 514 try {
michael@0 515 return Services.prefs.getCharPref(PREF_SELECTED_LOCALE);
michael@0 516 }
michael@0 517 catch (e) { }
michael@0 518
michael@0 519 return "en-US";
michael@0 520 }
michael@0 521

mercurial