bug 441794: merge back to main line
authorDaniel Brooks <db48x@db48x.net>
Thu, 17 Jul 2008 16:51:26 -0500
changeset 64877 f3f4bb4e103e8bd67789f9ed3a7594006b801e9c
parent 64876 38f906d7421ed8a060a82baf0e105b1a5b8b8811 (current diff)
parent 64761 4009ac17c401c554ec9a323f396742638932764f (diff)
child 64878 ad7487807f77902d259d0c45cc390e820b38249a
push id19389
push userffxbld
push dateWed, 06 Apr 2011 21:33:21 +0000
treeherdermozilla-central@8e9f90073a20 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs441794
bug 441794: merge back to main line
mobile/chrome/content/browser-ui.js
mobile/chrome/content/browser.xul
mobile/chrome/content/deckbrowser.css
mobile/chrome/jar.mn
mobile/chrome/skin/browser.css
mobile/chrome/skin/images/mono-sidebar.png
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -33,16 +33,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #filter substitution
 
 pref("toolkit.defaultChromeURI", "chrome://browser/content/browser.xul");
 pref("general.useragent.extra.mobile", "@APP_UA_NAME@/@APP_VERSION@");
+pref("browser.chromeURL", "chrome://browser/content/");
 
 pref("browser.startup.homepage", "http://www.mozilla.org/");
 pref("browser.ui.cursor", false);
 
 /* cache prefs */
 pref("browser.cache.disk.enable", false);
 pref("browser.cache.disk.capacity", 0);
 pref("browser.cache.memory.enable", true);
@@ -118,8 +119,41 @@ pref("extensions.logging.enabled", false
 pref("extensions.update.url", "chrome://mozapps/locale/extensions/extensions.properties");
 pref("extensions.getMoreExtensionsURL", "chrome://mozapps/locale/extensions/extensions.properties");
 pref("extensions.getMoreThemesURL", "chrome://mozapps/locale/extensions/extensions.properties");
 
 /* make clicking on links stand out a bit */
 pref("browser.display.use_focus_colors", true);
 pref("browser.display.focus_background_color", "#ffffa0");
 pref("browser.display.focus_text_color", "#00000");
+
+/* block popups by default, and notify the user about blocked popups */
+pref("dom.disable_open_during_load", true);
+pref("privacy.popups.showBrowserMessage", true);
+
+pref("keyword.enabled", true);
+pref("keyword.URL", "http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q=");
+
+pref("snav.enabled", true);
+
+pref("accessibility.typeaheadfind", false);
+pref("accessibility.typeaheadfind.timeout", 5000);
+pref("accessibility.typeaheadfind.flashBar", 1);
+pref("accessibility.typeaheadfind.linksonly", false);
+pref("accessibility.typeaheadfind.casesensitive", false);
+
+// pointer to the default engine name
+pref("browser.search.defaultenginename",      "chrome://browser/locale/region.properties");
+
+// disable logging for the search service by default
+pref("browser.search.log", false);
+
+// Ordering of Search Engines in the Engine list. 
+pref("browser.search.order.1",                "chrome://browser/locale/region.properties");
+pref("browser.search.order.2",                "chrome://browser/locale/region.properties");
+
+// disable updating
+pref("browser.search.update", false);
+pref("browser.search.update.log", false);
+pref("browser.search.updateinterval", 6);
+
+// enable search suggestions by default
+pref("browser.search.suggest.enabled", true);
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -45,16 +45,17 @@ const PANELMODE_BOOKMARK          = 3;
 const PANELMODE_BOOKMARKLIST      = 4;
 const PANELMODE_SHORTCUTLIST      = 5;
 
 var BrowserUI = {
   _panel : null,
   _caption : null,
   _edit : null,
   _throbber : null,
+  _autocompleteNavbuttons : null,
   _favicon : null,
   _faviconAdded : false,
   _fadeoutID : null,
   _allowHide : true,
 
   _titleChanged : function(aEvent) {
     if (aEvent.target != getBrowser().contentDocument)
       return;
@@ -132,22 +133,23 @@ var BrowserUI = {
 
     return items;
   },
 
   init : function() {
     this._caption = document.getElementById("urlbar-caption");
     this._caption.addEventListener("click", this, false);
     this._edit = document.getElementById("urlbar-edit");
-    this._edit.addEventListener("focus", this, false);
     this._edit.addEventListener("blur", this, false);
-    this._edit.addEventListener("keypress", this, false);
+    this._edit.addEventListener("keypress", this, true);
+    this._edit.addEventListener("input", this, false);
     this._throbber = document.getElementById("urlbar-throbber");
     this._favicon = document.getElementById("urlbar-favicon");
     this._favicon.addEventListener("error", this, false);
+    this._autocompleteNavbuttons = document.getElementById("autocomplete_navbuttons");
 
     getBrowser().addEventListener("DOMTitleChanged", this, true);
     getBrowser().addEventListener("DOMLinkAdded", this, true);
   },
 
   update : function(aState) {
     if (aState == TOOLBARSTATE_INDETERMINATE) {
       this._faviconAdded = false;
@@ -215,45 +217,91 @@ var BrowserUI = {
       this._allowHide = false;
     }
 
     this._caption.value = urlString;
     this._edit.value = urlString;
   },
 
   goToURI : function(aURI) {
+    this._edit.reallyClosePopup();
+
     if (!aURI)
       aURI = this._edit.value;
 
-    if (!this._URIFixup)
-      this._URIFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
-
-    try {
-      aURI = this._URIFixup.createFixupURI(aURI, 0);
-      aURI = this._URIFixup.createExposableURI(aURI);
-    }
-    catch (ex) {
-      aURI = null;
-    }
-
-    if (aURI == null)
-      this.search();
-    else
-      getBrowser().loadURI(aURI.spec, null, null, false);
-
+    var flags = Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+    getBrowser().loadURIWithFlags(aURI, flags, null, null);
     this._showMode(PANELMODE_VIEW);
   },
 
   search : function() {
     var queryURI = "http://www.google.com/search?q=" + this._edit.value + "&hl=en&lr=&btnG=Search";
     getBrowser().loadURI(queryURI, null, null, false);
 
     this._showMode(PANELMODE_VIEW);
   },
 
+  sizeAutocompletePopup : function () {
+    var rect = document.getElementById("browser-container").getBoundingClientRect();
+    var popup = document.getElementById("popup_autocomplete");
+    popup.height = rect.bottom - rect.top;
+  },
+
+  openDefaultHistory : function () {
+    if (!this._edit.value) {
+      this._autocompleteNavbuttons.hidden = true;
+      this._edit.showHistoryPopup();
+    }
+  },
+
+  doButtonSearch : function(button)
+  {
+    if (!("engine" in button) || !button.engine)
+      return;
+
+    var urlbar = this._edit;
+    urlbar.open = false;
+    var value = urlbar.value;
+    if (!value)
+      return;
+
+    var submission = button.engine.getSubmission(value, null);
+    getBrowser().loadURI(submission.uri.spec, null, submission.postData, false);
+  },
+
+  engines : null,
+  updateSearchEngines : function () {
+    if (this.engines)
+      return;
+
+    // XXXndeakin remove the try-catch once the search service is properly built
+    try {
+      var searchService = Cc["@mozilla.org/browser/search-service;1"].
+                          getService(Ci.nsIBrowserSearchService);
+    } catch (ex) {
+      this.engines = [ ];
+      return;
+    }
+
+    var engines = searchService.getVisibleEngines({ });
+    this.engines = engines;
+    const kXULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+    var container = this._autocompleteNavbuttons;
+    for (var e = 0; e < engines.length; e++) {
+      var button = document.createElementNS(kXULNS, "toolbarbutton");
+      var engine = engines[e];
+      button.id = engine.name;
+      button.setAttribute("label", engine.name);
+      if (engine.iconURI)
+        button.setAttribute("image", engine.iconURI.spec);
+      container.insertBefore(button, container.firstChild);
+      button.engine = engine;
+    }
+  },
+
   _showMode : function(aMode) {
     if (this._fadeoutID) {
       clearTimeout(this._fadeoutID);
       this._fadeoutID = null;
     }
 
     var toolbar = document.getElementById("toolbar-main");
     var bookmark = document.getElementById("bookmark-container");
@@ -265,21 +313,19 @@ var BrowserUI = {
       bookmark.hidden = true;
       urllist.hidden = true;
     }
     else if (aMode == PANELMODE_EDIT) {
       toolbar.setAttribute("mode", "edit");
       this._caption.hidden = true;
       this._edit.hidden = false;
       this._edit.focus();
-
-      this.showHistory();
-
       bookmark.hidden = true;
-      urllist.hidden = false;
+      urllist.hidden = true;
+      this.openDefaultHistory();
     }
     else if (aMode == PANELMODE_BOOKMARK) {
       toolbar.setAttribute("mode", "view");
       this._edit.hidden = true;
       this._caption.hidden = false;
 
       urllist.hidden = true;
       bookmark.hidden = false;
@@ -363,24 +409,27 @@ var BrowserUI = {
         break;
       case "DOMLinkAdded":
         this._linkAdded(aEvent);
         break;
       // URL textbox events
       case "click":
         this._showMode(PANELMODE_EDIT);
         break;
-      case "focus":
-        setTimeout(function() { aEvent.target.select(); }, 0);
+      case "input":
+        if (this._edit.value) {
+          this.updateSearchEngines();
+          this._autocompleteNavbuttons.hidden = false;
+        }
         break;
       case "keypress":
-        if (aEvent.keyCode == aEvent.DOM_VK_RETURN)
-          this.goToURI();
-        if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
+        if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
+          this._edit.reallyClosePopup();
           this._showMode(PANELMODE_VIEW);
+        }
         break;
       // Favicon events
       case "error":
         this._favicon.setAttribute("src", "chrome://browser/skin/images/default-favicon.png");
         break;
     }
   },
 
@@ -466,47 +515,64 @@ var BrowserUI = {
 };
 
 var BookmarkHelper = {
   _item : null,
   _uri : null,
   _bmksvc : null,
   _tagsvc : null,
 
+  _getTagsArrayFromTagField : function() {
+    // we don't require the leading space (after each comma)
+    var tags = document.getElementById("bookmark-tags").value.split(",");
+    for (var i=0; i<tags.length; i++) {
+      // remove trailing and leading spaces
+      tags[i] = tags[i].replace(/^\s+/, "").replace(/\s+$/, "");
+
+      // remove empty entries from the array.
+      if (tags[i] == "") {
+        tags.splice(i, 1);
+        i--;
+      }
+    }
+    return tags;
+  },
+
   edit : function(aURI) {
     this._bmksvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].getService(Ci.nsINavBookmarksService);
     this._tagsvc = Cc["@mozilla.org/browser/tagging-service;1"].getService(Ci.nsITaggingService);
 
     this._uri = aURI;
     var bookmarkIDs = this._bmksvc.getBookmarkIdsForURI(this._uri, {});
     if (bookmarkIDs.length > 0) {
       this._item = bookmarkIDs[0];
       document.getElementById("bookmark-name").value = this._bmksvc.getItemTitle(this._item);
       var currentTags = this._tagsvc.getTagsForURI(this._uri, {});
-      document.getElementById("bookmark-tags").value = currentTags.join(" ");
+      document.getElementById("bookmark-tags").value = currentTags.join(", ");
     }
+
+    window.addEventListener("keypress", this, true);
   },
 
   remove : function() {
     if (this._item) {
       this._bmksvc.removeItem(this._item);
       document.getElementById("tool-star").removeAttribute("starred");
     }
     this.close();
   },
 
   save : function() {
     if (this._item) {
       // Update the name
       this._bmksvc.setItemTitle(this._item, document.getElementById("bookmark-name").value);
 
       // Update the tags
-      var taglist = document.getElementById("hudbookmark-tags").value;
+      var tags = this._getTagsArrayFromTagField();
       var currentTags = this._tagsvc.getTagsForURI(this._uri, {});
-      var tags = taglist.split(" ");
       if (tags.length > 0 || currentTags.length > 0) {
         var tagsToRemove = [];
         var tagsToAdd = [];
         var i;
         for (i=0; i<currentTags.length; i++) {
           if (tags.indexOf(currentTags[i]) == -1)
             tagsToRemove.push(currentTags[i]);
         }
@@ -521,12 +587,22 @@ var BookmarkHelper = {
           this._tagsvc.untagURI(this._uri, tagsToRemove);
       }
 
     }
     this.close();
   },
 
   close : function() {
+    window.removeEventListener("keypress", this, true);
     this._item = null;
     BrowserUI.hide();
+  },
+
+  handleEvent: function (aEvent) {
+    switch (aEvent.type) {
+      case "keypress":
+        if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE)
+          this.close();
+        break;
+    }
   }
 };
rename from mobile/chrome/content/deckbrowser.css
rename to mobile/chrome/content/browser.css
--- a/mobile/chrome/content/deckbrowser.css
+++ b/mobile/chrome/content/browser.css
@@ -1,3 +1,7 @@
 deckbrowser {
     -moz-binding: url("chrome://browser/content/deckbrowser.xml#deckbrowser");
 }
+
+#urlbar-edit {
+    -moz-binding: url("chrome://browser/content/urlbar.xml#autocomplete-aligned");
+}
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -38,16 +38,30 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
+const FINDSTATE_FIND = 0;
+const FINDSTATE_FIND_AGAIN = 1;
+const FINDSTATE_FIND_PREVIOUS = 2;
+
+Cu.import("resource://gre/modules/SpatialNavigation.js");
+
+// create a lazy-initialized handle for the pref service on the global object
+// in the style of bug 385809
+__defineGetter__("gPrefService", function () {
+  delete gPrefService;
+  return gPrefService = Components.classes["@mozilla.org/preferences-service;1"]
+                                  .getService(Components.interfaces.nsIPrefBranch2);
+});
+
 function getBrowser() {
   return Browser.content.browser;
 }
 
 var Browser = {
   _content : null,
 
   _titleChanged : function(aEvent) {
@@ -76,21 +90,26 @@ var Browser = {
     }
 
     // load styles for scrollbars
     var styleURI = ios.newURI("chrome://browser/content/scrollbars.css", null, null);
     styleSheets.loadAndRegisterSheet(styleURI, styleSheets.AGENT_SHEET);
 
     this._content = document.getElementById("content");
     this._content.addEventListener("DOMTitleChanged", this, true);
-
+    this._content.addEventListener("overpan", this, false);
+    this._content.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver.onUpdatePageReport, false);
     BrowserUI.init();
 
     this._progressController = new ProgressController(this.content);
 
+    this._spatialNavigation  = new SpatialNavigation(this.content);
+
+    this.setupGeolocationPrompt();
+
     Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
 
     // Determine the initial launch page
     var whereURI = null;
     try {
       // Use a homepage
       whereURI = this.prefs.getCharPref("browser.startup.homepage");
     }
@@ -115,31 +134,84 @@ var Browser = {
     }
 
     if (whereURI) {
       var self = this;
       setTimeout(function() { self.content.browser.loadURI(whereURI, null, null, false); }, 10);
     }
   },
 
+  setupGeolocationPrompt: function() {
+    var geolocationService = Cc["@mozilla.org/geolocation/service;1"].getService(Ci.nsIGeolocationService);
+    geolocationService.prompt = function(request) {
+
+      var notificationBox = Browser.getNotificationBox();
+      var notification = notificationBox.getNotificationWithValue("geolocation");
+
+      if (!notification) {
+        var bundle_browser = document.getElementById("bundle_browser");
+        var buttons = [{
+            label: bundle_browser.getString("gelocation.exactLocation"),
+            accessKey: bundle_browser.getString("gelocation.exactLocationKey"),
+            callback: function(){request.allow()},
+          },
+          {
+            label: bundle_browser.getString("gelocation.neighborhoodLocation"),
+            accessKey: bundle_browser.getString("gelocation.neighborhoodLocationKey"),
+            callback: function(){request.allowButFuzz()},
+          },
+          {
+            label: bundle_browser.getString("gelocation.nothingLocation"),
+            accessKey: bundle_browser.getString("gelocation.nothingLocationKey"),
+            callback: function(){request.cancel()},
+          }];
+
+        var message = bundle_browser.getFormattedString("geolocation.requestMessage", [request.requestingURI.spec]);      
+        notificationBox.appendNotification(message,
+                                           "geolocation",
+                                           null, // todo "chrome://browser/skin/Info.png",
+                                           notificationBox.PRIORITY_INFO_HIGH,
+                                           buttons);
+        return 1;
+      }
+    };
+  },
+
   get content() {
     return this._content;
   },
 
+  /**
+   * Return the currently active <browser> object, since a <deckbrowser> may
+   * have more than one
+   */
+  get currentBrowser() {
+    return this._content.browser;
+  },
+
   handleEvent: function (aEvent) {
     switch (aEvent.type) {
       case "DOMTitleChanged":
         this._titleChanged(aEvent);
         break;
+      case "overpan":
+        // Open the sidebar controls if we get a right side overpan
+        if (aEvent.detail == 2)
+          document.getElementById("browser-controls").collapsed = false;
+        // Close the sidebar controls if we get a left side overpan
+        else if (aEvent.detail == 1)
+          document.getElementById("browser-controls").collapsed = true;
+        break;
     }
   },
 
   supportsCommand : function(cmd) {
     var isSupported = false;
     switch (cmd) {
+      case "cmd_menu":
       case "cmd_fullscreen":
       case "cmd_addons":
       case "cmd_downloads":
         isSupported = true;
         break;
       default:
         isSupported = false;
         break;
@@ -148,20 +220,24 @@ var Browser = {
   },
 
   isCommandEnabled : function(cmd) {
     return true;
   },
 
   doCommand : function(cmd) {
     var browser = this.content.browser;
+    var controls = document.getElementById("browser-controls");
 
     switch (cmd) {
+      case "cmd_menu":
+        controls.collapsed = !controls.collapsed;
+        break;
       case "cmd_fullscreen":
-        window.fullScreen = window.fullScreen ? false : true;
+        window.fullScreen = !window.fullScreen;
         break;
       case "cmd_addons":
       {
         const EMTYPE = "Extension:Manager";
 
         var aOpenMode = "extensions";
         var wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator);
         var needToOpen = true;
@@ -182,17 +258,48 @@ var Browser = {
           window.openDialog(EMURL, "", EMFEATURES);
         }
         break;
       }
       case "cmd_downloads":
         Cc["@mozilla.org/download-manager-ui;1"].getService(Ci.nsIDownloadManagerUI).show(window);
         break;
     }
-  }
+  },
+
+  getNotificationBox : function() {
+    return document.getElementById("notifications");
+  },
+
+
+  findState: FINDSTATE_FIND,
+  openFind: function(aState) {
+    this.findState = aState;
+
+    var findbar = document.getElementById("findbar");
+    var browser = findbar.browser;
+    if (!browser) {
+      browser = this.content.browser;
+      findbar.browser = browser;
+    }
+
+    var panel = document.getElementById("findpanel");
+    if (panel.state == "open")
+      this.doFind(null);
+    else
+      panel.openPopup(document.getElementById("findpanel-placeholder"), "before_start");
+  },
+
+  doFind: function (aEvent) {
+    var findbar = document.getElementById("findbar");
+    if (Browser.findState == FINDSTATE_FIND)
+      findbar.onFindCommand();
+    else
+      findbar.onFindAgainCommand(Browser.findState == FINDSTATE_FIND_PREVIOUS);
+   }
 };
 
 function ProgressController(aTabBrowser) {
   this._tabbrowser = aTabBrowser;
   this.init(aTabBrowser.browser);
 }
 
 ProgressController.prototype = {
@@ -221,35 +328,64 @@ ProgressController.prototype = {
           this._tabbrowser.updateCanvasState();
         }
       }
     }
 
     if (aStateFlags & Ci.nsIWebProgressListener.STATE_IS_DOCUMENT) {
       if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
         aWebProgress.DOMWindow.focus();
-        this._tabbrowser.updateCanvasState();
+        this._tabbrowser.updateCanvasState(true);
         //aWebProgress.DOMWindow.scrollbars.visible = false;
       }
     }
   },
 
   // This method is called to indicate progress changes for the currently
   // loading page.
   onProgressChange : function(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
   },
 
   // This method is called to indicate a change to the current location.
-  onLocationChange : function(aWebProgress, aRequest, aLocation) {
-    
+  onLocationChange : function(aWebProgress, aRequest, aLocationURI) {
+
+    var location = aLocationURI ? aLocationURI.spec : "";
     this._hostChanged = true;
-    
+
+    // This code here does not compare uris exactly when determining
+    // whether or not the message(s) should be hidden since the message
+    // may be prematurely hidden when an install is invoked by a click
+    // on a link that looks like this:
+    //
+    // <a href="#" onclick="return install();">Install Foo</a>
+    //
+    // - which fires a onLocationChange message to uri + '#'...
+    cBrowser = Browser.currentBrowser;
+    if (cBrowser.lastURI) {
+      var oldSpec = cBrowser.lastURI.spec;
+      var oldIndexOfHash = oldSpec.indexOf("#");
+      if (oldIndexOfHash != -1)
+        oldSpec = oldSpec.substr(0, oldIndexOfHash);
+      var newSpec = location;
+      var newIndexOfHash = newSpec.indexOf("#");
+      if (newIndexOfHash != -1)
+        newSpec = newSpec.substr(0, newSpec.indexOf("#"));
+      if (newSpec != oldSpec) {
+        // Remove all the notifications, except for those which want to
+        // persist across the first location change.
+        var nBox = Browser.getNotificationBox();
+        nBox.removeTransientNotifications();
+      }
+    }
+    cBrowser.lastURI = aLocationURI;
+
+
     if (aWebProgress.DOMWindow == this._browser.contentWindow) {
       BrowserUI.setURI();
-      this._tabbrowser.updateCanvasState(true);
+      this._tabbrowser.updateCanvasState();
     }
   },
 
   // This method is called to indicate a status changes for the currently
   // loading page.  The message is already formatted for display.
   onStatusChange : function(aWebProgress, aRequest, aStatus, aMessage) {
     this._tabbrowser.updateCanvasState();
   },
@@ -273,32 +409,32 @@ ProgressController.prototype = {
     try {
       this._host = getBrowser().contentWindow.location.host;
     } catch(ex) {
       this._host = null;
     }
 
     this._hostChanged = false;
 
-    // Don't pass in the actual location object, since it can cause us to 
+    // Don't pass in the actual location object, since it can cause us to
     // hold on to the window object too long.  Just pass in the fields we
     // care about. (bug 424829)
     var location = getBrowser().contentWindow.location;
     var locationObj = {};
     try {
       locationObj.host = location.host;
       locationObj.hostname = location.hostname;
       locationObj.port = location.port;
     } catch (ex) {
       // Can sometimes throw if the URL being visited has no host/hostname,
       // e.g. about:blank. The _state for these pages means we won't need these
       // properties anyways, though.
     }
     getIdentityHandler().checkIdentity(this._state, locationObj);
-    
+
   },
 
   QueryInterface : function(aIID) {
     if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
         aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
         aIID.equals(Components.interfaces.nsISupports))
       return this;
 
@@ -308,23 +444,23 @@ ProgressController.prototype = {
 
 /**
  * Utility class to handle manipulations of the identity indicators in the UI
  */
 function IdentityHandler() {
   this._stringBundle = document.getElementById("bundle_browser");
   this._staticStrings = {};
   this._staticStrings[this.IDENTITY_MODE_DOMAIN_VERIFIED] = {
-    encryption_label: this._stringBundle.getString("identity.encrypted")  
+    encryption_label: this._stringBundle.getString("identity.encrypted")
   };
   this._staticStrings[this.IDENTITY_MODE_IDENTIFIED] = {
     encryption_label: this._stringBundle.getString("identity.encrypted")
   };
   this._staticStrings[this.IDENTITY_MODE_UNKNOWN] = {
-    encryption_label: this._stringBundle.getString("identity.unencrypted")  
+    encryption_label: this._stringBundle.getString("identity.unencrypted")
   };
 
   this._cacheElements();
 }
 
 IdentityHandler.prototype = {
 
   // Mode strings used to control CSS display
@@ -353,110 +489,110 @@ IdentityHandler.prototype = {
   /**
    * Handler for mouseclicks on the "More Information" button in the
    * "identity-popup" panel.
    */
   handleMoreInfoClick : function(event) {
     displaySecurityInfo();
     event.stopPropagation();
   },
-  
+
   /**
    * Helper to parse out the important parts of _lastStatus (of the SSL cert in
    * particular) for use in constructing identity UI strings
   */
   getIdentityData : function() {
     var result = {};
     var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
     var cert = status.serverCert;
-    
+
     // Human readable name of Subject
     result.subjectOrg = cert.organization;
-    
+
     // SubjectName fields, broken up for individual access
     if (cert.subjectName) {
       result.subjectNameFields = {};
       cert.subjectName.split(",").forEach(function(v) {
         var field = v.split("=");
         this[field[0]] = field[1];
       }, result.subjectNameFields);
-      
+
       // Call out city, state, and country specifically
       result.city = result.subjectNameFields.L;
       result.state = result.subjectNameFields.ST;
       result.country = result.subjectNameFields.C;
     }
-    
+
     // Human readable name of Certificate Authority
     result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
     result.cert = cert;
-    
+
     return result;
   },
-  
+
   /**
    * Determine the identity of the page being displayed by examining its SSL cert
    * (if available) and, if necessary, update the UI to reflect this.  Intended to
    * be called by onSecurityChange
-   * 
+   *
    * @param PRUint32 state
    * @param JS Object location that mirrors an nsLocation (i.e. has .host and
    *                           .hostname and .port)
    */
   checkIdentity : function(state, location) {
     var currentStatus = getBrowser().securityUI
                                 .QueryInterface(Components.interfaces.nsISSLStatusProvider)
                                 .SSLStatus;
     this._lastStatus = currentStatus;
     this._lastLocation = location;
-    
+
     if (state & Components.interfaces.nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
       this.setMode(this.IDENTITY_MODE_IDENTIFIED);
     else if (state & Components.interfaces.nsIWebProgressListener.STATE_SECURE_HIGH)
       this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
     else
       this.setMode(this.IDENTITY_MODE_UNKNOWN);
   },
-  
+
   /**
    * Return the eTLD+1 version of the current hostname
    */
   getEffectiveHost : function() {
     // Cache the eTLDService if this is our first time through
     if (!this._eTLDService)
       this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
                          .getService(Ci.nsIEffectiveTLDService);
     try {
       return this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
     } catch (e) {
       // If something goes wrong (e.g. hostname is an IP address) just fail back
       // to the full domain.
       return this._lastLocation.hostname;
     }
   },
-  
+
   /**
    * Update the UI to reflect the specified mode, which should be one of the
    * IDENTITY_MODE_* constants.
    */
   setMode : function(newMode) {
     if (!this._identityBox) {
       // No identity box means the identity box is not visible, in which
       // case there's nothing to do.
       return;
     }
 
     this._identityBox.className = newMode;
     this.setIdentityMessages(newMode);
-    
+
     // Update the popup too, if it's open
     if (this._identityPopup.state == "open")
       this.setPopupMessages(newMode);
   },
-  
+
   /**
    * Set up the messages for the primary identity UI based on the specified mode,
    * and the details of the SSL cert, where applicable
    *
    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
   setIdentityMessages : function(newMode) {
     if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
@@ -472,132 +608,213 @@ IdentityHandler.prototype = {
       if (!this._overrideService)
         this._overrideService = Components.classes["@mozilla.org/security/certoverride;1"]
                                           .getService(Components.interfaces.nsICertOverrideService);
 
       // Verifier is either the CA Org, for a normal cert, or a special string
       // for certs that are trusted because of a security exception.
       var tooltip = this._stringBundle.getFormattedString("identity.identified.verifier",
                                                           [iData.caOrg]);
-      
+
       // Check whether this site is a security exception. XPConnect does the right
       // thing here in terms of converting _lastLocation.port from string to int, but
       // the overrideService doesn't like undefined ports, so make sure we have
       // something in the default case (bug 432241).
-      if (this._overrideService.hasMatchingOverride(this._lastLocation.hostname, 
+      if (this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
                                                     (this._lastLocation.port || 443),
                                                     iData.cert, {}, {}))
         tooltip = this._stringBundle.getString("identity.identified.verified_by_you");
     }
     else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
       // If it's identified, then we can populate the dialog with credentials
-      iData = this.getIdentityData();  
+      iData = this.getIdentityData();
       tooltip = this._stringBundle.getFormattedString("identity.identified.verifier",
                                                       [iData.caOrg]);
     }
     else {
       tooltip = this._stringBundle.getString("identity.unknown.tooltip");
     }
-    
+
     // Push the appropriate strings out to the UI
     this._identityBox.tooltipText = tooltip;
   },
-  
+
   /**
    * Set up the title and content messages for the identity message popup,
    * based on the specified mode, and the details of the SSL cert, where
    * applicable
    *
    * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
    */
   setPopupMessages : function(newMode) {
-      
+
     this._identityPopup.className = newMode;
     this._identityPopupContentBox.className = newMode;
-    
+
     // Set the static strings up front
     this._identityPopupEncLabel.textContent = this._staticStrings[newMode].encryption_label;
-    
+
     // Initialize the optional strings to empty values
     var supplemental = "";
     var verifier = "";
-    
+
     if (newMode == this.IDENTITY_MODE_DOMAIN_VERIFIED) {
       var iData = this.getIdentityData();
       var host = this.getEffectiveHost();
       var owner = this._stringBundle.getString("identity.ownerUnknown2");
       verifier = this._identityBox.tooltipText;
       supplemental = "";
     }
     else if (newMode == this.IDENTITY_MODE_IDENTIFIED) {
       // If it's identified, then we can populate the dialog with credentials
       iData = this.getIdentityData();
       host = this.getEffectiveHost();
-      owner = iData.subjectOrg; 
+      owner = iData.subjectOrg;
       verifier = this._identityBox.tooltipText;
 
       // Build an appropriate supplemental block out of whatever location data we have
       if (iData.city)
-        supplemental += iData.city + "\n";        
+        supplemental += iData.city + "\n";
       if (iData.state && iData.country)
         supplemental += this._stringBundle.getFormattedString("identity.identified.state_and_country",
                                                               [iData.state, iData.country]);
       else if (iData.state) // State only
         supplemental += iData.state;
       else if (iData.country) // Country only
         supplemental += iData.country;
     }
     else {
       // These strings will be hidden in CSS anyhow
       host = "";
       owner = "";
     }
-    
+
     // Push the appropriate strings out to the UI
     this._identityPopupContentHost.textContent = host;
     this._identityPopupContentOwner.textContent = owner;
     this._identityPopupContentSupp.textContent = supplemental;
     this._identityPopupContentVerif.textContent = verifier;
   },
 
   hideIdentityPopup : function() {
     this._identityPopup.hidePopup();
   },
 
   /**
-   * Click handler for the identity-box element in primary chrome.  
+   * Click handler for the identity-box element in primary chrome.
    */
   handleIdentityButtonEvent : function(event) {
-  
+
     event.stopPropagation();
- 
+
     if ((event.type == "click" && event.button != 0) ||
         (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
          event.keyCode != KeyEvent.DOM_VK_RETURN))
       return; // Left click, space or enter only
 
     // Make sure that the display:none style we set in xul is removed now that
     // the popup is actually needed
     this._identityPopup.hidden = false;
-    
+
     // Tell the popup to consume dismiss clicks, to avoid bug 395314
     this._identityPopup.popupBoxObject
         .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
-    
+
     // Update the popup strings
     this.setPopupMessages(this._identityBox.className);
-    
+
     // Now open the popup, anchored off the primary chrome element
     this._identityPopup.openPopup(this._identityBox, 'after_start');
   }
 };
 
-var gIdentityHandler; 
+var gIdentityHandler;
 
 /**
  * Returns the singleton instance of the identity handler class.  Should always be
  * used instead of referencing the global variable directly or creating new instances
  */
 function getIdentityHandler() {
   if (!gIdentityHandler)
     gIdentityHandler = new IdentityHandler();
-  return gIdentityHandler;    
+  return gIdentityHandler;
 }
+
+
+/**
+ * Handler for blocked popups, triggered by DOMUpdatePageReport events in browser.xml
+ */
+const gPopupBlockerObserver = {
+  _kIPM: Components.interfaces.nsIPermissionManager,
+
+  onUpdatePageReport: function (aEvent)
+  {
+    var cBrowser = Browser.currentBrowser;
+    if (aEvent.originalTarget != cBrowser)
+      return;
+
+    if (!cBrowser.pageReport)
+      return;
+
+    // Only show the notification again if we've not already shown it. Since
+    // notifications are per-browser, we don't need to worry about re-adding
+    // it.
+    if (!cBrowser.pageReport.reported) {
+      if(gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
+        var bundle_browser = document.getElementById("bundle_browser");
+        var brandBundle = document.getElementById("bundle_brand");
+        var brandShortName = brandBundle.getString("brandShortName");
+        var message;
+        var popupCount = cBrowser.pageReport.length;
+
+        if (popupCount > 1)
+          message = bundle_browser.getFormattedString("popupWarningMultiple", [brandShortName, popupCount]);
+        else
+          message = bundle_browser.getFormattedString("popupWarning", [brandShortName]);
+
+        var notificationBox = Browser.getNotificationBox();
+        var notification = notificationBox.getNotificationWithValue("popup-blocked");
+        if (notification) {
+          notification.label = message;
+        }
+        else {
+          var buttons = [
+            {
+              label: bundle_browser.getString("popupButtonAlwaysAllow"),
+              accessKey: bundle_browser.getString("popupButtonAlwaysAllow.accesskey"),
+              callback: function() { gPopupBlockerObserver.toggleAllowPopupsForSite(); }
+            },
+            {
+              label: bundle_browser.getString("popupButtonNeverWarn"),
+              accessKey: bundle_browser.getString("popupButtonNeverWarn.accesskey"),
+              callback: function() { gPopupBlockerObserver.dontShowMessage(); }
+            }
+          ];
+
+          const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+          notificationBox.appendNotification(message, "popup-blocked",
+                                             "",
+                                             priority, buttons);
+        }
+      }
+      // Record the fact that we've reported this blocked popup, so we don't
+      // show it again.
+      cBrowser.pageReport.reported = true;
+    }
+  },
+
+  toggleAllowPopupsForSite: function (aEvent)
+  {
+    var currentURI = Browser.currentBrowser.webNavigation.currentURI;
+    var pm = Components.classes["@mozilla.org/permissionmanager;1"]
+                       .getService(this._kIPM);
+    pm.add(currentURI, "popup", this._kIPM.ALLOW_ACTION);
+
+    Browser.getNotificationBox().removeCurrentNotification();
+  },
+
+  dontShowMessage: function ()
+  {
+    var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
+    gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
+    Browser.getNotificationBox().removeCurrentNotification();
+  }
+};
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -34,60 +34,73 @@
    - decision by deleting the provisions above and replace them with the notice
    - and other provisions required by the LGPL or the GPL. If you do not delete
    - the provisions above, a recipient may use your version of this file under
    - the terms of any one of the MPL, the GPL or the LGPL.
    -
    - ***** END LICENSE BLOCK ***** -->
 
 <?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
-<?xml-stylesheet href="chrome://browser/content/deckbrowser.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
 
 <!DOCTYPE window [
 <!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
 %browserDTD;
 <!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
 %brandDTD;
 ]>
 
 <window id="main-window" title="&brandShortName;"
         width="800" height="480"
         onload="Browser.startup();"
         windowtype="navigator:browser"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <stringbundleset id="stringbundleset">
     <stringbundle id="bundle_browser" src="chrome://browser/locale/browser.properties"/>
+    <stringbundle id="bundle_brand"  src="chrome://branding/locale/brand.properties"/>
     <stringbundle id="bundle-keys" src="chrome://global/locale/keys.properties"/>
     <stringbundle id="bundle-platformKeys" src="chrome://global-platform/locale/platformKeys.properties"/>
   </stringbundleset>
   <stringbundleset id="shortcut-bundles">
     <stringbundle src="chrome://browser/locale/shortcuts.properties"/>
   </stringbundleset>
 
   <script type="application/x-javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
   <script type="application/x-javascript" src="chrome://browser/content/commandUtil.js"/>
   <script type="application/x-javascript" src="chrome://browser/content/browser.js"/>
   <script type="application/x-javascript" src="chrome://browser/content/browser-ui.js"/>
   <script type="application/x-javascript;version=1.8" src="chrome://browser/content/shortcuts.js"/>
 
+  <stringbundleset id="stringbundleset">
+    <stringbundle id="bundle_browser"  src="chrome://browser/locale/browser.properties"/>
+    <stringbundle id="bundle_brand"  src="chrome://branding/locale/brand.properties"/>
+    <stringbundle id="bundle-keys" src="chrome://global/locale/keys.properties"/>
+    <stringbundle id="bundle-platformKeys" src="chrome://global-platform/locale/platformKeys.properties"/>
+  </stringbundleset>
+
+  <stringbundleset id="stringbundleset">
+    <stringbundle id="bundle_browser"  src="chrome://browser/locale/browser.properties"/>
+  </stringbundleset>
+
   <commandset id="cmdset_main">
     <!-- basic navigation -->
     <command id="cmd_back" label="&back.label;" disabled="true" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_forward" label="&forward.label;" disabled="true" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_reload" label="&reload.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_stop" label="&stop.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_search" label="&search.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_go" label="&go.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- bookmarking -->
     <command id="cmd_star" label="&star.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_bookmarks" label="&bookmarks.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- misc -->
+    <command id="cmd_menu" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_fullscreen" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_addons" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_downloads" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_shortcuts" oncommand="CommandUpdater.doCommand(this.id);"/>
 
     <!-- scrolling -->
     <command id="cmd_scrollPageUp" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_scrollToBeginning" oncommand="CommandUpdater.doCommand(this.id);"/>
@@ -96,39 +109,58 @@
 
     <!-- editing -->
     <command id="cmd_cut" label="&cut.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_copy" label="&copy.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_copylink" label="&copylink.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_paste" label="&paste.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_delete" label="&delete.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
     <command id="cmd_selectAll" label="&selectAll.label;" oncommand="CommandUpdater.doCommand(this.id);"/>
+
+    <command id="cmd_find" oncommand="Browser.openFind(FINDSTATE_FIND);"/>
+    <command id="cmd_findAgain" oncommand="Browser.openFind(FINDSTATE_FIND_AGAIN);"/>
+    <command id="cmd_findPrevious" oncommand="Browser.openFind(FINDSTATE_FIND_PREVIOUS);"/>
   </commandset>
 
   <keyset id="mainKeyset">
     <!-- basic navigation -->
     <key id="key_back" keycode="VK_LEFT" command="cmd_back" modifiers="control"/>
     <key id="key_forward" keycode="VK_RIGHT" command="cmd_forward" modifiers="control"/>
     <key id="key_back2" keycode="VK_BACK" command="cmd_back"/>
     <key id="key_forward2" keycode="VK_BACK" command="cmd_forward" modifiers="shift"/>
     <key id="key_reload" keycode="VK_F5" command="cmd_reload"/>
 
     <!-- scrolling -->
     <key id="key_pageUp" keycode="VK_UP" command="cmd_scrollPageUp" modifiers="shift"/>
     <key id="key_pageDown" keycode="VK_DOWN" command="cmd_scrollPageDown" modifiers="shift"/>
 
     <!-- misc -->
+    <key id="key_menu" keycode="VK_F4" command="cmd_menu"/>
     <key id="key_fullscreen" keycode="VK_F6" command="cmd_fullscreen"/>
     <key id="key_addons" key="E" command="cmd_addons" modifiers="accel"/>
     <key id="key_downloads" key="J" command="cmd_downloads" modifiers="control"/>
+
+    <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
+    <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
+    <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/>
+    <key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/>
+    <key keycode="&findAgainCmd.commandkey2;"  command="cmd_findPrevious" modifiers="shift"/>
   </keyset>
 
   <popupset id="mainPopupSet">
+    <panel type="autocomplete-richlistbox" id="popup_autocomplete"
+           noautofocus="true" onpopupshowing="BrowserUI.sizeAutocompletePopup()">
+      <hbox id="autocomplete_navbuttons" align="center" flex="1"
+            oncommand="BrowserUI.doButtonSearch(event.target);">
+        <image class="tool-search"/>
+      </hbox>
+    </panel>
+
     <!-- popup for content autocomplete -->
-    <panel type="autocomplete" id="popup_autocomplete" noautofocus="true"/>
+    <panel type="autocomplete" id="popup_autocomplete_content" noautofocus="true"/>
 
     <!-- popup for site identity information -->
     <panel id="identity-popup" position="after_start" hidden="true" noautofocus="true"
            norestorefocus="true">
       <hbox id="identity-popup-container" align="top">
         <image id="identity-popup-icon"/>
         <vbox id="identity-popup-content-box">
           <label id="identity-popup-connectedToLabel" value="&identity.connectedTo;"/>
@@ -157,52 +189,61 @@
              onclick="getIdentityHandler().handleIdentityButtonEvent(event);"
              onkeypress="getIdentityHandler().handleIdentityButtonEvent(event);">
           <stack id="urlbar-image-stack">
             <image id="urlbar-throbber" src=""/>
             <image id="urlbar-favicon" src=""/>
           </stack>
         </box>
         <description id="urlbar-caption" crop="end" flex="1"/>
-        <textbox id="urlbar-edit" flex="1" hidden="true"/>
+        <textbox id="urlbar-edit"
+                 type="autocomplete"
+                 autocompletesearch="history"
+                 enablehistory="false"
+                 maxrows="6"
+                 completeselectedindex="true"
+                 minresultsforpopup="0"
+                 flex="1"
+                 hidden="true"
+                 autocompletepopup="popup_autocomplete"
+                 ontextentered="BrowserUI.goToURI();"
+                 clickSelectsAll="true"/>
       </hbox>
       <hbox id="urlbar-icons">
         <toolbarbutton id="tool-reload" class="urlbar-icon-button" command="cmd_reload"/>
         <toolbarbutton id="tool-stop" class="urlbar-icon-button" command="cmd_stop"/>
         <toolbarbutton id="tool-go" class="urlbar-icon-button" command="cmd_go"/>
       </hbox>
     </toolbar>
   </toolbox>
 
   <stack id="ui-stack" flex="1">
     <hbox id="browser-container" flex="1">
       <vbox id="browser" flex="1">
-        <deckbrowser id="content" autocompletepopup="popup_autocomplete" flex="1"/>
+        <notificationbox id="notifications" flex="1">
+          <deckbrowser id="content" autocompletepopup="popup_autocomplete_content" flex="1"/>
+        </notificationbox>
       </vbox>
-      <vbox id="browser-controls">
+      <vbox id="browser-controls" collapsed="true">
         <toolbarbutton id="tool-back" class="browser-control-button" command="cmd_back"/>
         <toolbarbutton id="tool-forward" class="browser-control-button" command="cmd_forward"/>
         <toolbarbutton id="tool-star" class="browser-control-button" command="cmd_star"/>
         <toolbarbutton id="tool-bookmarks" class="browser-control-button" command="cmd_bookmarks"/>
         <toolbarbutton id="tool-shortcuts" class="browser-control-button" command="cmd_shortcuts"/>
         <toolbarbutton id="tool-actions" class="browser-control-button" command="cmd_actions"/>
       </vbox>
     </hbox>
 
-    <vbox id="notifcationbox-container" flex="1" hidden="true">
-      <notifcationbox id="notifications"/>
-    </vbox>
-
     <vbox id="urllist-container" flex="1" hidden="true">
       <hbox id="urllist-items-container" flex="1">
         <richlistbox id="urllist-items" flex="1"/>
       </hbox>
       <separator class="thin"/>
       <hbox id="urllist-search">
-        <image id="tool-search"/>
+        <image class="tool-search"/>
       </hbox>
     </vbox>
 
     <vbox id="bookmark-container" flex="1" hidden="true">
       <vbox id="bookmark-form">
         <hbox align="start">
           <image id="bookmark-image" src="chrome://browser/skin/images/starred48.png"/>
           <grid id="bookmark-grid" flex="1">
@@ -245,9 +286,15 @@
       </tree>
       <hbox pack="end">
         <textbox id="test"/>
         <button label="Dismiss" oncommand="Shortcuts.dismiss();"/>
       </hbox>
     </vbox>
   </stack>
 
+  <vbox id="findpanel-placeholder" sizetopopup="always">
+    <panel id="findpanel" onpopupshown="Browser.doFind()">
+      <findbar id="findbar"/>
+   </panel>
+  </vbox>
+
 </window>
--- a/mobile/chrome/content/deckbrowser.xml
+++ b/mobile/chrome/content/deckbrowser.xml
@@ -29,35 +29,34 @@
 
         // panning
         this._stack.addEventListener("mousedown", this.stackEventHandler, true);
         // need mouseup handled on the window to catch mouseups on e.g. the toolbar
         window.addEventListener("mouseup", this.stackEventHandler, true);
         this._stack.addEventListener("mousemove", this.stackEventHandler, true);
 
         // zoom
-        this._stack.addEventListener("dblclick", this.stackEventHandler, true);
+        // FIXME: dblclicks don't work on the device
+        // this._stack.addEventListener("dblclick", this.stackEventHandler, true);
         this._stack.addEventListener("DOMMouseScroll", this.stackEventHandler, true);
 
-        this._scrollStartTimeout = -1;
+        this._dragStartTimeout = -1;
       </constructor>
 
       <field name="dragData">
         ({
           dragging: false,
-          offX: 0,
-          offY: 0,
+          dragX: 0,
+          dragY: 0,
           sX: 0,
           sY: 0,
-          scrollableWidth: 0,
-          scrollableHeight: 0,
-          canvasH: 0,
-          canvasW: 0,
           pageX: 0,
-          pageY: 0
+          pageY: 0,
+          oldPageX: 0,
+          oldPageY: 0
         })
       </field>
 
       <field name="_stack">
         document.getAnonymousElementByAttribute(this, "anonid", "cstack");
       </field>
 
       <field name="_canvas">
@@ -66,69 +65,84 @@
 
       <property name="browser" readonly="true">
         <getter>
           return document.getAnonymousElementByAttribute(this, "anonid", "browser");
         </getter>
       </property>
 
       <method name="updateCanvasState">
-        <parameter name="aLocationChanged"/>
+        <parameter name="aNewDoc"/>
         <body><![CDATA[
-          if (aLocationChanged) {
-            this.dragData.pageX = 0;
-            this.dragData.pageY = 0;
-          }
+          if (aNewDoc)
+            this._updateViewState();
 
           if (this._updateTimeout)
             clearTimeout(this._updateTimeout);
 
           var self = this;
           this._updateTimeout = setTimeout(function () {
             if (!self.dragData.dragging)
               self._browserToCanvas();
           }, 100);
         ]]></body>
       </method>
 
+      <method name="_updateViewState">
+        <body><![CDATA[
+          // Reset the pan data.
+          this.dragData.pageX = 0;
+          this.dragData.pageY = 0;
+
+          this._zoomed = false;
+
+          // Adjust the zoomLevel to fit the page contents in our window
+          // width
+          var [contentWidth, ] = this._contentAreaDimensions;
+          var canvasRect = this._canvas.getBoundingClientRect();
+          var canvasWidth = canvasRect.right - canvasRect.left;
+
+          this._zoomLevel = canvasWidth / contentWidth;
+        ]]></body>
+      </method>
+
       <method name="_browserToCanvas">
         <body><![CDATA[
-          this._updateCanvasPosition();
-
           // FIXME: canvas needs to know it's actual width/height
           var rect = this._canvas.getBoundingClientRect();
           var w = rect.right - rect.left;
           var h = rect.bottom - rect.top;
           this._canvas.width = w;
           this._canvas.height = h;
 
           var ctx = this._canvas.getContext("2d");
 
           ctx.clearRect(0,0,w,h);
 
-          //dump("x, y: " + this.dragData.pageX + "," + this.dragData.pageY + "\n");
           ctx.save();
           ctx.scale(this._zoomLevel, this._zoomLevel);
-          ctx.drawWindow(this.browser.contentWindow, 
-                         -this.dragData.pageX / this._zoomLevel, -this.dragData.pageY / this._zoomLevel,
+          ctx.drawWindow(this.browser.contentWindow,
+                         this.dragData.pageX, this.dragData.pageY,
                          w / this._zoomLevel, h / this._zoomLevel,
                          "white");
           ctx.restore();
         ]]></body>
       </method>
 
       <method name="_updateCanvasPosition">
         <body><![CDATA[
-          //dump("setting left/top: " + this.dragData.offX + "/" + this.dragData.offY + "\n");
-          this._canvas.style.marginLeft = this.dragData.offX + "px";
-          this._canvas.style.marginRight = -this.dragData.offX + "px";
-          this._canvas.style.marginTop = this.dragData.offY + "px";
-          this._canvas.style.marginBottom = -this.dragData.offY + "px";
+          this._canvas.style.marginLeft = this.dragData.dragX + "px";
+          this._canvas.style.marginRight = -this.dragData.dragX + "px";
+          this._canvas.style.marginTop = this.dragData.dragY + "px";
+          this._canvas.style.marginBottom = -this.dragData.dragY + "px";
 
-          //window.QueryInterface(Components.interfaces.nsIInterfaceRequestor).getInterface(Components.interfaces.nsIDOMWindowUtils).redraw();
+          // Force a sync redraw
+          window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                .getInterface(Components.interfaces.nsIDOMWindowUtils)
+                .redraw();
         ]]></body>
       </method>
 
       <property name="zoomLevel" readonly="true" onget="return this._zoomLevel;"/>
 
       <method name="zoom">
         <parameter name="aDirection"/>
         <body><![CDATA[
@@ -144,210 +158,420 @@
 
           if (this._zoomLevel > max)
             this._zoomLevel = max;
 
           this._browserToCanvas();
         ]]></body>
       </method>
 
+      /**
+       * Retrieve the content element for a given point (relative to the top
+       * left corner of the browser window).
+       */
+      <method name="elementFromPoint">
+        <parameter name="aX"/>
+        <parameter name="aY"/>
+        <body><![CDATA[
+          var cdoc = this.browser.contentDocument;
+
+          // Need to adjust for the toolbar height, etc.
+          var browserTop = this.browser.getBoundingClientRect().top;
+
+          // Scroll the browser so that elementFromPoint works properly
+          var [pageOffsetX, pageOffsetY] = this._scrollAndGetOffset();
+
+          var element =  cdoc.elementFromPoint((aX / this._zoomLevel) + pageOffsetX,
+                                               (aY / this._zoomLevel) + pageOffsetY - browserTop);
+
+          // Reset scroll state
+          this.browser.contentWindow.scrollTo(0, 0);
+
+          return element;
+        ]]></body>
+      </method>
+
+      <method name="zoomToElement">
+        <parameter name="aElement"/>
+        <body><![CDATA[
+          const margin = 0;
+
+          // scale to the element's width
+          var elRect = this._getPagePosition(aElement);
+          this._zoomLevel = Math.max((this.browser.boxObject.width) / (elRect.width + (2 * margin)),
+                                     2);
+
+          // pan to the element
+          this._panTo(Math.max(elRect.x - margin, 0),
+                      Math.max(0, elRect.y - margin));
+        ]]></body>
+      </method>
+
+      <method name="_getPagePosition">
+        <parameter name="aElement"/>
+        <body><![CDATA[
+          var r = aElement.getBoundingClientRect();
+          var retVal = {
+            width: r.right - r.left,
+            height: r.bottom - r.top,
+            x: r.left,
+            y: r.top
+          };
+
+          return retVal;
+        ]]></body>
+      </method>
+
+      <method name="_scrollAndGetOffset">
+        <parameter name="aX"/>
+        <parameter name="aY"/>
+        <body><![CDATA[
+          var cwin = this.browser.contentWindow;
+          cwin.scrollTo(this.dragData.pageX, this.dragData.pageY);
+
+          // Might not have been able to scroll all the way if we're zoomed in,
+          // the caller might need to account for that difference.
+          var pageOffsetX = this.dragData.pageX - cwin.scrollX;
+          var pageOffsetY = this.dragData.pageY - cwin.scrollY;
+
+          return [pageOffsetX, pageOffsetY];
+        ]]></body>
+      </method>
+
       <method name="_redispatchMouseEvent">
         <parameter name="aEvent"/>
         <parameter name="aType"/>
         <body><![CDATA[
-          //return;
+          if (!(aEvent instanceof MouseEvent)) {
+            Components.utils.reportError("_redispatchMouseEvent called with a non-mouse event");
+            return;
+          }
+
+          // Scroll the browser so that the event is targeted properly
+          var [pageOffsetX, pageOffsetY] = this._scrollAndGetOffset();
+
+          // Need to adjust for the toolbar height, etc.
+          var browserTop = this.browser.getBoundingClientRect().top;
+          var clickOffsetX = aEvent.clientX / this._zoomLevel;
+          var clickOffsetY = (aEvent.clientY - browserTop) / this._zoomLevel;
+
           var cwin = this.browser.contentWindow;
-          
-          // Scroll the browser so that the event is targeted properly
-          cwin.scrollTo(-this.dragData.pageX / this._zoomLevel, -this.dragData.pageY / this._zoomLevel);
-
           var cwu = cwin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                         .getInterface(Components.interfaces.nsIDOMWindowUtils);
-                        
-          // Need to adjust for the toolbar height, etc.
-          var browserTop = this.browser.getBoundingClientRect().top;
+          cwu.sendMouseEvent(aType || aEvent.type,
+                             pageOffsetX + clickOffsetX,
+                             pageOffsetY + clickOffsetY,
+                             aEvent.button || 0,
+                             aEvent.detail || 1,
+                             0);
 
-          cwu.sendMouseEvent(aType || aEvent.type,
-                             (aEvent.clientX) / this._zoomLevel,
-                             (aEvent.clientY - browserTop) / this._zoomLevel,
-                             aEvent.button || 0,
-                             aEvent.clickCount || 1,
-                             0);
+          // Reset scroll state
+          cwin.scrollTo(0, 0);
         ]]></body>
       </method>
 
-      <method name="_doPan">
+      <property name="_contentAreaDimensions" readonly="true">
+        <getter>
+          var cdoc = this.browser.contentDocument;
+
+          // These might not exist yet
+          var body = cdoc.body || {};
+          var html = cdoc.documentElement || {};
+
+          var w = Math.max(body.scrollWidth, html.scrollWidth);
+          var h = Math.max(body.scrollHeight, html.scrollHeight);
+
+          if (isNaN(w) || isNaN(h))
+            throw "Can't get content width/height";
+
+          return [w, h];
+        </getter>
+      </property>
+
+      <property name="_effectiveCanvasDimensions" readonly="true">
+        <getter><![CDATA[
+          return [this._canvas.width / this._zoomLevel,
+                  this._canvas.height / this._zoomLevel];
+         ]]></getter>
+      </property>
+
+      <field name="_fireOverpan">
+        0
+      </field>
+
+      /**
+       * Given a set of page coordinates, constrain them such that they
+       * fit within the rect defined by [0,0] and [x,y], where x and y are
+       * the maximum values that can be used for the canvas' .top and .left
+       * such that it is still within the scrollable area of the page, taking
+       * into account the current zoomLevel.
+       */
+      <method name="_constrainPanCoords">
+        <parameter name="aX"/>
+        <parameter name="aY"/>
+        <body><![CDATA[
+          const OVERPAN_LIMIT = 30;
+          const OVERPAN_LEFT = 1;
+          const OVERPAN_RIGHT = 2;
+          const OVERPAN_TOP = 3;
+          const OVERPAN_BOTTOM = 4;
+
+          var origX = aX;
+          var origY = aY;
+
+          var [contentAreaWidth, contentAreaHeight] = this._contentAreaDimensions;
+          var [canvasW, canvasH] = this._effectiveCanvasDimensions;
+
+          var offscreenWidth = contentAreaWidth - canvasW;
+          if (offscreenWidth <= 0) {
+            // Content is narrower than viewport, no need to pan horizontally
+            aX = 0;
+
+            // Check for an overpan
+            if (origX < -OVERPAN_LIMIT)
+              this._fireOverpan = OVERPAN_LEFT;
+            else if (origX > OVERPAN_LIMIT)
+              this._fireOverpan = OVERPAN_RIGHT;
+          } else {
+            var newPageX = Math.min(this.dragData.pageX + aX, offscreenWidth);
+            newPageX = Math.max(newPageX, 0);
+            aX = newPageX - this.dragData.pageX;
+
+            // Check for an overpan
+            if (origX < -OVERPAN_LIMIT && aX <= 0 && newPageX == 0)
+              this._fireOverpan = OVERPAN_LEFT;
+            else if (origX > OVERPAN_LIMIT && aX >= 0 && (offscreenWidth - newPageX) == 0)
+              this._fireOverpan = OVERPAN_RIGHT;
+          }
+
+          var offscreenHeight = contentAreaHeight - canvasH;
+          if (offscreenHeight <= 0) {
+            // Content is shorter than viewport, no need to pan vertically
+            aY = 0;
+
+            // Check for an overpan
+            if (origY < -OVERPAN_LIMIT)
+              this._fireOverpan = OVERPAN_TOP;
+            else if (origY > OVERPAN_LIMIT)
+              this._fireOverpan = OVERPAN_BOTTOM;
+          } else {
+            // min of 0, max of contentAreaHeight - canvasHeight
+            var newPageY = Math.min(this.dragData.pageY + aY, offscreenHeight);
+            newPageY = Math.max(newPageY, 0);
+            aY = newPageY - this.dragData.pageY;
+
+            // Check for an overpan
+            if (origY < -OVERPAN_LIMIT && aY <= 0 && newPageY == 0)
+              this._fireOverpan = OVERPAN_TOP;
+            else if (origY > OVERPAN_LIMIT && aY >= 0 && (offscreenHeight - newPageY) == 0)
+              this._fireOverpan = OVERPAN_BOTTOM;
+          }
+
+          return [aX, aY];
+        ]]></body>
+      </method>
+
+      <method name="_moveCanvas">
         <parameter name="aDx"/>
         <parameter name="aDy"/>
         <body><![CDATA[
           // constrain offsets to the actual scrollWidth/scrollHeight
-
-          var offscreenWidth = this.dragData.scrollableWidth - this.dragData.canvasW;
-          if (offscreenWidth <= 0) {
-            // Content is narrower than viewport, no need to pan horizontally
-            this.dragData.offX = 0;
-          } else {
-            var newPageX = Math.max(this.dragData.pageX + aDx, -offscreenWidth);
-            newPageX = Math.min(newPageX, 0);
-            var deltaX = newPageX - this.dragData.pageX;
-            this.dragData.offX = deltaX;
-          }
+          var [x, y] = this._constrainPanCoords(aDx, aDy);
 
-          var offscreenHeight = this.dragData.scrollableHeight - this.dragData.canvasH;
-          if (offscreenHeight <= 0) {
-            // Content is shorter than viewport, no need to pan vertically
-            this.dragData.offY = 0;
-          } else {
-            // min of 0, max of scrollableHeight - canvasHeight
-            var newPageY = Math.max(this.dragData.pageY + aDy, -offscreenHeight);
-            newPageY = Math.min(newPageY, 0);
-            var deltaY = newPageY - this.dragData.pageY;
-            this.dragData.offY = deltaY;
-          }
+          // Canvas needs to move up for content to scroll down
+          this.dragData.dragX = -x;
+          this.dragData.dragY = -y;
 
           this._updateCanvasPosition();
         ]]></body>
       </method>
 
+      <!-- Pans directly to a given X/Y (in page coordinates) -->
+      <method name="_panTo">
+        <parameter name="aX"/>
+        <parameter name="aY"/>
+        <body><![CDATA[
+          var [deltaX, deltaY] = this._constrainPanCoords(aX - this.dragData.pageX,
+                                                          aY - this.dragData.pageY);
+          this.dragData.pageX += deltaX;
+          this.dragData.pageY += deltaY;
+
+          this._browserToCanvas();
+        ]]></body>
+      </method>
+
       <method name="_dragStartTimer">
         <body><![CDATA[
           this.dragData.lastMouseEvent = Date.now() - 10;
           this.dragData.dragging = true;
-          this._scrollStartTimeout = -1;
+          this._dragStartTimeout = -1;
         ]]></body>
       </method>
 
       <method name="_endPan">
         <body><![CDATA[
-          // update the pageX/Y coords
-          this.dragData.pageX += this.dragData.offX;
-          this.dragData.pageY += this.dragData.offY;
+          // dragX/dragY are garanteed to be within the correct bounds, so just
+          // update pageX/pageY directly.
+          this.dragData.pageX -= this.dragData.dragX / this._zoomLevel;
+          this.dragData.pageY -= this.dragData.dragY / this._zoomLevel;
 
           // relocate the canvas to 0x0 in the window
-          this.dragData.offX = 0;
-          this.dragData.offY = 0;
+          this.dragData.dragX = 0;
+          this.dragData.dragY = 0;
 
           // update canvas position and draw the canvas at the new location
+          this._browserToCanvas();
           this._updateCanvasPosition();
-          this._browserToCanvas();
 
           this.dragData.dragging = false;
+
+          // Do we need to fire a content overpan event
+          if (this._fireOverpan > 0) {
+            var event = document.createEvent("UIEvents");
+            event.initUIEvent("overpan", true, false, window, this._fireOverpan);
+            this.dispatchEvent(event);
+          }
+          this._fireOverpan = 0;
         ]]></body>
       </method>
 
       <field name="stackEventHandler">
         <![CDATA[
         ({
           deckbrowser: this,
 
           handleEvent: function seh_handleEvent(aEvent) {
             if (!aEvent.type in this) {
-              dump("MouseController called with unknown event type " + aEvent.type + "\n");
+              Components.reportError("MouseController called with unknown event type " + aEvent.type + "\n");
               return;
             }
             this[aEvent.type](aEvent);
           },
 
           mousedown: function seh_mousedown(aEvent) {
             if (aEvent.button != 0)
-              return false;
-            
+              return;
+
             // cancel any pending canvas updates, since we're going to update again
             if (this._updateTimeout)
               clearTimeout(this._updateTimeout);
 
-            this.deckbrowser.dragData.canvasW = this.deckbrowser._canvas.width;
-            this.deckbrowser.dragData.canvasH = this.deckbrowser._canvas.height;
-
-            var cdoc = this.deckbrowser.browser.contentDocument;
-            var body = cdoc.body;
-            var html = cdoc.documentElement;
-            this.deckbrowser.dragData.scrollableWidth = Math.max(body.scrollWidth, html.scrollWidth);
-            this.deckbrowser.dragData.scrollableWidth *= this.deckbrowser._zoomLevel;
-            this.deckbrowser.dragData.scrollableHeight = Math.max(body.scrollHeight, html.scrollHeight);
-            this.deckbrowser.dragData.scrollableHeight *= this.deckbrowser._zoomLevel;
+            var zoomLevel = this.deckbrowser._zoomLevel;
+            var dragData = this.deckbrowser.dragData;
 
             // The start of the current portion drag
-            this.deckbrowser.dragData.sX = aEvent.screenX;
-            this.deckbrowser.dragData.sY = aEvent.screenY;
+            dragData.sX = aEvent.screenX;
+            dragData.sY = aEvent.screenY;
 
             // The total delta between current mouse position and sX/sY
-            this.deckbrowser.dragData.offX = 0;
-            this.deckbrowser.dragData.offY = 0;
+            dragData.dragX = 0;
+            dragData.dragY = 0;
 
             //this.deckbrowser._updateCanvasPosition();
 
             var self = this.deckbrowser;
-            this.deckbrowser._scrollStartTimeout = setTimeout(function () {
+            this.deckbrowser._dragStartTimeout = setTimeout(function () {
               self._dragStartTimer();
             }, 200);
+
+            this._lastMouseDown = aEvent;
           },
 
           mouseup: function seh_mouseup(aEvent) {
             if (aEvent.button == 0 && this.deckbrowser.dragData.dragging) {
               this.deckbrowser._endPan();
             } else if (aEvent.originalTarget == this.deckbrowser._canvas) {
               // Mouseup on canvas that isn't releasing from a drag
               // cancel scrollStart timer
-              clearTimeout(this.deckbrowser._scrollStartTimeout);
-              this.deckbrowser._scrollStartTimeout = -1;
+              clearTimeout(this.deckbrowser._dragStartTimeout);
+              this.deckbrowser._dragStartTimeout = -1;
 
               // send mousedown & mouseup
-              this.deckbrowser._redispatchMouseEvent(aEvent, "mousedown");
+              this.deckbrowser._redispatchMouseEvent(this._lastMouseDown);
+              this._lastMouseDown = null;
               this.deckbrowser._redispatchMouseEvent(aEvent);
+
+              // FIXME: dblclick events don't fire on the n810, check to see if
+              // we should treat this as a double-click
+              if (this._lastMouseUp &&
+                  (aEvent.timeStamp - this._lastMouseUp.timeStamp) < 400 &&
+                  Math.abs(aEvent.clientX - this._lastMouseUp.clientX) < 30 &&
+                  Math.abs(aEvent.clientY - this._lastMouseUp.clientY) < 30) {
+                this.dblclick(aEvent);
+                return;
+              }
+
+              this._lastMouseUp = aEvent;
             }
           },
 
           mousemove: function seh_mousemove(aEvent) {
             if (!this.deckbrowser.dragData.dragging) {
               // If we've moved more than N pixels lets go ahead and assume we're dragging
               // and not wait for the timeout to complete.
-              if (this.deckbrowser._scrollStartTimeout != -1 &&
+              if (this.deckbrowser._dragStartTimeout != -1 &&
                   (Math.abs(this.deckbrowser.dragData.sX - aEvent.screenX) > 10 ||
                    Math.abs(this.deckbrowser.dragData.sY - aEvent.screenY) > 10)) {
-                  clearTimeout(this.deckbrowser._scrollStartTimeout);
+                  clearTimeout(this.deckbrowser._dragStartTimeout);
                   this.deckbrowser._dragStartTimer();
               } else {
                 return false;
               }
             }
 
-            var dx = aEvent.screenX - this.deckbrowser.dragData.sX;
-            var dy = aEvent.screenY - this.deckbrowser.dragData.sY;
+            var dx = this.deckbrowser.dragData.sX - aEvent.screenX;
+            var dy = this.deckbrowser.dragData.sY - aEvent.screenY;
 
-            this.deckbrowser._doPan(dx, dy);
+            // Filter out noise in big panning operations which are
+            // almost certainly intended to be on-axis horizontal or
+            // vertical pans.
+            if (Math.abs(dx) > 40 || Math.abs(dy) > 40) {
+              if (Math.abs(dx/dy) < 0.3) // dx is a lot less than dy, probably a vertical drag
+                dx = 0;
+              else if (Math.abs(dy/dx) < 0.3) // probably a horizontal drag
+                dy = 0;
+            }
+            
+            this.deckbrowser._moveCanvas(dx, dy);
 
             if (Date.now() - this.deckbrowser.dragData.lastMouseEvent < 75) { // FIXME: make this a constant
               //dump("dropping event\n");
               return false;
             }
 
             this.deckbrowser.dragData.lastMouseEvent = Date.now();
 
             aEvent.preventDefault();
-            return true; 
+            return true;
           },
-          
+
           DOMMouseScroll: function seh_DOMMouseScroll(aEvent) {
             this.deckbrowser.zoom(aEvent.detail);
           },
-          
+
           dblclick: function seh_dblclick(aEvent) {
-            //dump("Zooming...\n");
-            var x = aEvent.clientX;
-            var y = aEvent.clientY;
+            var target = aEvent.originalTarget;
+            var dragData = this.deckbrowser.dragData;
+
             if (this.deckbrowser._zoomed) {
-              this.deckbrowser._zoomLevel = 1;
+              // reset zoom, pan state
+              this.deckbrowser._zoomLevel = this._oldZoomLevel;
+              [dragData.pageX, dragData.pageY] = [dragData.oldPageX, dragData.oldPageY];
+
+              this.deckbrowser._browserToCanvas();
               this.deckbrowser._zoomed = false;
             } else {
-              this.deckbrowser._zoomLevel = 2;
+              // Remember pageX/pageY
+              [dragData.oldPageX, dragData.oldPageY] = [dragData.pageX, dragData.pageY];
+              this._oldZoomLevel = this.deckbrowser._zoomLevel;
+
+              var element = this.deckbrowser.elementFromPoint(aEvent.clientX, aEvent.clientY);
+              this.deckbrowser.zoomToElement(element);
               this.deckbrowser._zoomed = true;
             }
-
-            this.deckbrowser._browserToCanvas();
           }
         });
         ]]>
       </field>
     </implementation>
   </binding>
 
 </bindings>
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/content/urlbar.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!DOCTYPE bindings PUBLIC "-//MOZILLA//DTD XBL V1.0//EN" "http://www.mozilla.org/xbl">
+
+<bindings
+    xmlns="http://www.mozilla.org/xbl"
+    xmlns:xbl="http://www.mozilla.org/xbl"
+    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+
+  <binding id="autocomplete-aligned" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
+    <implementation>
+      <method name="openPopup">
+        <body><![CDATA[
+          this.popup.openAutocompletePopup(this, document.getElementById("toolbar-main"));
+        ]]></body>
+      </method>
+      <method name="closePopup">
+        <body><![CDATA[
+          // do nothing
+        ]]></body>
+      </method>
+      <method name="reallyClosePopup">
+        <body><![CDATA[
+          this.mConsumeRollupEvent = false;
+          this.popup.closePopup();
+        ]]></body>
+      </method>
+    </implementation>
+  </binding>
+
+</bindings>
--- a/mobile/chrome/jar.mn
+++ b/mobile/chrome/jar.mn
@@ -1,37 +1,42 @@
 #filter substitution
 
 browser.jar:
 % content browser %content/
 * content/browser.xul                  (content/browser.xul)
   content/browser.js                   (content/browser.js)
   content/browser-ui.js                (content/browser-ui.js)
   content/commandUtil.js               (content/commandUtil.js)
+  content/urlbar.xml                   (content/urlbar.xml)
   content/deckbrowser.xml              (content/deckbrowser.xml)
-  content/deckbrowser.css              (content/deckbrowser.css)
+  content/browser.css                  (content/browser.css)
   content/scrollbars.css               (content/scrollbars.css)
   content/content.css                  (content/content.css)
 * content/shortcuts.js
 % content branding %branding/
 % locale branding @AB_CD@ %branding/
   branding/brand.dtd                   (locale/@AB_CD@/brand/brand.dtd)
   branding/brand.properties            (locale/@AB_CD@/brand/brand.properties)
 
 classic.jar:
 % skin browser classic/1.0 %
   browser.css                          (skin/browser.css)
   images/close.png                     (skin/images/close.png)
   images/close-small.png               (skin/images/close-small.png)
   images/default-favicon.png           (skin/images/default-favicon.png)
   images/identity.png                  (skin/images/identity.png)
   images/starred48.png                 (skin/images/starred48.png)
+  images/page-starred.png              (skin/images/page-starred.png)
+  images/tag.png                       (skin/images/tag.png)
   images/throbber.png                  (skin/images/throbber.png)
   images/throbber.gif                  (skin/images/throbber.gif)
+  images/toolbar.png                   (skin/images/toolbar.png)
   images/mono-toolbar.png              (skin/images/mono-toolbar.png)
   images/toolbar-background.png        (skin/images/toolbar-background.png)
-  images/mono-sidebar.png              (skin/images/mono-sidebar.png)
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %
   browser.dtd                          (locale/@AB_CD@/browser.dtd)
   browser.properties                   (locale/@AB_CD@/browser.properties)
   shortcuts.properties                 (locale/@AB_CD@/shortcuts.properties)
+  search.properties                    (locale/@AB_CD@/search.properties)
+  region.properties                    (locale/@AB_CD@/region.properties)
--- a/mobile/chrome/locale/en-US/browser.dtd
+++ b/mobile/chrome/locale/en-US/browser.dtd
@@ -26,16 +26,24 @@
 
 <!ENTITY bookmarkName.label    "Name:">
 <!ENTITY bookmarkFolder.label  "Folder:">
 <!ENTITY bookmarkTags.label    "Tags:">
 <!ENTITY bookmarkRemove.label  "Remove Bookmark">
 <!ENTITY bookmarkCancel.label  "Cancel">
 <!ENTITY bookmarkDone.label    "Done">
 
+<!ENTITY findOnCmd.label     "Find in This Pageā€¦">
+<!ENTITY findOnCmd.accesskey "F">
+<!ENTITY findOnCmd.commandkey "f">
+<!ENTITY findAgainCmd.label  "Find Again">
+<!ENTITY findAgainCmd.accesskey "g">
+<!ENTITY findAgainCmd.commandkey "g">
+<!ENTITY findAgainCmd.commandkey2 "VK_F3">
+
 <!ENTITY identity.unverifiedsite2 "This web site does not supply identity information.">
 <!ENTITY identity.connectedTo "You are connected to">
 <!-- Localization note (identity.runBy) : This string appears between a
 domain name (above) and an organization name (below). E.g.
 
 example.com
 which is run by
 Example Enterprises, Inc.
--- a/mobile/chrome/locale/en-US/browser.properties
+++ b/mobile/chrome/locale/en-US/browser.properties
@@ -1,11 +1,27 @@
+# Popup Blocker
+popupWarning=%S prevented this site from opening a pop-up window.
+popupWarningMultiple=%S prevented this site from opening %S pop-up windows.
+popupButtonAlwaysAllow=Always Allow
+popupButtonAlwaysAllow.accesskey=A
+popupButtonNeverWarn=Never tell me
+popupButtonNeverWarn.accesskey=N
+
+
+# Site Identity
 identity.identified.verifier=Verified by: %S
 identity.identified.verified_by_you=You have added a security exception for this site
 identity.identified.state_and_country=%S, %S
 identity.identified.title_with_country=%S (%S)
-
 identity.encrypted=Your connection to this web site is encrypted to prevent eavesdropping.
 identity.unencrypted=Your connection to this web site is not encrypted.
+identity.unknown.tooltip=This web site does not supply identity information.
+identity.ownerUnknown2=(unknown)
 
-identity.unknown.tooltip=This web site does not supply identity information.
-
-identity.ownerUnknown2=(unknown)
+# Geolocation UI
+gelocation.exactLocation=Exact Location (within 10 feet)
+gelocation.exactLocationKey=E
+gelocation.neighborhoodLocation=Neighborhood (within 1 mile)
+gelocation.neighborhoodLocationKey=N
+gelocation.nothingLocation=Nothing
+gelocation.nothingLocationKey=C
+geolocation.requestMessage=%S wants to know where you are.  Tell them:
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/locale/en-US/region.properties
@@ -0,0 +1,34 @@
+# Default search engine
+browser.search.defaultenginename=Google
+
+# Search engine order (order displayed in the search bar dropdown)s
+browser.search.order.1=Google
+browser.search.order.2=Yahoo
+
+# This is the default set of web based feed handlers shown in the reader
+# selection UI
+browser.contentHandlers.types.0.title=Bloglines
+browser.contentHandlers.types.0.uri=http://www.bloglines.com/login?r=/sub/%s
+browser.contentHandlers.types.1.title=My Yahoo
+browser.contentHandlers.types.1.uri=http://add.my.yahoo.com/rss?url=%s
+browser.contentHandlers.types.2.title=Google
+browser.contentHandlers.types.2.uri=http://fusion.google.com/add?feedurl=%s
+
+# Keyword URL (for location bar searches)
+keyword.URL=http://www.google.com/search?ie=UTF-8&oe=UTF-8&sourceid=navclient&gfns=1&q=
+
+# increment this number when anything gets changed in the list below.  This will
+# cause Firefox to re-read these prefs and inject any new handlers into the 
+# profile database.  Note that "new" is defined as "has a different URL"; this
+# means that it's not possible to update the name of existing handler, so 
+# don't make any spelling errors here.
+gecko.handlerService.defaultHandlersVersion=1
+
+# The default set of protocol handlers for webcal:
+gecko.handlerService.schemes.webcal.0.name=30 Boxes
+gecko.handlerService.schemes.webcal.0.uriTemplate=http://30boxes.com/external/widget?refer=ff&url=%s
+
+# The default set of protocol handlers for mailto:
+gecko.handlerService.schemes.mailto.0.name=Yahoo! Mail
+gecko.handlerService.schemes.mailto.0.uriTemplate=http://compose.mail.yahoo.com/?To=%s 
+
new file mode 100755
--- /dev/null
+++ b/mobile/chrome/locale/en-US/search.properties
@@ -0,0 +1,25 @@
+searchtip=Search using %S
+
+cmd_clearHistory=Clear Search History
+cmd_clearHistory_accesskey=C
+
+cmd_showSuggestions=Show Suggestions
+cmd_showSuggestions_accesskey=S
+
+addEngineConfirmTitle=Add Search Engine
+addEngineConfirmation=Add "%S" to the list of engines available in the search bar?\n\nFrom: %S
+addEngineUseNowText=Start &using it right away
+addEngineAddButtonLabel=Add
+
+error_loading_engine_title=Download Error
+# LOCALIZATION NOTE (error_loading_engine_msg2): %1$S = brandShortName, %2$S = location
+error_loading_engine_msg2=%S could not download the search plugin from:\n%S
+error_duplicate_engine_msg=%S could not install the search plugin from "%S" because an engine with the same name already exists.
+
+error_invalid_engine_title=Install Error
+# LOCALIZATION NOTE (error_invalid_engine_msg): %S = brandShortName
+error_invalid_engine_msg=This search engine isn't supported by %S and can't be installed.
+
+cmd_addFoundEngine=Add "%S"
+
+suggestion_label=Suggestions
--- a/mobile/chrome/skin/browser.css
+++ b/mobile/chrome/skin/browser.css
@@ -82,72 +82,85 @@ toolbarbutton .toolbarbutton-text {
 
 toolbarbutton:hover,
 toolbarbutton:hover:active,
 toolbarbutton[open="true"] {
   border-color: transparent;
 }
 
 #toolbar-main {
+  -moz-appearance: none;
   -moz-box-align: center;
-  background: url("chrome://browser/skin/images/toolbar-background.png") repeat-x top left;
+  background-image: url("chrome://browser/skin/images/toolbar-background.png");
+  background-repeat: repeat-x;
+  background-position: top left;
   padding: 8px;
 }
 
 toolbarbutton.urlbar-icon-button {
-  list-style-image: url("chrome://browser/skin/images/mono-toolbar.png");
+  list-style-image: url("chrome://browser/skin/images/toolbar.png");
 }
 
 #tool-reload {
-  -moz-image-region: rect(0px 108px 36px 72px);
+  -moz-image-region: rect(6px 302px 32px 275px);
 }
 
 #tool-stop {
-  -moz-image-region: rect(0px 216px 36px 180px);
+  -moz-image-region: rect(7px 336px 32px 311px);
 }
 
 #tool-go {
-  -moz-image-region: rect(0px 72px 36px 36px);
+  -moz-image-region: rect(7px 368px 32px 346px);
 }
 
 #browser-controls {
   background-color: rgb(123,125,123);
 }
 
 toolbarbutton.browser-control-button {
-  list-style-image: url("chrome://browser/skin/images/mono-sidebar.png");
+  list-style-image: url("chrome://browser/skin/images/toolbar.png");
 }
 
 #tool-star {
-  -moz-image-region: rect(180px 59px 239px 0px);
+  -moz-image-region: rect(2px 151px 39px 113px);
 }
 
 #tool-star[starred="true"] {
-  -moz-image-region: rect(120px 59px 179px 0px);
+  -moz-image-region: rect(121px 151px 158px 113px);
 }
 
 #tool-back {
-  -moz-image-region: rect(0px 59px 59px 0px);
-  padding-bottom: 0px;
+  -moz-image-region: rect(0px 58px 58px 0px);
+  padding-bottom: 0px !important;
+}
+
+#tool-back[disabled="true"] {
+  -moz-image-region: rect(58px 58px 116px 0px);
+  padding-bottom: 0px !important;
 }
 
 #tool-forward {
-  -moz-image-region: rect(60px 59px 119px 0px);
-  padding-top: 0px;
+  -moz-image-region: rect(0px 108px 37px 60px);
+  padding-top: 0px !important;
+}
+
+#tool-forward[disabled="true"] {
+  -moz-image-region: rect(58px 108px 95px 60px);
+  padding-top: 0px !important;
 }
 
 #tool-bookmarks {
-  -moz-image-region: rect(240px 59px 299px 0px);
+  -moz-image-region: rect(1px 190px 37px 160px);
 }
 
 #tool-actions {
-  -moz-image-region: rect(300px 59px 359px 0px);
+  -moz-image-region: rect(0px 242px 45px 197px);
 }
 
-#tool-search {
+.tool-search {
   list-style-image: url("chrome://browser/skin/images/mono-toolbar.png");
   -moz-image-region: rect(0px 36px 36px 0px);
 }
 
 #toolbar-main[mode="loading"] > #urlbar-icons > #tool-go,
 #toolbar-main[mode="loading"] > #urlbar-icons > #tool-reload {
   visibility: collapse;
 }
@@ -203,17 +216,17 @@ toolbarbutton.browser-control-button {
 
 
 /* URL List */
 #urllist-container {
   background-color: rgba(123,125,123,0.9);
   padding: 8px;
 }
 
-#urllist-items {
+#urllist-items, .autocomplete-richlistbox {
   -moz-appearance: none !important;
   background-color: rgba(207,207,207,0.9);
   border: 2px solid #fff !important;
   -moz-border-radius: 10px;
 }
 
 .urllist-item {
   -moz-appearance: none !important;
@@ -221,23 +234,81 @@ toolbarbutton.browser-control-button {
   color: #000;
 }
 
 .urllist-image {
   width: 24px;
   height: 24px;
 }
 
-#urllist-search {
+#urllist-search, #autocomplete_navbuttons {
   background-color: rgba(207,207,207,0.9);
   border: 2px solid #fff !important;
   -moz-border-radius: 10px;
   _moz-box-align: start;
 }
 
+/* autocomplete */
+
+#popup_autocomplete {
+  moz-appearance: none;
+  background-color: rgba(207,207,207,0.9);
+}
+
+#autocomplete_navbuttons {
+  margin-top: 12px;
+}
+
+#PopupAutoCompleteRichResult {
+  direction: ltr !important;
+}
+
+.ac-result-type-bookmark {
+   list-style-image: url("chrome://browser/skin/images/page-starred.png");
+   width: 16px;
+   height: 16px;
+ }
+ 
+.ac-result-type-tag {
+   list-style-image: url("chrome://browser/skin/images/tag.png");
+   width: 16px;
+   height: 16px;
+ }
+ 
+.ac-comment {
+  font-size: 1.15em;
+}
+
+.ac-extra > .ac-comment {
+  font-size: inherit;
+}
+
+.ac-url-box {
+  display: none;
+}
+
+.ac-url-text {
+  color: GrayText;
+}
+
+.ac-comment[selected="true"], .ac-url-text[selected="true"] {
+  color: inherit !important;
+}
+
+/* find bar */
+
+#findpanel {
+  padding: 0 !important;
+  -moz-appearance: none;
+  background: transparent;
+}
+
+findbar {
+  background: rgba(128, 128, 128, 0.75);
+}
 
 /* Bookmark editor */
 #bookmark-container {
   background-color: rgba(123,125,123,0.9);
   padding: 8px;
 }
 
 #bookmark-form {
deleted file mode 100644
index 72dacc00b94e12a0b9246b5f18bd55fb78ad6019..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..efc863368c335baddeb3a85521dd2eb25c46e62f
GIT binary patch
literal 718
zc$@*w0x|uGP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV0007&Nkl<ZIE{^x
zy=zlp6o;Spz4zw3P1<NQ3W~8*MX}hVB8r1JIXEb|*g<emsXCR8{soR|Ar69|li=bk
zqJyBlZLI{UwOag4TVrhVb$dTwhgz^0!~<tJ=lmWx=lq0Higq!6yAW1^e+NKF?R@@%
zX}6ox+p2-Uwa=w@70v?ucxyfdZnvA0?TThG9}~JUl09&o?15tlT^})v`PhF00?!#t
zZQI=wO=pRwGo-fl_5_|g_|Fy0Vt-Tx!4?(xJt#H2Y40IWF5r;a+fViVo8gzY`aVgi
z%_7CO!o_h5K$yk+B_YJ1(6p_gSVz`~CnKGkx3jtLDBuAOD3|iobL#V7ahlB)w`G-s
z!2gO;_r=&~-}u&nQ~ewE96=Wr!jK>W@LHgJP`@FX2Y#KvY2p)Qae9vOtCtfJA+CL&
zer9>C8X}ek*9O}H&jEK4>}hC?LG2kd$B~gGoW=r`nUV$KnovqHHoE_0D4u%Id*V_!
znv6rE1nw;OmEU&&5D?!;v(dr3#}l^OS~)W?H2FxZ8EI^E|LJgY<K5oFI};IY4*URw
zwkiaW0?Uar^L)PHG+O5dh9)1bd*i^+>xV1L)snUFc2(>t@B;9Ba9!|SRw_%_4Xd;!
zUI#FXg_LgSxky-o>w{)MN7rg<2Dm<vaDZ-TIkQ+uwKGyu4~c{k)gjRd-5KgNNxdea
zI|GSMjHr$t5(yyrmyuMUa^ZL=rf1Vw%K_yVWvX*O7XZ3<bZyG+=_Z<u5sq6iyJh8o
zkLv)S_Q{aOm#Hd?Gd~^A_HSysybe&At&S{xUcQp)?Fz||2B>|&lXU>8$*J0W^Xbx;
z`Ujzj8wW2>O#ZRnuvyICE0-!)r7m+)lT&MrUq%%gd|}2WQUCw|07*qoM6N<$g7?c)
A8~^|S
new file mode 100755
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4c876b92b0faf34c5a01102ca80789a1f169e8da
GIT binary patch
literal 517
zc%17D@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!WQl7;NpOBzNqJ&XDuZK6ep0G}
zXKrG8YEWuoN@d~6R2!h8O94J1uK)l4PX~huEB8-WwSVerAUZH>-TqnY4lLNXf8oac
z%eNd@xBbAn9S64V-oJJCf$e(^?Am)^&%OgdaOlJtAXt0l+qx^?w%+)*?Z&tLcfRkx
z`|Z@j@24MryZRi6zCU>L{lVMsPv3oi`u_XNkKbN>{QmCq_fKEGfBE+P>$mSfu=UQr
zo%jCjy7zD2ga7*<{M!#CAO1V?=--jYK=l9Qvwx?a{yX*T-}x6n^#AJXe^+1szxDRt
zt#|)#zx)5-!@ma~{y+Ti@7ZS{`v2zZ|2N<Moo#-j1@xVENswPKgG0j>bg+N_7i^$w
z+MEwSZIe7*9780+V^5vsYcddUyC~_=6T+f$WUkt~FMsEs^0<EC+<Z-^#rMuWv6gJu
zFCcK)G<spnBfA@0r%zyPUoQUKBF!M**iLN`ub$QR<;&*I41Xb8X1;M&hd9H^cXKyo
z`38z{*qAQLdSwy8)FITTb|~g$3TK0cOv_of|34WLl$Kjn-DOaD^>1n!U-@&Pht@iE
b<?ncJC<RZOqZ=FwbRC1ItDnm{r-UW|0#Y-#
index 16b6ee91291bd32fd60ce1eff13e7812197f13b1..32782064d3606097365952233b8430d51d96d5b4
GIT binary patch
literal 675
zc$@*F0$lxxP)<h;3K|Lk000e1NJLTq000O8002e^1^@s6@m<xI00004XF*Lt006O%
z3;baP0000NbVXQnQ*UN;cVTj608n9RZgehAMN}YmGA=c|hspi`00K8jL_t(YOYM+p
zPg+3~hEK!47Ll;Yz9ah%A$%brBy8~?)TB0TOq15uDy;~L6l=k@A@;mu=Vk=F{sBoI
z&Y3gMyfeA4`u+ZEgTX-RZ@FBOHtTk~2mAZ`2WnM&>h*eW^q@|sbI2zSWks(-?RNXE
z9@J{J-swTjX7jxs)Mzx0^nma|kLQJ2t@f#2uYY_Bs#dGVke&j<mvXuMxl*YdZ-RJu
zS}Yd70`5;%p<FI^3Zzsjl}=P3e9vSu-}3qVfcpXWXN5xH3>6UnohOq?ApuB|Y&JU(
z0!XD&Lc;KokWQyZiA3Up>kuMWAtM!)cyr9nC?1bra2-N~BoPAJqrJU7Apn-oc`cx5
zG<vB@stB1Pk;o;V7_%f&04EM5#DgoOK;k+AQbffS6G%wnItJX2BtihOSWHN&h*Vtx
zX%q|wuK}MFDWOnEOF|k20)cA~4u>Z!t_yr(s!9-fedYK21xWbe^ZBMg75U%<kjLYh
zL1KGN!~x=s8?V<pgP^9#RO055!17c@2)Kr;xQ6>FkP_bT^FK(sak*SGiQDa-X#zF@
zh7yRhySsZUQ+NYuxKe}I`sHvqZUF?qGf@FKoz68u6*neGK_u90wz&jptB}=dodbRY
zu3CfacKZTYESC8uh=+Gl+EmGGHsA3T3l)CTLLvk`aK&#4r)a&LA>of6&kIJQ@$V|z
z+uPcVwzjskh3wTC?d<H_uflKAW@Iv%?)7*P9=^}ZcX;>?{{U^5q`glT3d;Zh002ov
JPDHLkV1nlfDuVz3
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..62cc600f172e02b7e6bc66b29ba1d710e2ed957d
GIT binary patch
literal 12967
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
PpkELLHTgo>H$ndcA+v@%
--- a/mobile/confvars.sh
+++ b/mobile/confvars.sh
@@ -32,15 +32,15 @@
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
 MOZ_APP_NAME=fennec
 MOZ_APP_DISPLAYNAME=Fennec
-MOZ_APP_VERSION=0.4
+MOZ_APP_VERSION=0.5
 
 MOZ_XUL_APP=1
 MOZ_UPDATER=0
 MOZ_ENABLE_LIBXUL=1
 MOZ_NO_XPCOM_OBSOLETE=1
 MOZ_XULRUNNER=1
--- a/mobile/installer/Makefile.in
+++ b/mobile/installer/Makefile.in
@@ -71,17 +71,12 @@ ifeq ($(OS_TARGET),Linux)
 deb: 
 	rm -rf $(DEBDESTDIR)/usr/local/*
 	$(NSINSTALL) -D $(DEBDESTDIR)/usr/local
 	cd $(DEBDESTDIR)/usr/local; cat $(DEPTH)/../../../../dist/$(PKG_BASENAME)$(PKG_SUFFIX) | $(UNMAKE_PACKAGE)  
 	rm -rf $(DEBDESTDIR)/usr/local/$(MOZ_APP_NAME)/xulrunner	
 	$(NSINSTALL)  debian/$(MOZ_APP_NAME).desktop $(DEBDESTDIR)/usr/share/applications/hildon/
 	$(NSINSTALL) -D $(DEBDESTDIR)/usr/share/dbus-1/services/
 	cp debian/$(MOZ_APP_NAME).service $(DEBDESTDIR)/usr/share/dbus-1/services/org.mozilla.$(MOZ_APP_NAME).service
-ifdef NS_HILDON
-	$(NSINSTALL) $(DEPTH)/dist/bin/components/softkey.xpt $(DEBDESTDIR)/usr/local/$(MOZ_APP_NAME)/components/
-	$(NSINSTALL) $(DEPTH)/dist/bin/components/libsoftkey.so $(DEBDESTDIR)/usr/local/$(MOZ_APP_NAME)/components/
-	$(NSINSTALL) $(DEPTH)/dist/bin/libsoftokn3.* $(DEBDESTDIR)/usr/local/$(MOZ_APP_NAME)/	
-endif
-	#$(NSINSTALL) -m 755 debian/postinst debian/$(MOZ_APP_NAME)/DEBIAN
+#	$(NSINSTALL) -m 755 debian/postinst debian/$(MOZ_APP_NAME)/DEBIAN
 	dh_link; fakeroot dh_fixperms; 	dh_shlibdeps; fakeroot dh_gencontrol; fakeroot dh_md5sums; dh_builddeb
 endif