merge m-c to kiwifox
authorAli Juma <ajuma@mozilla.com>
Wed, 18 Jan 2012 11:04:04 -0500
changeset 90848 8adc6d07c8e05d05dd30d0cdab79bc3da1f5dc21
parent 90847 c96dd4bb2388791dd00da266b64df574f33dfe26 (current diff)
parent 86012 79e5d0b77d1080c7f29378ee48b0cad85220de33 (diff)
child 90849 8abc11cc5fb954d44b54eed5632111aaa7c8d2d4
push idunknown
push userunknown
push dateunknown
milestone12.0a1
merge m-c to kiwifox
browser/components/migration/src/nsProfileMigrator.cpp
browser/components/migration/src/nsProfileMigrator.h
dom/sms/src/SmsServiceFactory.cpp
dom/sms/src/SmsServiceFactory.h
gfx/2d/ScaledFontSkia.cpp
gfx/2d/ScaledFontSkia.h
gfx/gl/GLContextProviderCGL.mm
gfx/layers/opengl/LayerManagerOGL.cpp
modules/libpref/src/init/all.js
widget/android/nsWindow.cpp
widget/cocoa/nsChildView.mm
xpcom/ds/nsIRecyclingAllocator.idl
xpcom/ds/nsRecyclingAllocator.cpp
xpcom/ds/nsRecyclingAllocator.h
--- a/.gitignore
+++ b/.gitignore
@@ -34,8 +34,14 @@ js/src/configure
 js/src/autom4te.cache
 # SpiderMonkey test result logs
 js/src/tests/results-*.html
 js/src/tests/results-*.txt
 
 # Java HTML5 parser classes
 parser/html/java/htmlparser/
 parser/html/java/javaparser/
+
+# Ignore the files and directory that Eclipse IDE creates
+/.project
+/.cproject
+/.settings/
+
--- a/.hgignore
+++ b/.hgignore
@@ -34,8 +34,14 @@
 # SpiderMonkey test result logs
 ^js/src/tests/results-.*\.(html|txt)$
 
 # Java HTML5 parser classes
 ^parser/html/java/(html|java)parser/
 
 # SVN directories
 \.svn/
+
+# Ignore the files and directory that Eclipse IDE creates
+^\.project$
+^\.cproject$
+^\.settings$
+
--- a/accessible/src/atk/nsAccessibleWrap.cpp
+++ b/accessible/src/atk/nsAccessibleWrap.cpp
@@ -436,23 +436,19 @@ nsAccessibleWrap::CreateMaiInterfaces(vo
     //nsIAccessibleValue
     nsCOMPtr<nsIAccessibleValue> accessInterfaceValue;
     QueryInterface(NS_GET_IID(nsIAccessibleValue),
                    getter_AddRefs(accessInterfaceValue));
     if (accessInterfaceValue) {
        interfacesBits |= 1 << MAI_INTERFACE_VALUE; 
     }
 
-    //nsIAccessibleDocument
-    nsCOMPtr<nsIAccessibleDocument> accessInterfaceDocument;
-    QueryInterface(NS_GET_IID(nsIAccessibleDocument),
-                              getter_AddRefs(accessInterfaceDocument));
-    if (accessInterfaceDocument) {
+    // document accessible
+    if (IsDoc())
         interfacesBits |= 1 << MAI_INTERFACE_DOCUMENT;
-    }
 
     if (IsImageAccessible())
         interfacesBits |= 1 << MAI_INTERFACE_IMAGE;
 
   // HyperLinkAccessible
   if (IsLink())
     interfacesBits |= 1 << MAI_INTERFACE_HYPERLINK_IMPL;
 
--- a/accessible/src/atk/nsApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.cpp
@@ -891,25 +891,25 @@ PreInit()
     return;
 
   DBusConnection* bus = dbus_bus_get(DBUS_BUS_SESSION, nsnull);
   if (!bus)
     return;
 
   dbus_connection_set_exit_on_disconnect(bus, FALSE);
 
+  static const char* iface = "org.a11y.Status";
+  static const char* member = "IsEnabled";
   DBusMessage *message;
   message = dbus_message_new_method_call("org.a11y.Bus", "/org/a11y/bus",
                                          "org.freedesktop.DBus.Properties",
                                          "Get");
   if (!message)
     goto dbus_done;
 
-  static const char* iface = "org.a11y.Status";
-  static const char* member = "IsEnabled";
   dbus_message_append_args(message, DBUS_TYPE_STRING, &iface,
                            DBUS_TYPE_STRING, &member, DBUS_TYPE_INVALID);
   dbus_connection_send_with_reply(bus, message, &sPendingCall, 1000);
   dbus_message_unref(message);
 
 dbus_done:
   dbus_connection_unref(bus);
 #endif
--- a/accessible/src/atk/nsMaiInterfaceDocument.cpp
+++ b/accessible/src/atk/nsMaiInterfaceDocument.cpp
@@ -32,22 +32,32 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * 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 ***** */
 
-#include "nsAccessibleWrap.h"
 #include "nsMaiInterfaceDocument.h"
 
-const char *const kDocTypeName = "W3C-doctype";
-const char *const kDocUrlName = "DocURL";
-const char *const kMimeTypeName = "MimeType";
+#include "nsAccessibleWrap.h"
+#include "nsDocAccessible.h"
+
+static const char* const kDocTypeName = "W3C-doctype";
+static const char* const kDocUrlName = "DocURL";
+static const char* const kMimeTypeName = "MimeType";
+
+// below functions are vfuncs on an ATK  interface so they need to be C call
+extern "C" {
+
+static const gchar* getDocumentLocaleCB(AtkDocument* aDocument);
+static AtkAttributeSet* getDocumentAttributesCB(AtkDocument* aDocument);
+static const gchar* getDocumentAttributeValueCB(AtkDocument* aDocument,
+                                                const gchar* aAttrName);
 
 void
 documentInterfaceInitCB(AtkDocumentIface *aIface)
 {
     NS_ASSERTION(aIface, "Invalid Interface");
     if(!aIface)
         return;
 
@@ -59,101 +69,82 @@ documentInterfaceInitCB(AtkDocumentIface
     aIface->get_document_attributes = getDocumentAttributesCB;
     aIface->get_document_attribute_value = getDocumentAttributeValueCB;
     aIface->get_document_locale = getDocumentLocaleCB;
 }
 
 const gchar *
 getDocumentLocaleCB(AtkDocument *aDocument)
 {
-    nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-    if (!accWrap)
-        return nsnull;
+  nsAccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
+  if (!accWrap)
+    return nsnull;
 
-    nsCOMPtr<nsIAccessNode> docAccessNode;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessNode),
-                            getter_AddRefs(docAccessNode));
-    NS_ENSURE_TRUE(docAccessNode, nsnull);
-
-    nsAutoString locale;
-    docAccessNode->GetLanguage(locale);
-    if (locale.IsEmpty()) {
-      return nsnull;
-    }
-    return nsAccessibleWrap::ReturnString(locale);
+  nsAutoString locale;
+  accWrap->GetLanguage(locale);
+  return locale.IsEmpty() ? nsnull : nsAccessibleWrap::ReturnString(locale);
 }
 
 static inline GSList *
 prependToList(GSList *aList, const char *const aName, const nsAutoString &aValue)
 {
+  if (aValue.IsEmpty())
+    return aList;
+
     // libspi will free these
     AtkAttribute *atkAttr = (AtkAttribute *)g_malloc(sizeof(AtkAttribute));
     atkAttr->name = g_strdup(aName);
     atkAttr->value = g_strdup(NS_ConvertUTF16toUTF8(aValue).get());
     return g_slist_prepend(aList, atkAttr);
 }
 
 AtkAttributeSet *
 getDocumentAttributesCB(AtkDocument *aDocument)
 {
-    nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-    if (!accWrap)
-        return nsnull;
+  nsAccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
+  if (!accWrap || !accWrap->IsDoc())
+    return nsnull;
 
-    nsCOMPtr<nsIAccessibleDocument> accDocument;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessibleDocument),
-                            getter_AddRefs(accDocument));
-    NS_ENSURE_TRUE(accDocument, nsnull);
-
-    // according to atkobject.h, AtkAttributeSet is a GSList
-    GSList *attributes = nsnull;
+  // according to atkobject.h, AtkAttributeSet is a GSList
+  GSList* attributes = nsnull;
+  nsDocAccessible* document = accWrap->AsDoc();
+  nsAutoString aURL;
+  nsresult rv = document->GetURL(aURL);
+  if (NS_SUCCEEDED(rv))
+    attributes = prependToList(attributes, kDocUrlName, aURL);
 
-    nsAutoString aURL;
-    nsresult rv = accDocument->GetURL(aURL);
-    if (NS_SUCCEEDED(rv)) {
-        attributes = prependToList(attributes, kDocUrlName, aURL);
-    }
-    nsAutoString aW3CDocType;
-    rv = accDocument->GetDocType(aW3CDocType);
-    if (NS_SUCCEEDED(rv)) {
-        attributes = prependToList(attributes, kDocTypeName, aW3CDocType);
-    }
-    nsAutoString aMimeType;
-    rv = accDocument->GetMimeType(aMimeType);
-    if (NS_SUCCEEDED(rv)) {
-        attributes = prependToList(attributes, kMimeTypeName, aMimeType);
-    }
-    
-    return attributes;
+  nsAutoString aW3CDocType;
+  rv = document->GetDocType(aW3CDocType);
+  if (NS_SUCCEEDED(rv))
+    attributes = prependToList(attributes, kDocTypeName, aW3CDocType);
+
+  nsAutoString aMimeType;
+  rv = document->GetMimeType(aMimeType);
+  if (NS_SUCCEEDED(rv))
+    attributes = prependToList(attributes, kMimeTypeName, aMimeType);
+
+  return attributes;
 }
 
 const gchar *
 getDocumentAttributeValueCB(AtkDocument *aDocument,
                             const gchar *aAttrName)
 {
-    nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
-    if (!accWrap)
-        return nsnull;
-
-    nsCOMPtr<nsIAccessibleDocument> accDocument;
-    accWrap->QueryInterface(NS_GET_IID(nsIAccessibleDocument),
-                            getter_AddRefs(accDocument));
-    NS_ENSURE_TRUE(accDocument, nsnull);
+  nsAccessibleWrap* accWrap = GetAccessibleWrap(ATK_OBJECT(aDocument));
+  if (!accWrap || !accWrap->IsDoc())
+    return nsnull;
 
-    nsresult rv;
-    nsAutoString attrValue;
-    if (!g_ascii_strcasecmp(aAttrName, kDocTypeName)) {
-        rv = accDocument->GetDocType(attrValue);
-        NS_ENSURE_SUCCESS(rv, nsnull);
-    }
-    else if (!g_ascii_strcasecmp(aAttrName, kDocUrlName)) {
-        rv = accDocument->GetURL(attrValue);
-        NS_ENSURE_SUCCESS(rv, nsnull);
-    }
-    else if (!g_ascii_strcasecmp(aAttrName, kMimeTypeName)) {
-        rv = accDocument->GetMimeType(attrValue);
-        NS_ENSURE_SUCCESS(rv, nsnull);
-    }
-    else {
-        return nsnull;
-    }
-    return nsAccessibleWrap::ReturnString(attrValue);
+  nsDocAccessible* document = accWrap->AsDoc();
+  nsresult rv;
+  nsAutoString attrValue;
+  if (!strcasecmp(aAttrName, kDocTypeName))
+    rv = document->GetDocType(attrValue);
+  else if (!strcasecmp(aAttrName, kDocUrlName))
+    rv = document->GetURL(attrValue);
+  else if (!strcasecmp(aAttrName, kMimeTypeName))
+    rv = document->GetMimeType(attrValue);
+  else
+    return nsnull;
+
+  NS_ENSURE_SUCCESS(rv, nsnull);
+  return attrValue.IsEmpty() ? nsnull : nsAccessibleWrap::ReturnString(attrValue);
 }
+}
--- a/accessible/src/atk/nsMaiInterfaceDocument.h
+++ b/accessible/src/atk/nsMaiInterfaceDocument.h
@@ -36,22 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __MAI_INTERFACE_DOCUMENT_H__
 #define __MAI_INTERFACE_DOCUMENT_H__
 
 #include "nsMai.h"
-#include "nsIAccessibleDocument.h"
 
 G_BEGIN_DECLS
 
 /* document interface callbacks */
 void documentInterfaceInitCB(AtkDocumentIface *aIface);
-AtkAttributeSet* getDocumentAttributesCB(AtkDocument *aDocument);
-const gchar* getDocumentLocaleCB(AtkDocument *aDocument);
-const gchar* getDocumentAttributeValueCB(AtkDocument *aDocument,
-                                         const gchar *aAttrName);
 
 G_END_DECLS
 
 #endif /* __MAI_INTERFACE_DOCUMENT_H__ */
--- a/accessible/src/atk/nsMaiInterfaceHypertext.cpp
+++ b/accessible/src/atk/nsMaiInterfaceHypertext.cpp
@@ -33,17 +33,16 @@
  * decision by deleting the provisions above and replace them with the notice
  * 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 ***** */
 
 #include "nsMaiInterfaceHypertext.h"
-#include "nsIAccessibleDocument.h"
 #include "nsHyperTextAccessible.h"
 
 void
 hypertextInterfaceInitCB(AtkHypertextIface *aIface)
 {
     g_return_if_fail(aIface != NULL);
 
     aIface->get_link = getLinkCB;
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -603,25 +603,22 @@ nsAccUtils::GetLiveAttrValue(PRUint32 aR
   return false;
 }
 
 #ifdef DEBUG_A11Y
 
 bool
 nsAccUtils::IsTextInterfaceSupportCorrect(nsAccessible *aAccessible)
 {
+  // Don't test for accessible docs, it makes us create accessibles too
+  // early and fire mutation events before we need to
+  if (aAccessible->IsDoc())
+    return true;
+
   bool foundText = false;
-  
-  nsCOMPtr<nsIAccessibleDocument> accDoc = do_QueryObject(aAccessible);
-  if (accDoc) {
-    // Don't test for accessible docs, it makes us create accessibles too
-    // early and fire mutation events before we need to
-    return true;
-  }
-
   PRInt32 childCount = aAccessible->GetChildCount();
   for (PRint32 childIdx = 0; childIdx < childCount; childIdx++) {
     nsAccessible *child = GetChildAt(childIdx);
     if (IsText(child)) {
       foundText = true;
       break;
     }
   }
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -36,17 +36,16 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef nsAccUtils_h_
 #define nsAccUtils_h_
 
 #include "nsIAccessible.h"
 #include "nsIAccessNode.h"
-#include "nsIAccessibleDocument.h"
 #include "nsIAccessibleRole.h"
 #include "nsIAccessibleText.h"
 #include "nsIAccessibleTable.h"
 
 #include "nsARIAMap.h"
 #include "nsAccessibilityService.h"
 #include "nsCoreUtils.h"
 
--- a/accessible/src/base/nsAccessNode.cpp
+++ b/accessible/src/base/nsAccessNode.cpp
@@ -42,17 +42,16 @@
 
 #include "nsAccCache.h"
 #include "nsAccUtils.h"
 #include "nsCoreUtils.h"
 
 #include "nsHashtable.h"
 #include "nsAccessibilityService.h"
 #include "nsApplicationAccessibleWrap.h"
-#include "nsIAccessibleDocument.h"
 #include "nsIDocShell.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIDocument.h"
 #include "nsIDOMCSSPrimitiveValue.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLElement.h"
 #include "nsIDOMWindow.h"
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -1001,17 +1001,17 @@ nsIFrame* nsAccessible::GetBoundsFrame()
 /* void removeSelection (); */
 NS_IMETHODIMP nsAccessible::SetSelected(bool aSelect)
 {
   // Add or remove selection
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   if (State() & states::SELECTABLE) {
-    nsCOMPtr<nsIAccessible> multiSelect =
+    nsAccessible* multiSelect =
       nsAccUtils::GetMultiSelectableContainer(mContent);
     if (!multiSelect) {
       return aSelect ? TakeFocus() : NS_ERROR_FAILURE;
     }
 
     if (mRoleMapEntry) {
       if (aSelect) {
         return mContent->SetAttr(kNameSpaceID_None,
@@ -1029,22 +1029,21 @@ NS_IMETHODIMP nsAccessible::SetSelected(
 /* void takeSelection (); */
 NS_IMETHODIMP nsAccessible::TakeSelection()
 {
   // Select only this item
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   if (State() & states::SELECTABLE) {
-    nsCOMPtr<nsIAccessible> multiSelect =
+    nsAccessible* multiSelect =
       nsAccUtils::GetMultiSelectableContainer(mContent);
-    if (multiSelect) {
-      nsCOMPtr<nsIAccessibleSelectable> selectable = do_QueryInterface(multiSelect);
-      selectable->ClearSelection();
-    }
+    if (multiSelect)
+      multiSelect->ClearSelection();
+
     return SetSelected(true);
   }
 
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsAccessible::TakeFocus()
--- a/accessible/src/base/nsRootAccessible.h
+++ b/accessible/src/base/nsRootAccessible.h
@@ -36,17 +36,16 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsRootAccessible_H_
 #define _nsRootAccessible_H_
 
 #include "nsCaretAccessible.h"
 #include "nsDocAccessibleWrap.h"
 
-#include "nsIAccessibleDocument.h"
 #ifdef MOZ_XUL
 #include "nsXULTreeAccessible.h"
 #endif
 
 #include "nsHashtable.h"
 #include "nsCaretAccessible.h"
 #include "nsIDocument.h"
 #include "nsIDOMEventListener.h"
--- a/accessible/src/msaa/nsAccessibleWrap.cpp
+++ b/accessible/src/msaa/nsAccessibleWrap.cpp
@@ -43,17 +43,16 @@
 #include "nsCoreUtils.h"
 #include "nsWinUtils.h"
 #include "Relation.h"
 #include "Role.h"
 #include "States.h"
 
 #include "ia2AccessibleRelation.h"
 
-#include "nsIAccessibleDocument.h"
 #include "nsIAccessibleEvent.h"
 #include "nsIAccessibleRelation.h"
 #include "nsIAccessibleWin32Object.h"
 
 #include "Accessible2_i.c"
 #include "AccessibleStates.h"
 
 #include "nsIMutableArray.h"
--- a/accessible/src/xul/Makefile.in
+++ b/accessible/src/xul/Makefile.in
@@ -45,16 +45,17 @@ include $(DEPTH)/config/autoconf.mk
 
 MODULE = accessibility
 LIBRARY_NAME = accessibility_xul_s
 LIBXUL_LIBRARY = 1
 
 
 
 CPPSRCS = \
+  XULSelectControlAccessible.cpp \
   nsXULAlertAccessible.cpp \
   nsXULColorPickerAccessible.cpp \
   nsXULComboboxAccessible.cpp \
   nsXULFormControlAccessible.cpp \
   nsXULListboxAccessible.cpp \
   nsXULMenuAccessible.cpp \
   nsXULSliderAccessible.cpp \
   nsXULTabAccessible.cpp \
new file mode 100644
--- /dev/null
+++ b/accessible/src/xul/XULSelectControlAccessible.cpp
@@ -0,0 +1,309 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jignesh Kakadiya (jigneshhk1992@gmail.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "XULSelectControlAccessible.h"
+
+#include "nsAccessibilityService.h"
+#include "nsDocAccessible.h"
+
+#include "nsIDOMXULContainerElement.h"
+#include "nsIDOMXULSelectCntrlItemEl.h"
+#include "nsIDOMXULMultSelectCntrlEl.h"
+#include "nsIDOMKeyEvent.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMXULElement.h"
+#include "nsIMutableArray.h"
+#include "nsIServiceManager.h"
+
+#include "mozilla/dom/Element.h"
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible
+////////////////////////////////////////////////////////////////////////////////
+
+XULSelectControlAccessible::
+  XULSelectControlAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
+  nsAccessibleWrap(aContent, aShell)
+{
+  mSelectControl = do_QueryInterface(aContent);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible: nsAccessNode
+
+void
+XULSelectControlAccessible::Shutdown()
+{
+  mSelectControl = nsnull;
+  nsAccessibleWrap::Shutdown();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible: SelectAccessible
+
+bool
+XULSelectControlAccessible::IsSelect()
+{
+  return !!mSelectControl;
+}
+
+// Interface methods
+already_AddRefed<nsIArray>
+XULSelectControlAccessible::SelectedItems()
+{
+  nsCOMPtr<nsIMutableArray> selectedItems =
+    do_CreateInstance(NS_ARRAY_CONTRACTID);
+  if (!selectedItems)
+    return nsnull;
+
+  // For XUL multi-select control
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> xulMultiSelect =
+    do_QueryInterface(mSelectControl);
+  if (xulMultiSelect) {
+    PRInt32 length = 0;
+    xulMultiSelect->GetSelectedCount(&length);
+    for (PRInt32 index = 0; index < length; index++) {
+      nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
+      xulMultiSelect->GetSelectedItem(index, getter_AddRefs(itemElm));
+      nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
+      nsAccessible* item =
+        GetAccService()->GetAccessibleInWeakShell(itemNode, mWeakShell);
+      if (item)
+        selectedItems->AppendElement(static_cast<nsIAccessible*>(item),
+                                     false);
+    }
+  } else {  // Single select?
+      nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
+      mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
+      nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
+      if(itemNode) {
+        nsAccessible* item =
+          GetAccService()->GetAccessibleInWeakShell(itemNode, mWeakShell);
+        if (item)
+          selectedItems->AppendElement(static_cast<nsIAccessible*>(item),
+                                     false);
+      }
+    }
+
+  nsIMutableArray* items = nsnull;
+  selectedItems.forget(&items);
+  return items;
+}
+
+nsAccessible*
+XULSelectControlAccessible::GetSelectedItem(PRUint32 aIndex)
+{
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
+  if (multiSelectControl)
+    multiSelectControl->GetSelectedItem(aIndex, getter_AddRefs(itemElm));
+  else if (aIndex == 0)
+    mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
+
+  nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
+  return itemNode ?
+    GetAccService()->GetAccessibleInWeakShell(itemNode, mWeakShell) : nsnull;
+}
+
+PRUint32
+XULSelectControlAccessible::SelectedItemCount()
+{
+  // For XUL multi-select control
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+  if (multiSelectControl) {
+    PRInt32 count = 0;
+    multiSelectControl->GetSelectedCount(&count);
+    return count;
+  }
+
+  // For XUL single-select control/menulist
+  PRInt32 index;
+  mSelectControl->GetSelectedIndex(&index);
+  return (index >= 0) ? 1 : 0;
+}
+
+bool
+XULSelectControlAccessible::AddItemToSelection(PRUint32 aIndex)
+{
+  nsAccessible* item = GetChildAt(aIndex);
+  if (!item)
+    return false;
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+    do_QueryInterface(item->GetContent());
+  if (!itemElm)
+    return false;
+
+  bool isItemSelected = false;
+  itemElm->GetSelected(&isItemSelected);
+  if (isItemSelected)
+    return true;
+
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+
+  if (multiSelectControl)
+    multiSelectControl->AddItemToSelection(itemElm);
+  else
+    mSelectControl->SetSelectedItem(itemElm);
+
+  return true;
+}
+
+bool
+XULSelectControlAccessible::RemoveItemFromSelection(PRUint32 aIndex)
+{
+  nsAccessible* item = GetChildAt(aIndex);
+  if (!item)
+    return false;
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+      do_QueryInterface(item->GetContent());
+  if (!itemElm)
+    return false;
+
+  bool isItemSelected = false;
+  itemElm->GetSelected(&isItemSelected);
+  if (!isItemSelected)
+    return true;
+
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+
+  if (multiSelectControl)
+    multiSelectControl->RemoveItemFromSelection(itemElm);
+  else
+    mSelectControl->SetSelectedItem(nsnull);
+
+  return true;
+}
+
+bool
+XULSelectControlAccessible::IsItemSelected(PRUint32 aIndex)
+{
+  nsAccessible* item = GetChildAt(aIndex);
+  if (!item)
+    return false;
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+    do_QueryInterface(item->GetContent());
+  if (!itemElm)
+    return false;
+
+  bool isItemSelected = false;
+  itemElm->GetSelected(&isItemSelected);
+  return isItemSelected;
+}
+
+bool
+XULSelectControlAccessible::UnselectAll()
+{
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+  multiSelectControl ?
+    multiSelectControl->ClearSelection() : mSelectControl->SetSelectedIndex(-1);
+
+  return true;
+}
+
+bool
+XULSelectControlAccessible::SelectAll()
+{
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+  if (multiSelectControl) {
+    multiSelectControl->SelectAll();
+    return true;
+  }
+
+  // otherwise, don't support this method
+  return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// XULSelectControlAccessible: Widgets
+
+nsAccessible*
+XULSelectControlAccessible::CurrentItem()
+{
+  if (!mSelectControl)
+    return nsnull;
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> currentItemElm;
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+  if (multiSelectControl)
+    multiSelectControl->GetCurrentItem(getter_AddRefs(currentItemElm));
+  else
+    mSelectControl->GetSelectedItem(getter_AddRefs(currentItemElm));
+
+  nsCOMPtr<nsINode> DOMNode;
+  if (currentItemElm)
+    DOMNode = do_QueryInterface(currentItemElm);
+
+  if (DOMNode) {
+    nsDocAccessible* document = GetDocAccessible();
+    if (document)
+      return document->GetAccessible(DOMNode);
+  }
+
+  return nsnull;
+}
+
+void
+XULSelectControlAccessible::SetCurrentItem(nsAccessible* aItem)
+{
+  if (!mSelectControl)
+    return;
+
+  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
+    do_QueryInterface(aItem->GetContent());
+  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
+    do_QueryInterface(mSelectControl);
+  if (multiSelectControl)
+    multiSelectControl->SetCurrentItem(itemElm);
+  else
+    mSelectControl->SetSelectedItem(itemElm);
+}
new file mode 100644
--- /dev/null
+++ b/accessible/src/xul/XULSelectControlAccessible.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jignesh Kakadiya (jigneshhk1992@gmail.com)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef _XULSelectControlAccessible_H_
+#define _XULSelectControlAccessible_H_
+
+#include "nsAccessibleWrap.h"
+#include "nsIDOMXULSelectCntrlEl.h"
+
+/**
+ * The basic implementation of accessible selection for XUL select controls.
+ */
+class XULSelectControlAccessible : public nsAccessibleWrap
+{
+public:
+  XULSelectControlAccessible(nsIContent *aContent, nsIWeakReference *aShell);
+  virtual ~XULSelectControlAccessible() {}
+
+  // nsAccessNode
+  virtual void Shutdown();
+
+  // SelectAccessible
+  virtual bool IsSelect();
+  virtual already_AddRefed<nsIArray> SelectedItems();
+  virtual PRUint32 SelectedItemCount();
+  virtual nsAccessible* GetSelectedItem(PRUint32 aIndex);
+  virtual bool IsItemSelected(PRUint32 aIndex);
+  virtual bool AddItemToSelection(PRUint32 aIndex);
+  virtual bool RemoveItemFromSelection(PRUint32 aIndex);
+  virtual bool SelectAll();
+  virtual bool UnselectAll();
+
+  // Widgets
+  virtual nsAccessible* CurrentItem();
+  virtual void SetCurrentItem(nsAccessible* aItem);
+
+protected:
+  // nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have
+  // one of these if the widget is valid and not defunct
+  nsCOMPtr<nsIDOMXULSelectControlElement> mSelectControl;
+};
+
+#endif
+
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -554,17 +554,17 @@ nsXULRadioButtonAccessible::ContainerWid
   *   focus whereas the Buttons do not. So we only have an accessible object for
   *   this for the purpose of getting the proper RadioButton. Need this here to 
   *   avoid circular reference problems when navigating the accessible tree and
   *   for getting to the radiobuttons.
   */
 
 nsXULRadioGroupAccessible::
   nsXULRadioGroupAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
-  nsXULSelectableAccessible(aContent, aShell)
+  XULSelectControlAccessible(aContent, aShell)
 { 
 }
 
 role
 nsXULRadioGroupAccessible::NativeRole()
 {
   return roles::GROUPING;
 }
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -38,18 +38,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsXULFormControlAccessible_H_
 #define _nsXULFormControlAccessible_H_
 
 // NOTE: alphabetically ordered
 #include "nsAccessibleWrap.h"
 #include "nsFormControlAccessible.h"
-#include "nsXULMenuAccessible.h"
 #include "nsHyperTextAccessibleWrap.h"
+#include "XULSelectControlAccessible.h"
 
 /**
  * Used for XUL progressmeter element.
  */
 typedef ProgressMeterAccessible<100> XULProgressMeterAccessible;
 
 /**
  * Used for XUL button.
@@ -168,17 +168,17 @@ public:
 
   // Widgets
   virtual nsAccessible* ContainerWidget() const;
 };
 
 /**
  * Used for XUL radiogroup element.
  */
-class nsXULRadioGroupAccessible : public nsXULSelectableAccessible
+class nsXULRadioGroupAccessible : public XULSelectControlAccessible
 {
 public:
   nsXULRadioGroupAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsAccessible
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
 
@@ -279,10 +279,10 @@ protected:
   // nsHyperTextAccessible
   virtual already_AddRefed<nsFrameSelection> FrameSelection();
 
   // nsXULTextFieldAccessible
   already_AddRefed<nsIContent> GetInputField() const;
 };
 
 
-#endif  
+#endif
 
--- a/accessible/src/xul/nsXULListboxAccessible.cpp
+++ b/accessible/src/xul/nsXULListboxAccessible.cpp
@@ -128,34 +128,34 @@ nsXULColumnItemAccessible::DoAction(PRUi
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULListboxAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULListboxAccessible::
   nsXULListboxAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
-  nsXULSelectableAccessible(aContent, aShell)
+  XULSelectControlAccessible(aContent, aShell)
 {
   nsIContent* parentContent = mContent->GetParent();
   if (parentContent) {
     nsCOMPtr<nsIAutoCompletePopup> autoCompletePopupElm =
       do_QueryInterface(parentContent);
     if (autoCompletePopupElm)
       mFlags |= eAutoCompletePopupAccessible;
   }
 }
 
-NS_IMPL_ADDREF_INHERITED(nsXULListboxAccessible, nsXULSelectableAccessible)
-NS_IMPL_RELEASE_INHERITED(nsXULListboxAccessible, nsXULSelectableAccessible)
+NS_IMPL_ADDREF_INHERITED(nsXULListboxAccessible, XULSelectControlAccessible)
+NS_IMPL_RELEASE_INHERITED(nsXULListboxAccessible, XULSelectControlAccessible)
 
 nsresult
 nsXULListboxAccessible::QueryInterface(REFNSIID aIID, void** aInstancePtr)
 {
-  nsresult rv = nsXULSelectableAccessible::QueryInterface(aIID, aInstancePtr);
+  nsresult rv = XULSelectControlAccessible::QueryInterface(aIID, aInstancePtr);
   if (*aInstancePtr)
     return rv;
 
   if (aIID.Equals(NS_GET_IID(nsIAccessibleTable)) && IsMulticolumn()) {
     *aInstancePtr = static_cast<nsIAccessibleTable*>(this);
     NS_ADDREF_THIS();
     return NS_OK;
   }
--- a/accessible/src/xul/nsXULListboxAccessible.h
+++ b/accessible/src/xul/nsXULListboxAccessible.h
@@ -40,16 +40,17 @@
 #ifndef __nsXULListboxAccessible_h__
 #define __nsXULListboxAccessible_h__
 
 #include "nsIAccessibleTable.h"
 
 #include "nsCOMPtr.h"
 #include "nsXULMenuAccessible.h"
 #include "nsBaseWidgetAccessible.h"
+#include "XULSelectControlAccessible.h"
 
 class nsIWeakReference;
 
 /**
  * nsXULColumnsAccessible are accessible for list and tree columns elements
  * (xul:treecols and xul:listcols).
  */
 class nsXULColumnsAccessible : public nsAccessibleWrap
@@ -83,17 +84,17 @@ public:
   virtual PRUint8 ActionCount();
 
   enum { eAction_Click = 0 };
 };
 
 /*
  * A class the represents the XUL Listbox widget.
  */
-class nsXULListboxAccessible : public nsXULSelectableAccessible,
+class nsXULListboxAccessible : public XULSelectControlAccessible,
                                public nsIAccessibleTable
 {
 public:
   nsXULListboxAccessible(nsIContent *aContent, nsIWeakReference *aShell);
   virtual ~nsXULListboxAccessible() {}
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIACCESSIBLETABLE
--- a/accessible/src/xul/nsXULMenuAccessible.cpp
+++ b/accessible/src/xul/nsXULMenuAccessible.cpp
@@ -62,269 +62,16 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/dom/Element.h"
 
 using namespace mozilla;
 using namespace mozilla::a11y;
 
 ////////////////////////////////////////////////////////////////////////////////
-// nsXULSelectableAccessible
-////////////////////////////////////////////////////////////////////////////////
-
-nsXULSelectableAccessible::
-  nsXULSelectableAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
-  nsAccessibleWrap(aContent, aShell)
-{
-  mSelectControl = do_QueryInterface(aContent);
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsXULSelectableAccessible: nsAccessNode
-
-void
-nsXULSelectableAccessible::Shutdown()
-{
-  mSelectControl = nsnull;
-  nsAccessibleWrap::Shutdown();
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsXULSelectableAccessible: SelectAccessible
-
-bool
-nsXULSelectableAccessible::IsSelect()
-{
-  return !!mSelectControl;
-}
-
-// Interface methods
-already_AddRefed<nsIArray>
-nsXULSelectableAccessible::SelectedItems()
-{
-  nsCOMPtr<nsIMutableArray> selectedItems =
-    do_CreateInstance(NS_ARRAY_CONTRACTID);
-  if (!selectedItems)
-    return nsnull;
-
-  // For XUL multi-select control
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> xulMultiSelect =
-    do_QueryInterface(mSelectControl);
-  if (xulMultiSelect) {
-    PRInt32 length = 0;
-    xulMultiSelect->GetSelectedCount(&length);
-    for (PRInt32 index = 0; index < length; index++) {
-      nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-      xulMultiSelect->GetSelectedItem(index, getter_AddRefs(itemElm));
-      nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
-      nsAccessible* item =
-        GetAccService()->GetAccessibleInWeakShell(itemNode, mWeakShell);
-      if (item)
-        selectedItems->AppendElement(static_cast<nsIAccessible*>(item),
-                                     false);
-    }
-  }
-  else {  // Single select?
-    nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-    mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
-    nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
-    if(itemNode) {
-      nsAccessible* item =
-        GetAccService()->GetAccessibleInWeakShell(itemNode, mWeakShell);
-      if (item)
-        selectedItems->AppendElement(static_cast<nsIAccessible*>(item),
-                                     false);
-    }
-  }
-
-  nsIMutableArray* items = nsnull;
-  selectedItems.forget(&items);
-  return items;
-}
-
-nsAccessible*
-nsXULSelectableAccessible::GetSelectedItem(PRUint32 aIndex)
-{
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm;
-  if (multiSelectControl)
-    multiSelectControl->GetSelectedItem(aIndex, getter_AddRefs(itemElm));
-  else if (aIndex == 0)
-    mSelectControl->GetSelectedItem(getter_AddRefs(itemElm));
-
-  nsCOMPtr<nsINode> itemNode(do_QueryInterface(itemElm));
-  return itemNode ?
-    GetAccService()->GetAccessibleInWeakShell(itemNode, mWeakShell) : nsnull;
-}
-
-PRUint32
-nsXULSelectableAccessible::SelectedItemCount()
-{
-  // For XUL multi-select control
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-  if (multiSelectControl) {
-    PRInt32 count = 0;
-    multiSelectControl->GetSelectedCount(&count);
-    return count;
-  }
-
-  // For XUL single-select control/menulist
-  PRInt32 index;
-  mSelectControl->GetSelectedIndex(&index);
-  return (index >= 0) ? 1 : 0;
-}
-
-bool
-nsXULSelectableAccessible::AddItemToSelection(PRUint32 aIndex)
-{
-  nsAccessible* item = GetChildAt(aIndex);
-  if (!item)
-    return false;
-
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-    do_QueryInterface(item->GetContent());
-  if (!itemElm)
-    return false;
-
-  bool isItemSelected = false;
-  itemElm->GetSelected(&isItemSelected);
-  if (isItemSelected)
-    return true;
-
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-
-  if (multiSelectControl)
-    multiSelectControl->AddItemToSelection(itemElm);
-  else
-    mSelectControl->SetSelectedItem(itemElm);
-
-  return true;
-}
-
-bool
-nsXULSelectableAccessible::RemoveItemFromSelection(PRUint32 aIndex)
-{
-  nsAccessible* item = GetChildAt(aIndex);
-  if (!item)
-    return false;
-
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-      do_QueryInterface(item->GetContent());
-  if (!itemElm)
-    return false;
-
-  bool isItemSelected = false;
-  itemElm->GetSelected(&isItemSelected);
-  if (!isItemSelected)
-    return true;
-
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-
-  if (multiSelectControl)
-    multiSelectControl->RemoveItemFromSelection(itemElm);
-  else
-    mSelectControl->SetSelectedItem(nsnull);
-
-  return true;
-}
-
-bool
-nsXULSelectableAccessible::IsItemSelected(PRUint32 aIndex)
-{
-  nsAccessible* item = GetChildAt(aIndex);
-  if (!item)
-    return false;
-
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-    do_QueryInterface(item->GetContent());
-  if (!itemElm)
-    return false;
-
-  bool isItemSelected = false;
-  itemElm->GetSelected(&isItemSelected);
-  return isItemSelected;
-}
-
-bool
-nsXULSelectableAccessible::UnselectAll()
-{
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-  multiSelectControl ?
-    multiSelectControl->ClearSelection() : mSelectControl->SetSelectedIndex(-1);
-
-  return true;
-}
-
-bool
-nsXULSelectableAccessible::SelectAll()
-{
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-  if (multiSelectControl) {
-    multiSelectControl->SelectAll();
-    return true;
-  }
-
-  // otherwise, don't support this method
-  return false;
-}
-
-////////////////////////////////////////////////////////////////////////////////
-// nsXULSelectableAccessible: Widgets
-
-nsAccessible*
-nsXULSelectableAccessible::CurrentItem()
-{
-  if (!mSelectControl)
-    return nsnull;
-
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> currentItemElm;
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-  if (multiSelectControl)
-    multiSelectControl->GetCurrentItem(getter_AddRefs(currentItemElm));
-  else
-    mSelectControl->GetSelectedItem(getter_AddRefs(currentItemElm));
-
-  nsCOMPtr<nsINode> DOMNode;
-  if (currentItemElm)
-    DOMNode = do_QueryInterface(currentItemElm);
-
-  if (DOMNode) {
-    nsDocAccessible* document = GetDocAccessible();
-    if (document)
-      return document->GetAccessible(DOMNode);
-  }
-
-  return nsnull;
-}
-
-void
-nsXULSelectableAccessible::SetCurrentItem(nsAccessible* aItem)
-{
-  if (!mSelectControl)
-    return;
-
-  nsCOMPtr<nsIDOMXULSelectControlItemElement> itemElm =
-    do_QueryInterface(aItem->GetContent());
-  nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelectControl =
-    do_QueryInterface(mSelectControl);
-  if (multiSelectControl)
-    multiSelectControl->SetCurrentItem(itemElm);
-  else
-    mSelectControl->SetSelectedItem(itemElm);
-}
-
-////////////////////////////////////////////////////////////////////////////////
 // nsXULMenuitemAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenuitemAccessible::
   nsXULMenuitemAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
   nsAccessibleWrap(aContent, aShell)
 {
 }
@@ -703,17 +450,17 @@ nsXULMenuSeparatorAccessible::ActionCoun
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULMenupopupAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULMenupopupAccessible::
   nsXULMenupopupAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
-  nsXULSelectableAccessible(aContent, aShell)
+  XULSelectControlAccessible(aContent, aShell)
 {
   nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(GetFrame());
   if (menuPopupFrame && menuPopupFrame->IsMenu())
     mFlags |= eMenuPopupAccessible;
 
   // May be the anonymous <menupopup> inside <menulist> (a combobox)
   mSelectControl = do_QueryInterface(mContent->GetParent());
 }
--- a/accessible/src/xul/nsXULMenuAccessible.h
+++ b/accessible/src/xul/nsXULMenuAccessible.h
@@ -36,49 +36,17 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsXULMenuAccessible_H_
 #define _nsXULMenuAccessible_H_
 
 #include "nsAccessibleWrap.h"
 #include "nsIDOMXULSelectCntrlEl.h"
-
-/**
- * The basic implementation of SelectAccessible for XUL select controls.
- */
-class nsXULSelectableAccessible : public nsAccessibleWrap
-{
-public:
-  nsXULSelectableAccessible(nsIContent *aContent, nsIWeakReference *aShell);
-  virtual ~nsXULSelectableAccessible() {}
-
-  // nsAccessNode
-  virtual void Shutdown();
-
-  // SelectAccessible
-  virtual bool IsSelect();
-  virtual already_AddRefed<nsIArray> SelectedItems();
-  virtual PRUint32 SelectedItemCount();
-  virtual nsAccessible* GetSelectedItem(PRUint32 aIndex);
-  virtual bool IsItemSelected(PRUint32 aIndex);
-  virtual bool AddItemToSelection(PRUint32 aIndex);
-  virtual bool RemoveItemFromSelection(PRUint32 aIndex);
-  virtual bool SelectAll();
-  virtual bool UnselectAll();
-
-  // Widgets
-  virtual nsAccessible* CurrentItem();
-  virtual void SetCurrentItem(nsAccessible* aItem);
-
-protected:
-  // nsIDOMXULMultiSelectControlElement inherits from this, so we'll always have
-  // one of these if the widget is valid and not defunct
-  nsCOMPtr<nsIDOMXULSelectControlElement> mSelectControl;
-};
+#include "XULSelectControlAccessible.h"
 
 /**
  * Used for XUL menu, menuitem elements.
  */
 class nsXULMenuitemAccessible : public nsAccessibleWrap
 {
 public:
   enum { eAction_Click = 0 };
@@ -131,17 +99,17 @@ public:
   // ActionAccessible
   virtual PRUint8 ActionCount();
 };
 
 
 /**
  * Used for XUL menupopup and panel.
  */
-class nsXULMenupopupAccessible : public nsXULSelectableAccessible
+class nsXULMenupopupAccessible : public XULSelectControlAccessible
 {
 public:
   nsXULMenupopupAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsAccessible
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
@@ -169,9 +137,9 @@ public:
 
   // Widget
   virtual bool IsActiveWidget() const;
   virtual bool AreItemsOperable() const;
   virtual nsAccessible* CurrentItem();
   virtual void SetCurrentItem(nsAccessible* aItem);
 };
 
-#endif  
+#endif
--- a/accessible/src/xul/nsXULTabAccessible.cpp
+++ b/accessible/src/xul/nsXULTabAccessible.cpp
@@ -174,17 +174,17 @@ nsXULTabAccessible::GetPositionAndSizeIn
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTabsAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXULTabsAccessible::
   nsXULTabsAccessible(nsIContent *aContent, nsIWeakReference *aShell) :
-  nsXULSelectableAccessible(aContent, aShell)
+  XULSelectControlAccessible(aContent, aShell)
 {
 }
 
 role
 nsXULTabsAccessible::NativeRole()
 {
   return roles::PAGETABLIST;
 }
--- a/accessible/src/xul/nsXULTabAccessible.h
+++ b/accessible/src/xul/nsXULTabAccessible.h
@@ -37,16 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef _nsXULTabAccessible_H_
 #define _nsXULTabAccessible_H_
 
 // NOTE: alphabetically ordered
 #include "nsBaseWidgetAccessible.h"
 #include "nsXULMenuAccessible.h"
+#include "XULSelectControlAccessible.h"
 
 /**
  * An individual tab, xul:tab element.
  */
 class nsXULTabAccessible : public nsAccessibleWrap
 {
 public:
   enum { eAction_Switch = 0 };
@@ -67,17 +68,17 @@ public:
   // ActionAccessible
   virtual PRUint8 ActionCount();
 };
 
 
 /**
  * A container of tab objects, xul:tabs element.
  */
-class nsXULTabsAccessible : public nsXULSelectableAccessible
+class nsXULTabsAccessible : public XULSelectControlAccessible
 {
 public:
   nsXULTabsAccessible(nsIContent *aContent, nsIWeakReference *aShell);
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& _retval);
 
   // nsAccessible
--- a/accessible/tests/mochitest/events/Makefile.in
+++ b/accessible/tests/mochitest/events/Makefile.in
@@ -40,32 +40,32 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/events
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
+# test_docload.xul, docload_wnd.xul, test_scroll.xul disabled for misusing <tabbrowser> (bug 715857)
+
 _TEST_FILES =\
 		docload_wnd.html \
-		docload_wnd.xul \
 		focus.html \
 		scroll.html \
 		test_aria_alert.html \
 		test_aria_menu.html \
 		test_aria_objattr.html \
 		test_aria_statechange.html \
 		test_attrs.html \
 		test_caretmove.html \
 		test_caretmove.xul \
 		test_coalescence.html \
 		test_contextmenu.html \
 		test_docload.html \
-		test_docload.xul \
 		test_dragndrop.html \
 		test_flush.html \
 		test_focus_aria_activedescendant.html \
 		test_focus_autocomplete.xul \
 		test_focus_browserui.xul \
 		test_focus_contextmenu.xul \
 		test_focus_controls.html \
 		test_focus_dialog.html \
@@ -76,17 +76,16 @@ include $(topsrcdir)/config/rules.mk
 		test_focus_menu.xul \
 		test_focus_name.html \
 		test_focus_selects.html \
 		test_focus_tabbox.xul \
 		test_focus_tree.xul \
 		test_menu.xul \
 		test_mutation.html \
 		test_mutation.xhtml \
-		test_scroll.xul \
 		test_selection_aria.html \
 		test_selection.html \
 		test_selection.xul \
 		test_statechange.html \
 		test_text_alg.html \
 		test_text.html \
 		test_textattrchange.html \
 		test_tree.xul \
--- a/accessible/tests/mochitest/name/Makefile.in
+++ b/accessible/tests/mochitest/name/Makefile.in
@@ -40,26 +40,26 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/name
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
+# test_nsRootAcc.xul, nsRootAcc_wnd.xul disabled for misusing <tabbrowser> (bug 715857)
+
 _TEST_FILES =\
 		general.css \
 		general.xbl \
 		markup.js \
-		nsRootAcc_wnd.xul \
 		test_button.html \
 		test_general.html \
 		test_general.xul \
 		test_link.html \
 		test_list.html \
 		test_markup.html \
-		test_nsRootAcc.xul \
 		test_tree.xul \
 		markuprules.xml \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/relations/Makefile.in
+++ b/accessible/tests/mochitest/relations/Makefile.in
@@ -40,18 +40,19 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/relations
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
+# test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857)
+
 _TEST_FILES =\
 		test_general.html \
 		test_general.xul \
-		test_tabbrowser.xul \
 		test_tree.xul \
 		test_update.html \
 		$(NULL)
 
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/a11y/$(relativesrcdir)
--- a/accessible/tests/mochitest/tree/Makefile.in
+++ b/accessible/tests/mochitest/tree/Makefile.in
@@ -40,16 +40,18 @@ DEPTH		= ../../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = accessible/tree
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
+# test_tabbrowser.xul disabled for misusing <tabbrowser> (bug 715857)
+
 _TEST_FILES =\
 		dockids.html \
 	$(warning test_applicationacc.xul temporarily disabled, see bug 561508) \
 		test_aria_globals.html \
 		test_aria_imgmap.html \
 		test_aria_presentation.html \
 		test_button.xul \
 		test_canvas.html \
@@ -65,17 +67,16 @@ include $(topsrcdir)/config/rules.mk
 		test_iframe.html \
 		test_img.html \
 		test_invalidationlist.html \
 		test_list.html \
 		test_map.html \
 		test_media.html \
 		test_select.html \
 		test_tabbox.xul \
-		test_tabbrowser.xul \
 		test_table.html \
 		test_tree.xul \
 		test_txtcntr.html \
 		test_txtctrl.html \
 		test_txtctrl.xul \
 		wnd.xul \
 		$(NULL)
 
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -151,16 +151,17 @@
 @BINPATH@/components/dom_system_b2g.xpt
 #endif
 @BINPATH@/components/dom_battery.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
+@BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
@@ -518,19 +519,17 @@
 @BINPATH@/res/entityTables/*
 #ifdef XP_MACOSX
 @BINPATH@/res/MainMenu.nib/
 #endif
 
 ; svg
 @BINPATH@/res/svg.css
 @BINPATH@/components/dom_svg.xpt
-#ifdef MOZ_SMIL
 @BINPATH@/components/dom_smil.xpt
-#endif
 
 ; [Personal Security Manager]
 ;
 @BINPATH@/@DLL_PREFIX@nssckbi@DLL_SUFFIX@
 @BINPATH@/components/pipboot.xpt
 @BINPATH@/components/pipnss.xpt
 @BINPATH@/components/pippki.xpt
 @BINPATH@/@DLL_PREFIX@nss3@DLL_SUFFIX@
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1028,19 +1028,16 @@ pref("devtools.inspector.enabled", true)
 pref("devtools.inspector.htmlHeight", 112);
 
 // Enable the style inspector
 pref("devtools.styleinspector.enabled", true);
 
 // Enable the Tilt inspector
 pref("devtools.tilt.enabled", true);
 
-// Enable the Tilt inspector even if WebGL capabilities are not detected
-pref("devtools.tilt.force-enabled", false);
-
 // Enable the rules view
 pref("devtools.ruleview.enabled", true);
 
 // Enable the Scratchpad tool.
 pref("devtools.scratchpad.enabled", true);
 
 // Enable the Style Editor.
 pref("devtools.styleeditor.enabled", true);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -40,17 +40,17 @@ tabbrowser {
 }
 
 .tabbrowser-tab:not([pinned]):not([fadein]) {
   max-width: 0.1px;
   min-width: 0.1px;
   opacity: 0 !important;
   -moz-transition: min-width 200ms ease-out,
                    max-width 250ms ease-out,
-                   opacity 50ms ease-out 100ms /* hide the tab for the last 100ms of the max-width transition */;
+                   opacity 50ms ease-out 180ms /* hide the tab for the last 20ms of the max-width transition */;
 }
 
 .tab-throbber:not([fadein]):not([pinned]),
 .tab-label:not([fadein]):not([pinned]),
 .tab-icon-image:not([fadein]):not([pinned]),
 .tab-close-button:not([fadein]):not([pinned]) {
   display: none;
 }
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -1009,18 +1009,18 @@
                          command="Inspector:Inspect"/>
           <arrowscrollbox id="inspector-breadcrumbs"
                           flex="1" orient="horizontal"
                           clicktoscroll="true"/>
           <hbox id="inspector-tools">
             <toolbarbutton id="inspector-3D-button"
                            class="devtools-toolbarbutton"
                            hidden="true"
-                           label="&inspect3DButton.label;"
-                           accesskey="&inspect3DButton.accesskey;"
+                           label="&inspect3DViewButton.label;"
+                           accesskey="&inspect3DViewButton.accesskey;"
                            command="Inspector:Tilt"/>
             <toolbarbutton id="inspector-style-button"
                            class="devtools-toolbarbutton"
                            label="&inspectStyleButton.label;"
                            accesskey="&inspectStyleButton.accesskey;"
                            command="Inspector:Sidebar"/>
             <!-- registered tools go here -->
           </hbox>
--- a/browser/base/content/syncSetup.xul
+++ b/browser/base/content/syncSetup.xul
@@ -403,16 +403,20 @@
                  accesskey="&syncComputerName.accesskey;"
                  control="syncComputerName"/>
           <textbox id="syncComputerName" flex="1"
                    onchange="gSyncUtils.changeName(this)"/>
         </row>
         <row>
           <label value="&syncMy.label;" />
           <vbox>
+            <checkbox label="&engine.addons.label;"
+                      accesskey="&engine.addons.accesskey;"
+                      id="engine.addons"
+                      checked="true"/>
             <checkbox label="&engine.bookmarks.label;"
                       accesskey="&engine.bookmarks.accesskey;"
                       id="engine.bookmarks"
                       checked="true"/>
             <checkbox label="&engine.passwords.label;"
                       accesskey="&engine.passwords.accesskey;"
                       id="engine.passwords"
                       checked="true"/>
@@ -423,20 +427,16 @@
             <checkbox label="&engine.history.label;"
                       accesskey="&engine.history.accesskey;"
                       id="engine.history"
                       checked="true"/>
             <checkbox label="&engine.tabs.label;"
                       accesskey="&engine.tabs.accesskey;"
                       id="engine.tabs"
                       checked="true"/>
-            <checkbox label="&engine.addons.label;"
-                      accesskey="&engine.addons.accesskey;"
-                      id="engine.addons"
-                      checked="true"/>
           </vbox>
         </row>
       </rows>
     </grid>
     </groupbox>
 
     <groupbox id="mergeOptions">
       <radiogroup id="mergeChoiceRadio" pack="start">
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2842,21 +2842,19 @@
           let visible = this.visible;
 
           document.getElementById("menu_closeWindow").hidden = !visible;
           document.getElementById("menu_close").setAttribute("label",
             this.tabbrowser.mStringBundle.getString(visible ? "tabs.closeTab" : "tabs.close"));
 
           goSetCommandEnabled("cmd_ToggleTabsOnTop", visible);
 
-          if (window.TabsOnTop)
-            TabsOnTop.syncUI();
-
-          if (window.TabsInTitlebar)
-            TabsInTitlebar.allowedBy("tabs-visible", visible);
+          TabsOnTop.syncUI();
+
+          TabsInTitlebar.allowedBy("tabs-visible", visible);
         ]]></body>
       </method>
 
       <method name="updateVisibility">
         <body><![CDATA[
           if (this.childNodes.length - this.tabbrowser._removingTabs.length == 1)
             this.visible = window.toolbar.visible &&
                            !Services.prefs.getBoolPref("browser.tabs.autoHide");
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -44,17 +44,16 @@
 #if defined(XP_WIN)
 #include "nsWindowsShellService.h"
 #elif defined(XP_MACOSX)
 #include "nsMacShellService.h"
 #elif defined(MOZ_WIDGET_GTK2)
 #include "nsGNOMEShellService.h"
 #endif
 
-#include "nsProfileMigrator.h"
 #if defined(XP_WIN) && !defined(__MINGW32__)
 #include "nsIEProfileMigrator.h"
 #elif defined(XP_MACOSX)
 #include "nsSafariProfileMigrator.h"
 #endif
 
 #include "rdf.h"
 #include "nsFeedSniffer.h"
@@ -72,17 +71,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(Directory
 #if defined(XP_WIN)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindowsShellService)
 #elif defined(XP_MACOSX)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsMacShellService)
 #elif defined(MOZ_WIDGET_GTK2)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsGNOMEShellService, Init)
 #endif
 
-NS_GENERIC_FACTORY_CONSTRUCTOR(nsProfileMigrator)
 #if defined(XP_WIN) && !defined(__MINGW32__)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsIEProfileMigrator)
 #elif defined(XP_MACOSX)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsSafariProfileMigrator)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFeedSniffer)
 
@@ -91,17 +89,16 @@ NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsPr
 NS_DEFINE_NAMED_CID(NS_BROWSERDIRECTORYPROVIDER_CID);
 #if defined(XP_WIN)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #elif defined(MOZ_WIDGET_GTK2)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_FEEDSNIFFER_CID);
 NS_DEFINE_NAMED_CID(NS_BROWSER_ABOUT_REDIRECTOR_CID);
-NS_DEFINE_NAMED_CID(NS_FIREFOX_PROFILEMIGRATOR_CID);
 #if defined(XP_WIN) && !defined(__MINGW32__)
 NS_DEFINE_NAMED_CID(NS_WINIEPROFILEMIGRATOR_CID);
 #elif defined(XP_MACOSX)
 NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_SAFARIPROFILEMIGRATOR_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID);
 
@@ -109,17 +106,16 @@ static const mozilla::Module::CIDEntry k
     { &kNS_BROWSERDIRECTORYPROVIDER_CID, false, NULL, DirectoryProviderConstructor },
 #if defined(XP_WIN)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor },
 #elif defined(MOZ_WIDGET_GTK2)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor },
 #endif
     { &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor },
     { &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, NULL, AboutRedirector::Create },
-    { &kNS_FIREFOX_PROFILEMIGRATOR_CID, false, NULL, nsProfileMigratorConstructor },
 #if defined(XP_WIN) && !defined(__MINGW32__)
     { &kNS_WINIEPROFILEMIGRATOR_CID, false, NULL, nsIEProfileMigratorConstructor },
 #elif defined(XP_MACOSX)
     { &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor },
     { &kNS_SAFARIPROFILEMIGRATOR_CID, false, NULL, nsSafariProfileMigratorConstructor },
 #endif
     { &kNS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID, false, NULL, nsPrivateBrowsingServiceWrapperConstructor },
     { NULL }
@@ -143,17 +139,16 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_SYNC
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-progress", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
-    { NS_PROFILEMIGRATOR_CONTRACTID, &kNS_FIREFOX_PROFILEMIGRATOR_CID },
 #if defined(XP_WIN) && !defined(__MINGW32__)
     { NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX "ie", &kNS_WINIEPROFILEMIGRATOR_CID },
 #elif defined(XP_MACOSX)
     { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
     { NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX "safari", &kNS_SAFARIPROFILEMIGRATOR_CID },
 #endif
     { NS_PRIVATE_BROWSING_SERVICE_CONTRACTID, &kNS_PRIVATE_BROWSING_SERVICE_WRAPPER_CID },
     { NULL }
--- a/browser/components/migration/src/BrowserProfileMigrators.manifest
+++ b/browser/components/migration/src/BrowserProfileMigrators.manifest
@@ -1,4 +1,6 @@
-component {4cec1de4-1671-4fc3-a53e-6c539dc77a26} ChromeProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=chrome {4cec1de4-1671-4fc3-a53e-6c539dc77a26}
-component {91185366-ba97-4438-acba-48deaca63386} FirefoxProfileMigrator.js
-contract @mozilla.org/profile/migrator;1?app=browser&type=firefox {91185366-ba97-4438-acba-48deaca63386}
+component {6F8BB968-C14F-4D6F-9733-6C6737B35DCE} ProfileMigrator.js
+contract @mozilla.org/toolkit/profile-migrator;1 {6F8BB968-C14F-4D6F-9733-6C6737B35DCE}
+component {4cec1de4-1671-4fc3-a53e-6c539dc77a26} ChromeProfileMigrator.js
+contract @mozilla.org/profile/migrator;1?app=browser&type=chrome {4cec1de4-1671-4fc3-a53e-6c539dc77a26}
+component {91185366-ba97-4438-acba-48deaca63386} FirefoxProfileMigrator.js
+contract @mozilla.org/profile/migrator;1?app=browser&type=firefox {91185366-ba97-4438-acba-48deaca63386}
--- a/browser/components/migration/src/Makefile.in
+++ b/browser/components/migration/src/Makefile.in
@@ -44,31 +44,31 @@ include $(DEPTH)/config/autoconf.mk
 MODULE		= migration
 LIBRARY_NAME	= migration_s
 FORCE_STATIC_LIB = 1
 ifndef MOZ_MEMORY
 USE_STATIC_LIBS = 1
 endif
 
 
-CPPSRCS  = nsProfileMigrator.cpp \
-           nsBrowserProfileMigratorUtils.cpp \
+CPPSRCS  = nsBrowserProfileMigratorUtils.cpp \
            $(NULL)
 
 ifeq ($(OS_ARCH)_$(GNU_CXX),WINNT_)
 CPPSRCS += nsIEProfileMigrator.cpp \
            $(NULL)
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS += nsSafariProfileMigrator.cpp \
            $(NULL)
 endif            
 
 EXTRA_PP_COMPONENTS = \
+  ProfileMigrator.js \
   ChromeProfileMigrator.js \
   FirefoxProfileMigrator.js \
   $(NULL)
 
 EXTRA_COMPONENTS = \
 	BrowserProfileMigrators.manifest \
 	$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/src/ProfileMigrator.js
@@ -0,0 +1,141 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/FileUtils.jsm");
+
+function ProfileMigrator() {
+}
+
+ProfileMigrator.prototype = {
+  migrate: function PM_migrate(aStartup) {
+    // By opening the wizard with a supplied migrator, it will automatically
+    // migrate from it.
+    let [key, migrator] = this._getDefaultMigrator();
+    if (!key)
+        return;
+
+    let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
+    params.appendElement(this._toCString(key), false);
+    params.appendElement(migrator, false);
+    params.appendElement(aStartup, false);
+
+    Services.ww.openWindow(null,
+                           "chrome://browser/content/migration/migration.xul",
+                           "_blank",
+                           "chrome,dialog,modal,centerscreen,titlebar",
+                           params);
+  },
+
+  _toCString: function PM__toCString(aStr) {
+    let cstr = Cc["@mozilla.org/supports-cstring;1"].
+               createInstance(Ci.nsISupportsCString);
+    cstr.data = aStr;
+    return cstr;
+  },
+
+  _getMigratorIfSourceExists: function PM__getMigratorIfSourceExists(aKey) {
+    let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
+    let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
+    if (migrator.sourceExists)
+      return migrator;
+    return null;
+  },
+
+  // We don't yet support checking for the default browser on all platforms,
+  // needless to say we don't have migrators for all browsers.  Thus, for each
+  // platform, there's a fallback list of migrators used in these cases.
+  _PLATFORM_FALLBACK_LIST:
+#ifdef XP_WIN
+     ["ie", "chrome", /* safari */],
+#elifdef XP_MACOSX
+     ["safari", "chrome"],
+#else
+     ["chrome"],
+#endif
+
+  _getDefaultMigrator: function PM__getDefaultMigrator() {
+    let migratorsOrdered = Array.slice(this._PLATFORM_FALLBACK_LIST);
+    let defaultBrowser = "";
+#ifdef XP_WIN
+    try {
+      const REG_KEY = "SOFTWARE\\Classes\\HTTP\\shell\\open\\command";
+      let regKey = Cc["@mozilla.org/windows-registry-key;1"].
+                   createInstance(Ci.nsIWindowsRegKey);
+      regKey.open(regKey.ROOT_KEY_LOCAL_MACHINE, REG_KEY,
+                  regKey.ACCESS_READ);
+      let value = regKey.readStringValue("").toLowerCase();      
+      let pathMatches = value.match(/^"?(.+?\.exe)"?/);
+      if (!pathMatches) {
+        throw new Error("Could not extract path from " +
+                        REG_KEY + "(" + value + ")");
+      }
+ 
+      // We want to find out what the default browser is but the path in and of
+      // itself isn't enough.  Why? Because sometimes on Windows paths get
+      // truncated like so: C:\PROGRA~1\MOZILL~2\MOZILL~1.EXE.  How do we know
+      // what product that is? Mozilla's file objects do nothing to 'normalize'
+      // the path so we need to attain an actual product descriptor from the
+      // file somehow, and in this case it means getting the "InternalName"
+      // field of the file's VERSIONINFO resource. 
+      //
+      // In the file's resource segment there is a VERSIONINFO section that is
+      // laid out like this:
+      //
+      // VERSIONINFO
+      //   StringFileInfo
+      //     <TranslationID>
+      //       InternalName           "iexplore"
+      //   VarFileInfo
+      //     Translation              <TranslationID>
+      //
+      // By Querying the VERSIONINFO section for its Tranlations, we can find
+      // out where the InternalName lives (A file can have more than one
+      // translation of its VERSIONINFO segment, but we just assume the first
+      // one).
+      let file = FileUtils.File(pathMatches[1])
+                          .QueryInterface(Ci.nsILocalFileWin);
+      switch (file.getVersionInfoField("InternalName").toLowerCase()) {
+        case "iexplore":
+          defaultBrowser = "ie";
+          break;
+        case "chrome":
+          defaultBrowser = "chrome";
+          break;
+      }
+    }
+    catch (ex) {
+      Cu.reportError("Could not retrieve default browser: " + ex);
+    }
+#endif
+
+    // If we found the default browser and we have support for that browser,
+    // make sure to check it before any other browser, by moving it to the head
+    // of the array.
+    if (defaultBrowser)
+      migratorsOrdered.sort(function(a, b) b == defaultBrowser ? 1 : 0);
+
+    for (let i = 0; i < migratorsOrdered.length; i++) {
+      let migrator = this._getMigratorIfSourceExists(migratorsOrdered[i]);
+      if (migrator)
+        return [migratorsOrdered[i], migrator];
+    }
+
+    return ["", null];
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIProfileMigrator]),
+  classDescription: "Profile Migrator",
+  contractID: "@mozilla.org/toolkit/profile-migrator;1",
+  classID: Components.ID("6F8BB968-C14F-4D6F-9733-6C6737B35DCE")
+};
+
+let NSGetFactory = XPCOMUtils.generateNSGetFactory([ProfileMigrator]);
deleted file mode 100644
--- a/browser/components/migration/src/nsProfileMigrator.cpp
+++ /dev/null
@@ -1,252 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is The Browser Profile Migrator.
- *
- * The Initial Developer of the Original Code is Ben Goodger.
- * Portions created by the Initial Developer are Copyright (C) 2004
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Ben Goodger <ben@bengoodger.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * 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 ***** */
-
-#include "nsProfileMigrator.h"
-
-#include "nsIBrowserProfileMigrator.h"
-#include "nsIComponentManager.h"
-#include "nsIDOMWindow.h"
-#include "nsILocalFile.h"
-#include "nsIObserverService.h"
-#include "nsIProperties.h"
-#include "nsIServiceManager.h"
-#include "nsISupportsPrimitives.h"
-#include "nsIMutableArray.h"
-#include "nsIToolkitProfile.h"
-#include "nsIToolkitProfileService.h"
-#include "nsIWindowWatcher.h"
-
-#include "nsCOMPtr.h"
-#include "nsBrowserCompsCID.h"
-#include "nsComponentManagerUtils.h"
-#include "nsDirectoryServiceDefs.h"
-#include "nsServiceManagerUtils.h"
-
-#include "nsStringAPI.h"
-#include "nsUnicharUtils.h"
-#ifdef XP_WIN
-#include <windows.h>
-#include "nsIWindowsRegKey.h"
-#include "nsILocalFileWin.h"
-#else
-#include <limits.h>
-#endif
-
-#include "nsAutoPtr.h"
-
-///////////////////////////////////////////////////////////////////////////////
-// nsIProfileMigrator
-
-#define MIGRATION_WIZARD_FE_URL "chrome://browser/content/migration/migration.xul"
-#define MIGRATION_WIZARD_FE_FEATURES "chrome,dialog,modal,centerscreen,titlebar"
-
-NS_IMETHODIMP
-nsProfileMigrator::Migrate(nsIProfileStartup* aStartup)
-{
-  nsresult rv;
-
-  nsCAutoString key;
-  nsCOMPtr<nsIBrowserProfileMigrator> bpm;
-
-  rv = GetDefaultBrowserMigratorKey(key, bpm);
-  if (NS_FAILED(rv)) return rv;
-
-  if (!bpm) {
-    nsCAutoString contractID(NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX);
-    contractID.Append(key);
-
-    bpm = do_CreateInstance(contractID.get());
-    if (!bpm) return NS_ERROR_FAILURE;
-  }
-
-  bool sourceExists;
-  bpm->GetSourceExists(&sourceExists);
-  if (!sourceExists) {
-#ifdef XP_WIN
-    // The "Default Browser" key in the registry was set to a browser for which
-    // no profile data exists. On Windows, this means the Default Browser settings
-    // in the registry are bad, and we should just fall back to IE in this case.
-    bpm = do_CreateInstance(NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX "ie");
-#else
-    return NS_ERROR_FAILURE;
-#endif
-  }
-
-  nsCOMPtr<nsISupportsCString> cstr
-    (do_CreateInstance("@mozilla.org/supports-cstring;1"));
-  if (!cstr) return NS_ERROR_OUT_OF_MEMORY;
-  cstr->SetData(key);
-
-  // By opening the Migration FE with a supplied bpm, it will automatically
-  // migrate from it. 
-  nsCOMPtr<nsIWindowWatcher> ww(do_GetService(NS_WINDOWWATCHER_CONTRACTID));
-  nsCOMPtr<nsIMutableArray> params = do_CreateInstance(NS_ARRAY_CONTRACTID);
-  if (!ww || !params) return NS_ERROR_FAILURE;
-
-  params->AppendElement(cstr, false);
-  params->AppendElement(bpm, false);
-  params->AppendElement(aStartup, false);
-
-  nsCOMPtr<nsIDOMWindow> migrateWizard;
-  return ww->OpenWindow(nsnull, 
-                        MIGRATION_WIZARD_FE_URL,
-                        "_blank",
-                        MIGRATION_WIZARD_FE_FEATURES,
-                        params,
-                        getter_AddRefs(migrateWizard));
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// nsProfileMigrator
-
-NS_IMPL_ISUPPORTS1(nsProfileMigrator, nsIProfileMigrator)
-
-#ifdef XP_WIN
-
-#define INTERNAL_NAME_IEXPLORE        "iexplore"
-#define INTERNAL_NAME_MOZILLA_SUITE   "apprunner"
-#define INTERNAL_NAME_CHROME          "chrome"
-#define INTERNAL_NAME_FIREFOX         "firefox"
-#endif
-
-nsresult
-nsProfileMigrator::GetDefaultBrowserMigratorKey(nsACString& aKey,
-                                                nsCOMPtr<nsIBrowserProfileMigrator>& bpm)
-{
-#if XP_WIN
-
-  nsCOMPtr<nsIWindowsRegKey> regKey = 
-    do_CreateInstance("@mozilla.org/windows-registry-key;1");
-  if (!regKey)
-    return NS_ERROR_FAILURE;
-
-  NS_NAMED_LITERAL_STRING(kCommandKey,
-                          "SOFTWARE\\Classes\\HTTP\\shell\\open\\command");
-
-  if (NS_FAILED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
-                             kCommandKey, nsIWindowsRegKey::ACCESS_READ)))
-    return NS_ERROR_FAILURE;
-
-  nsAutoString value;
-  if (NS_FAILED(regKey->ReadStringValue(EmptyString(), value)))
-    return NS_ERROR_FAILURE;
-
-  PRInt32 len = value.Find(NS_LITERAL_STRING(".exe"), CaseInsensitiveCompare);
-  if (len == -1)
-    return NS_ERROR_FAILURE;
-
-  // Move past ".exe"
-  len += 4;
-
-  PRUint32 start = 0;
-  // skip an opening quotation mark if present
-  if (value.get()[1] != ':') {
-    start = 1;
-    --len;
-  }
-
-  const nsDependentSubstring filePath(Substring(value, start, len)); 
-
-  // We want to find out what the default browser is but the path in and of itself
-  // isn't enough. Why? Because sometimes on Windows paths get truncated like so:
-  // C:\PROGRA~1\MOZILL~2\MOZILL~1.EXE
-  // How do we know what product that is? Mozilla or Mozilla Firebird? etc. Mozilla's
-  // file objects do nothing to 'normalize' the path so we need to attain an actual
-  // product descriptor from the file somehow, and in this case it means getting
-  // the "InternalName" field of the file's VERSIONINFO resource. 
-  //
-  // In the file's resource segment there is a VERSIONINFO section that is laid 
-  // out like this:
-  //
-  // VERSIONINFO
-  //   StringFileInfo
-  //     <TranslationID>
-  //       InternalName           "iexplore"
-  //   VarFileInfo
-  //     Translation              <TranslationID>
-  //
-  // By Querying the VERSIONINFO section for its Tranlations, we can find out where
-  // the InternalName lives. (A file can have more than one translation of its 
-  // VERSIONINFO segment, but we just assume the first one). 
-
-  nsCOMPtr<nsILocalFile> lf;
-  NS_NewLocalFile(filePath, true, getter_AddRefs(lf));
-  if (!lf)
-    return NS_ERROR_FAILURE;
-
-  nsCOMPtr<nsILocalFileWin> lfw = do_QueryInterface(lf); 
-  if (!lfw)
-    return NS_ERROR_FAILURE;
-
-  nsAutoString internalName;
-  if (NS_FAILED(lfw->GetVersionInfoField("InternalName", internalName)))
-    return NS_ERROR_FAILURE;
-
-  if (internalName.LowerCaseEqualsLiteral(INTERNAL_NAME_IEXPLORE)) {
-    aKey = "ie";
-    return NS_OK;
-  }
-  else if (internalName.LowerCaseEqualsLiteral(INTERNAL_NAME_CHROME)) {
-    aKey = "chrome";
-    return NS_OK;
-  }
-  else if (internalName.LowerCaseEqualsLiteral(INTERNAL_NAME_FIREFOX)) {
-    aKey = "firefox";
-    return NS_OK;
-  }
-
-#else
-  bool exists = false;
-#define CHECK_MIGRATOR(browser) do {\
-  bpm = do_CreateInstance(NS_BROWSERPROFILEMIGRATOR_CONTRACTID_PREFIX browser);\
-  if (bpm)\
-    bpm->GetSourceExists(&exists);\
-  if (exists) {\
-    aKey = browser;\
-    return NS_OK;\
-  }} while(0)
-
-#if defined(XP_MACOSX)
-  CHECK_MIGRATOR("safari");
-#endif
-  CHECK_MIGRATOR("chrome");
-  CHECK_MIGRATOR("firefox");
-
-#undef CHECK_MIGRATOR
-#endif
-  return NS_ERROR_FAILURE;
-}
deleted file mode 100644
--- a/browser/components/migration/src/nsProfileMigrator.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* ***** BEGIN LICENSE BLOCK *****
- * Version: MPL 1.1/GPL 2.0/LGPL 2.1
- *
- * The contents of this file are subject to the Mozilla Public License Version
- * 1.1 (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- * http://www.mozilla.org/MPL/
- *
- * Software distributed under the License is distributed on an "AS IS" basis,
- * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
- * for the specific language governing rights and limitations under the
- * License.
- *
- * The Original Code is The Browser Profile Migrator.
- *
- * The Initial Developer of the Original Code is Ben Goodger.
- * Portions created by the Initial Developer are Copyright (C) 2004
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *  Ben Goodger <ben@bengoodger.com>
- *
- * Alternatively, the contents of this file may be used under the terms of
- * either the GNU General Public License Version 2 or later (the "GPL"), or
- * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
- * in which case the provisions of the GPL or the LGPL are applicable instead
- * of those above. If you wish to allow use of your version of this file only
- * under the terms of either the GPL or the LGPL, and not to allow others to
- * use your version of this file under the terms of the MPL, indicate your
- * decision by deleting the provisions above and replace them with the notice
- * 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 ***** */
- 
-#ifndef profilemigrator___h___
-#define profilemigrator___h___
-
-#include "nsIBrowserProfileMigrator.h"
-#include "nsIProfileMigrator.h"
-#include "nsCOMPtr.h"
-
-#define NS_FIREFOX_PROFILEMIGRATOR_CID \
-{ 0x4ca3c946, 0x5408, 0x49f0, { 0x9e, 0xca, 0x3a, 0x97, 0xd5, 0xc6, 0x77, 0x50 } }
-
-class nsProfileMigrator : public nsIProfileMigrator
-{
-public:
-  NS_DECL_NSIPROFILEMIGRATOR
-  NS_DECL_ISUPPORTS
-
-  nsProfileMigrator() { }
-
-protected:
-  ~nsProfileMigrator() { }
-
-  nsresult GetDefaultBrowserMigratorKey(nsACString& key,
-                                        nsCOMPtr<nsIBrowserProfileMigrator>& bpm);
-};
-
-#endif
-
--- a/browser/components/preferences/main.js
+++ b/browser/components/preferences/main.js
@@ -366,17 +366,17 @@ var gMainPane = {
       // With 3.0, a new desktop folder - 'Downloads' was introduced for
       // platforms and versions that don't support a default system downloads
       // folder. See nsDownloadManager for details. 
       downloadFolder.label = bundlePreferences.getString("downloadsFolderName");
       iconUrlSpec = fph.getURLSpecFromFile(this._indexToFolder(1));
     } else {
       // 'Desktop'
       downloadFolder.label = bundlePreferences.getString("desktopFolderName");
-      iconUrlSpec = fph.getURLSpecFromFile(desk);
+      iconUrlSpec = fph.getURLSpecFromFile(this._getDownloadsFolder("Desktop"));
     }
     downloadFolder.image = "moz-icon://" + iconUrlSpec + "?size=16";
     
     // don't override the preference's value in UI
     return undefined;
   },
 
   /**
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -52,22 +52,22 @@
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
          xmlns:html="http://www.w3.org/1999/xhtml">
 
   <prefpane id="paneSync"
             helpTopic="prefs-weave"
             onpaneload="gSyncPane.init()">
 
     <preferences>
+      <preference id="engine.addons"    name="services.sync.engine.addons"    type="bool"/>
       <preference id="engine.bookmarks" name="services.sync.engine.bookmarks" type="bool"/>
       <preference id="engine.history"   name="services.sync.engine.history"   type="bool"/>
       <preference id="engine.tabs"      name="services.sync.engine.tabs"      type="bool"/>
       <preference id="engine.prefs"     name="services.sync.engine.prefs"     type="bool"/>
       <preference id="engine.passwords" name="services.sync.engine.passwords" type="bool"/>
-      <preference id="engine.addons"    name="services.sync.engine.addons"    type="bool"/>
     </preferences>
 
 
     <script type="application/javascript"
             src="chrome://browser/content/preferences/sync.js"/>
     <script type="application/javascript"
             src="chrome://browser/content/syncUtils.js"/>
 
@@ -124,16 +124,21 @@
             </hbox>
 
             <vbox>
               <label value="&syncMy.label;" />
               <richlistbox id="syncEnginesList"
                            orient="vertical"
                            onselect="if (this.selectedCount) this.clearSelection();">
                 <richlistitem>
+                  <checkbox label="&engine.addons.label;"
+                            accesskey="&engine.addons.accesskey;"
+                            preference="engine.addons"/>
+                </richlistitem>
+                <richlistitem>
                   <checkbox label="&engine.bookmarks.label;"
                             accesskey="&engine.bookmarks.accesskey;"
                             preference="engine.bookmarks"/>
                 </richlistitem>
                 <richlistitem>
                   <checkbox label="&engine.passwords.label;"
                             accesskey="&engine.passwords.accesskey;"
                             preference="engine.passwords"/>
@@ -148,21 +153,16 @@
                             accesskey="&engine.history.accesskey;"
                             preference="engine.history"/>
                 </richlistitem>
                 <richlistitem>
                   <checkbox label="&engine.tabs.label;"
                             accesskey="&engine.tabs.accesskey;"
                             preference="engine.tabs"/>
                 </richlistitem>
-                <richlistitem>
-                  <checkbox label="&engine.addons.label;"
-                            accesskey="&engine.addons.accesskey;"
-                            preference="engine.addons"/>
-                </richlistitem>
               </richlistbox>
             </vbox>
           </groupbox>
 
           <groupbox class="syncGroupBox">
             <grid>
               <columns>
                 <column/>
--- a/browser/devtools/highlighter/Makefile.in
+++ b/browser/devtools/highlighter/Makefile.in
@@ -44,15 +44,16 @@ VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
 	inspector.jsm \
 	domplate.jsm \
 	InsideOutBox.jsm \
 	TreePanel.jsm \
+	highlighter.jsm \
 	$(NULL)
 
 ifdef ENABLE_TESTS
  	DIRS += test
 endif
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/highlighter/TreePanel.jsm
+++ b/browser/devtools/highlighter/TreePanel.jsm
@@ -356,17 +356,17 @@ TreePanel.prototype = {
     if (node) {
       if (hitTwisty) {
         this.ioBox.toggleObject(node);
       } else {
         if (this.IUI.inspecting) {
           this.IUI.stopInspecting(true);
         } else {
           this.IUI.select(node, true, false);
-          this.IUI.highlighter.highlightNode(node);
+          this.IUI.highlighter.highlight(node);
         }
       }
     }
   },
 
   /**
    * Handle double-click events in the html tree panel.
    * (double-clicking an attribute value allows it to be edited)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/highlighter.jsm
@@ -0,0 +1,878 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla Highlighter Module.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Campbell <rcampbell@mozilla.com> (original author)
+ *   Mihai Șucan <mihai.sucan@gmail.com>
+ *   Julian Viereck <jviereck@mozilla.com>
+ *   Paul Rouget <paul@mozilla.com>
+ *   Kyle Simpson <ksimpson@mozilla.com>
+ *   Johan Charlez <johan.charlez@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+const Cu = Components.utils;
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+
+var EXPORTED_SYMBOLS = ["Highlighter"];
+
+const INSPECTOR_INVISIBLE_ELEMENTS = {
+  "head": true,
+  "base": true,
+  "basefont": true,
+  "isindex": true,
+  "link": true,
+  "meta": true,
+  "script": true,
+  "style": true,
+  "title": true,
+};
+
+/**
+ * A highlighter mechanism.
+ *
+ * The highlighter is built dynamically into the browser element.
+ * The caller is in charge of destroying the highlighter (ie, the highlighter
+ * won't be destroyed if a new tab is selected for example).
+ *
+ * API:
+ *
+ *   // Constructor and destructor.
+ *   // @param aWindow - browser.xul window.
+ *   Highlighter(aWindow); 
+ *   void destroy();
+ *
+ *   // Highlight a node.
+ *   // @param aNode - node to highlight
+ *   // @param aScroll - scroll to ensure the node is visible
+ *   void highlight(aNode, aScroll);
+ *
+ *   // Get the selected node.
+ *   DOMNode getNode();
+ *
+ *   // Lock and unlock the select node.
+ *   void lock();
+ *   void unlock();
+ *
+ *   // Show and hide the highlighter
+ *   void show();
+ *   void hide();
+ *   boolean isHidden();
+ *
+ *   // Redraw the highlighter if the visible portion of the node has changed.
+ *   void invalidateSize(aScroll);
+ *
+ *   // Is a node highlightable.
+ *   boolean isNodeHighlightable(aNode);
+ *
+ *   // Add/Remove lsiteners
+ *   // @param aEvent - event name
+ *   // @param aListener - function callback
+ *   void addListener(aEvent, aListener);
+ *   void removeListener(aEvent, aListener);
+ *
+ * Events:
+ *
+ *   "closed" - Highlighter is closing
+ *   "nodeselected" - A new node has been selected
+ *   "highlighting" - Highlighter is highlighting
+ *   "locked" - The selected node has been locked
+ *   "unlocked" - The selected ndoe has been unlocked
+ *
+ * Structure:
+ *
+ *   <stack id="highlighter-container">
+ *     <vbox id="highlighter-veil-container">...</vbox>
+ *     <box id="highlighter-controls>...</vbox>
+ *   </stack>
+ *
+ */
+
+
+/**
+ * Constructor.
+ *
+ * @param object aWindow
+ */
+function Highlighter(aWindow)
+{
+  this.chromeWin = aWindow;
+  this.tabbrowser = aWindow.gBrowser;
+  this.chromeDoc = aWindow.document;
+  this.browser = aWindow.gBrowser.selectedBrowser;
+  this.events = {};
+
+  this._init();
+}
+
+Highlighter.prototype = {
+  _init: function Highlighter__init()
+  {
+    let stack = this.browser.parentNode;
+    this.win = this.browser.contentWindow;
+    this._highlighting = false;
+
+    this.highlighterContainer = this.chromeDoc.createElement("stack");
+    this.highlighterContainer.id = "highlighter-container";
+
+    this.veilContainer = this.chromeDoc.createElement("vbox");
+    this.veilContainer.id = "highlighter-veil-container";
+
+    // The controlsBox will host the different interactive
+    // elements of the highlighter (buttons, toolbars, ...).
+    let controlsBox = this.chromeDoc.createElement("box");
+    controlsBox.id = "highlighter-controls";
+    this.highlighterContainer.appendChild(this.veilContainer);
+    this.highlighterContainer.appendChild(controlsBox);
+
+    stack.appendChild(this.highlighterContainer);
+
+    // The veil will make the whole page darker except
+    // for the region of the selected box.
+    this.buildVeil(this.veilContainer);
+
+    this.buildInfobar(controlsBox);
+
+    this.transitionDisabler = null;
+
+    this.computeZoomFactor();
+    this.unlock();
+    this.hide();
+  },
+
+  /**
+   * Destroy the nodes. Remove listeners.
+   */
+  destroy: function Highlighter_destroy()
+  {
+    this.detachKeysListeners();
+    this.detachMouseListeners();
+    this.detachPageListeners();
+
+    this.chromeWin.clearTimeout(this.transitionDisabler);
+    this.boundCloseEventHandler = null;
+    this._contentRect = null;
+    this._highlightRect = null;
+    this._highlighting = false;
+    this.veilTopBox = null;
+    this.veilLeftBox = null;
+    this.veilMiddleBox = null;
+    this.veilTransparentBox = null;
+    this.veilContainer = null;
+    this.node = null;
+    this.nodeInfo = null;
+    this.highlighterContainer.parentNode.removeChild(this.highlighterContainer);
+    this.highlighterContainer = null;
+    this.win = null
+    this.browser = null;
+    this.chromeDoc = null;
+    this.chromeWin = null;
+    this.tabbrowser = null;
+
+    this.emitEvent("closed");
+    this.removeAllListeners();
+  },
+
+  /**
+   * Show the veil, and select a node.
+   * If no node is specified, the previous selected node is highlighted if any.
+   * If no node was selected, the root element is selected.
+   *
+   * @param aNode [optional] - The node to be selected.
+   * @param aScroll [optional] boolean
+   *        Should we scroll to ensure that the selected node is visible.
+   */
+  highlight: function Highlighter_highlight(aNode, aScroll)
+  {
+    if (this.hidden)
+      this.show();
+
+    let oldNode = this.node;
+
+    if (!aNode) {
+      if (!this.node)
+        this.node = this.win.document.documentElement;
+    } else {
+      this.node = aNode;
+    }
+
+    if (oldNode !== this.node) {
+      this.updateInfobar();
+    }
+
+    this.invalidateSize(!!aScroll);
+
+    if (oldNode !== this.node) {
+      this.emitEvent("nodeselected");
+    }
+  },
+
+  /**
+   * Update the highlighter size and position.
+   */
+  invalidateSize: function Highlighter_invalidateSize(aScroll)
+  {
+    let rect = null;
+
+    if (this.node && this.isNodeHighlightable(this.node)) {
+
+      if (aScroll &&
+          this.node.scrollIntoView) { // XUL elements don't have such method
+        this.node.scrollIntoView();
+      }
+      let clientRect = this.node.getBoundingClientRect();
+      rect = LayoutHelpers.getDirtyRect(this.node);
+    }
+
+    this.highlightRectangle(rect);
+
+    this.moveInfobar();
+
+    if (this._highlighting) {
+      this.emitEvent("highlighting");
+    }
+  },
+
+  /**
+   * Returns the selected node.
+   *
+   * @returns node
+   */
+  getNode: function() {
+    return this.node;
+  },
+
+  /**
+   * Show the highlighter if it has been hidden.
+   */
+  show: function() {
+    if (!this.hidden) return;
+    this.veilContainer.removeAttribute("hidden");
+    this.nodeInfo.container.removeAttribute("hidden");
+    this.attachKeysListeners();
+    this.attachPageListeners();
+    this.invalidateSize();
+    this.hidden = false;
+  },
+
+  /**
+   * Hide the highlighter, the veil and the infobar.
+   */
+  hide: function() {
+    if (this.hidden) return;
+    this.veilContainer.setAttribute("hidden", "true");
+    this.nodeInfo.container.setAttribute("hidden", "true");
+    this.detachKeysListeners();
+    this.detachPageListeners();
+    this.hidden = true;
+  },
+
+  /**
+   * Is the highlighter visible?
+   *
+   * @return boolean
+   */
+  isHidden: function() {
+    return this.hidden;
+  },
+
+  /**
+   * Lock a node. Stops the inspection.
+   */
+  lock: function() {
+    if (this.locked === true) return;
+    this.veilContainer.setAttribute("locked", "true");
+    this.nodeInfo.container.setAttribute("locked", "true");
+    this.detachMouseListeners();
+    this.locked = true;
+    this.emitEvent("locked");
+  },
+
+  /**
+   * Start inspecting.
+   * Unlock the current node (if any), and select any node being hovered.
+   */
+  unlock: function() {
+    if (this.locked === false) return;
+    this.veilContainer.removeAttribute("locked");
+    this.nodeInfo.container.removeAttribute("locked");
+    this.attachMouseListeners();
+    this.locked = false;
+    this.emitEvent("unlocked");
+  },
+
+  /**
+   * Is the specified node highlightable?
+   *
+   * @param nsIDOMNode aNode
+   *        the DOM element in question
+   * @returns boolean
+   *          True if the node is highlightable or false otherwise.
+   */
+  isNodeHighlightable: function Highlighter_isNodeHighlightable(aNode)
+  {
+    if (aNode.nodeType != aNode.ELEMENT_NODE) {
+      return false;
+    }
+    let nodeName = aNode.nodeName.toLowerCase();
+    return !INSPECTOR_INVISIBLE_ELEMENTS[nodeName];
+  },
+  /**
+   * Build the veil:
+   *
+   * <vbox id="highlighter-veil-container">
+   *   <box id="highlighter-veil-topbox" class="highlighter-veil"/>
+   *   <hbox id="highlighter-veil-middlebox">
+   *     <box id="highlighter-veil-leftbox" class="highlighter-veil"/>
+   *     <box id="highlighter-veil-transparentbox"/>
+   *     <box id="highlighter-veil-rightbox" class="highlighter-veil"/>
+   *   </hbox>
+   *   <box id="highlighter-veil-bottombox" class="highlighter-veil"/>
+   * </vbox>
+   *
+   * @param nsIDOMElement aParent
+   *        The container of the veil boxes.
+   */
+
+  buildVeil: function Highlighter_buildVeil(aParent)
+  {
+    // We will need to resize these boxes to surround a node.
+    // See highlightRectangle().
+
+    this.veilTopBox = this.chromeDoc.createElement("box");
+    this.veilTopBox.id = "highlighter-veil-topbox";
+    this.veilTopBox.className = "highlighter-veil";
+
+    this.veilMiddleBox = this.chromeDoc.createElement("hbox");
+    this.veilMiddleBox.id = "highlighter-veil-middlebox";
+
+    this.veilLeftBox = this.chromeDoc.createElement("box");
+    this.veilLeftBox.id = "highlighter-veil-leftbox";
+    this.veilLeftBox.className = "highlighter-veil";
+
+    this.veilTransparentBox = this.chromeDoc.createElement("box");
+    this.veilTransparentBox.id = "highlighter-veil-transparentbox";
+
+    // We don't need any references to veilRightBox and veilBottomBox.
+    // These boxes are automatically resized (flex=1)
+
+    let veilRightBox = this.chromeDoc.createElement("box");
+    veilRightBox.id = "highlighter-veil-rightbox";
+    veilRightBox.className = "highlighter-veil";
+
+    let veilBottomBox = this.chromeDoc.createElement("box");
+    veilBottomBox.id = "highlighter-veil-bottombox";
+    veilBottomBox.className = "highlighter-veil";
+
+    this.veilMiddleBox.appendChild(this.veilLeftBox);
+    this.veilMiddleBox.appendChild(this.veilTransparentBox);
+    this.veilMiddleBox.appendChild(veilRightBox);
+
+    aParent.appendChild(this.veilTopBox);
+    aParent.appendChild(this.veilMiddleBox);
+    aParent.appendChild(veilBottomBox);
+  },
+
+  /**
+   * Build the node Infobar.
+   *
+   * <box id="highlighter-nodeinfobar-container">
+   *   <box id="Highlighter-nodeinfobar-arrow-top"/>
+   *   <vbox id="highlighter-nodeinfobar">
+   *     <label id="highlighter-nodeinfobar-tagname"/>
+   *     <label id="highlighter-nodeinfobar-id"/>
+   *     <vbox id="highlighter-nodeinfobar-classes"/>
+   *   </vbox>
+   *   <box id="Highlighter-nodeinfobar-arrow-bottom"/>
+   * </box>
+   *
+   * @param nsIDOMElement aParent
+   *        The container of the infobar.
+   */
+  buildInfobar: function Highlighter_buildInfobar(aParent)
+  {
+    let container = this.chromeDoc.createElement("box");
+    container.id = "highlighter-nodeinfobar-container";
+    container.setAttribute("position", "top");
+    container.setAttribute("disabled", "true");
+
+    let nodeInfobar = this.chromeDoc.createElement("hbox");
+    nodeInfobar.id = "highlighter-nodeinfobar";
+
+    let arrowBoxTop = this.chromeDoc.createElement("box");
+    arrowBoxTop.className = "highlighter-nodeinfobar-arrow";
+    arrowBoxTop.id = "highlighter-nodeinfobar-arrow-top";
+
+    let arrowBoxBottom = this.chromeDoc.createElement("box");
+    arrowBoxBottom.className = "highlighter-nodeinfobar-arrow";
+    arrowBoxBottom.id = "highlighter-nodeinfobar-arrow-bottom";
+
+    let tagNameLabel = this.chromeDoc.createElement("label");
+    tagNameLabel.id = "highlighter-nodeinfobar-tagname";
+    tagNameLabel.className = "plain";
+
+    let idLabel = this.chromeDoc.createElement("label");
+    idLabel.id = "highlighter-nodeinfobar-id";
+    idLabel.className = "plain";
+
+    let classesBox = this.chromeDoc.createElement("hbox");
+    classesBox.id = "highlighter-nodeinfobar-classes";
+
+    nodeInfobar.appendChild(tagNameLabel);
+    nodeInfobar.appendChild(idLabel);
+    nodeInfobar.appendChild(classesBox);
+    container.appendChild(arrowBoxTop);
+    container.appendChild(nodeInfobar);
+    container.appendChild(arrowBoxBottom);
+
+    aParent.appendChild(container);
+
+    let barHeight = container.getBoundingClientRect().height;
+
+    this.nodeInfo = {
+      tagNameLabel: tagNameLabel,
+      idLabel: idLabel,
+      classesBox: classesBox,
+      container: container,
+      barHeight: barHeight,
+    };
+  },
+
+  /**
+   * Highlight a rectangular region.
+   *
+   * @param object aRect
+   *        The rectangle region to highlight.
+   * @returns boolean
+   *          True if the rectangle was highlighted, false otherwise.
+   */
+  highlightRectangle: function Highlighter_highlightRectangle(aRect)
+  {
+    if (!aRect) {
+      this.unhighlight();
+      return;
+    }
+
+    let oldRect = this._contentRect;
+
+    if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
+        aRect.width == oldRect.width && aRect.height == oldRect.height) {
+      return; // same rectangle
+    }
+
+    let aRectScaled = LayoutHelpers.getZoomedRect(this.win, aRect);
+
+    if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
+        aRectScaled.width > 0 && aRectScaled.height > 0) {
+
+      this.veilTransparentBox.style.visibility = "visible";
+
+      // The bottom div and the right div are flexibles (flex=1).
+      // We don't need to resize them.
+      this.veilTopBox.style.height = aRectScaled.top + "px";
+      this.veilLeftBox.style.width = aRectScaled.left + "px";
+      this.veilMiddleBox.style.height = aRectScaled.height + "px";
+      this.veilTransparentBox.style.width = aRectScaled.width + "px";
+
+      this._highlighting = true;
+    } else {
+      this.unhighlight();
+    }
+
+    this._contentRect = aRect; // save orig (non-scaled) rect
+    this._highlightRect = aRectScaled; // and save the scaled rect.
+
+    return;
+  },
+
+  /**
+   * Clear the highlighter surface.
+   */
+  unhighlight: function Highlighter_unhighlight()
+  {
+    this._highlighting = false;
+    this.veilMiddleBox.style.height = 0;
+    this.veilTransparentBox.style.width = 0;
+    this.veilTransparentBox.style.visibility = "hidden";
+  },
+
+  /**
+   * Update node information (tagName#id.class) 
+   */
+  updateInfobar: function Highlighter_updateInfobar()
+  {
+    // Tag name
+    this.nodeInfo.tagNameLabel.textContent = this.node.tagName;
+
+    // ID
+    this.nodeInfo.idLabel.textContent = this.node.id ? "#" + this.node.id : "";
+
+    // Classes
+    let classes = this.nodeInfo.classesBox;
+    while (classes.hasChildNodes()) {
+      classes.removeChild(classes.firstChild);
+    }
+
+    if (this.node.className) {
+      let fragment = this.chromeDoc.createDocumentFragment();
+      for (let i = 0; i < this.node.classList.length; i++) {
+        let classLabel = this.chromeDoc.createElement("label");
+        classLabel.className = "highlighter-nodeinfobar-class plain";
+        classLabel.textContent = "." + this.node.classList[i];
+        fragment.appendChild(classLabel);
+      }
+      classes.appendChild(fragment);
+    }
+  },
+
+  /**
+   * Move the Infobar to the right place in the highlighter.
+   */
+  moveInfobar: function Highlighter_moveInfobar()
+  {
+    if (this._highlightRect) {
+      let winHeight = this.win.innerHeight * this.zoom;
+      let winWidth = this.win.innerWidth * this.zoom;
+
+      let rect = {top: this._highlightRect.top,
+                  left: this._highlightRect.left,
+                  width: this._highlightRect.width,
+                  height: this._highlightRect.height};
+
+      rect.top = Math.max(rect.top, 0);
+      rect.left = Math.max(rect.left, 0);
+      rect.width = Math.max(rect.width, 0);
+      rect.height = Math.max(rect.height, 0);
+
+      rect.top = Math.min(rect.top, winHeight);
+      rect.left = Math.min(rect.left, winWidth);
+
+      this.nodeInfo.container.removeAttribute("disabled");
+      // Can the bar be above the node?
+      if (rect.top < this.nodeInfo.barHeight) {
+        // No. Can we move the toolbar under the node?
+        if (rect.top + rect.height +
+            this.nodeInfo.barHeight > winHeight) {
+          // No. Let's move it inside.
+          this.nodeInfo.container.style.top = rect.top + "px";
+          this.nodeInfo.container.setAttribute("position", "overlap");
+        } else {
+          // Yes. Let's move it under the node.
+          this.nodeInfo.container.style.top = rect.top + rect.height + "px";
+          this.nodeInfo.container.setAttribute("position", "bottom");
+        }
+      } else {
+        // Yes. Let's move it on top of the node.
+        this.nodeInfo.container.style.top =
+          rect.top - this.nodeInfo.barHeight + "px";
+        this.nodeInfo.container.setAttribute("position", "top");
+      }
+
+      let barWidth = this.nodeInfo.container.getBoundingClientRect().width;
+      let left = rect.left + rect.width / 2 - barWidth / 2;
+
+      // Make sure the whole infobar is visible
+      if (left < 0) {
+        left = 0;
+        this.nodeInfo.container.setAttribute("hide-arrow", "true");
+      } else {
+        if (left + barWidth > winWidth) {
+          left = winWidth - barWidth;
+          this.nodeInfo.container.setAttribute("hide-arrow", "true");
+        } else {
+          this.nodeInfo.container.removeAttribute("hide-arrow");
+        }
+      }
+      this.nodeInfo.container.style.left = left + "px";
+    } else {
+      this.nodeInfo.container.style.left = "0";
+      this.nodeInfo.container.style.top = "0";
+      this.nodeInfo.container.setAttribute("position", "top");
+      this.nodeInfo.container.setAttribute("hide-arrow", "true");
+    }
+  },
+
+  /**
+   * Store page zoom factor.
+   */
+  computeZoomFactor: function Highlighter_computeZoomFactor() {
+    this.zoom =
+      this.win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+      .getInterface(Components.interfaces.nsIDOMWindowUtils)
+      .screenPixelsPerCSSPixel;
+  },
+
+  /////////////////////////////////////////////////////////////////////////
+  //// Event Emitter Mechanism
+
+  addListener: function Highlighter_addListener(aEvent, aListener)
+  {
+    if (!(aEvent in this.events))
+      this.events[aEvent] = [];
+    this.events[aEvent].push(aListener);
+  },
+
+  removeListener: function Highlighter_removeListener(aEvent, aListener)
+  {
+    if (!(aEvent in this.events))
+      return;
+    let idx = this.events[aEvent].indexOf(aListener);
+    if (idx > -1)
+      this.events[aEvent].splice(idx, 1);
+  },
+
+  emitEvent: function Highlighter_emitEvent(aEvent, aArgv)
+  {
+    if (!(aEvent in this.events))
+      return;
+
+    let listeners = this.events[aEvent];
+    let highlighter = this;
+    listeners.forEach(function(aListener) {
+      try {
+        aListener.apply(highlighter, aArgv);
+      } catch(e) {}
+    });
+  },
+
+  removeAllListeners: function Highlighter_removeAllIsteners()
+  {
+    for (let event in this.events) {
+      delete this.events[event];
+    }
+  },
+
+  /////////////////////////////////////////////////////////////////////////
+  //// Event Handling
+
+  attachMouseListeners: function Highlighter_attachMouseListeners()
+  {
+    this.browser.addEventListener("mousemove", this, true);
+    this.browser.addEventListener("click", this, true);
+    this.browser.addEventListener("dblclick", this, true);
+    this.browser.addEventListener("mousedown", this, true);
+    this.browser.addEventListener("mouseup", this, true);
+  },
+
+  detachMouseListeners: function Highlighter_detachMouseListeners()
+  {
+    this.browser.removeEventListener("mousemove", this, true);
+    this.browser.removeEventListener("click", this, true);
+    this.browser.removeEventListener("dblclick", this, true);
+    this.browser.removeEventListener("mousedown", this, true);
+    this.browser.removeEventListener("mouseup", this, true);
+  },
+
+  attachPageListeners: function Highlighter_attachPageListeners()
+  {
+    this.browser.addEventListener("resize", this, true);
+    this.browser.addEventListener("scroll", this, true);
+  },
+
+  detachPageListeners: function Highlighter_detachPageListeners()
+  {
+    this.browser.removeEventListener("resize", this, true);
+    this.browser.removeEventListener("scroll", this, true);
+  },
+
+  attachKeysListeners: function Highlighter_attachKeysListeners()
+  {
+    this.browser.addEventListener("keypress", this, true);
+    this.highlighterContainer.addEventListener("keypress", this, true);
+  },
+
+  detachKeysListeners: function Highlighter_detachKeysListeners()
+  {
+    this.browser.removeEventListener("keypress", this, true);
+    this.highlighterContainer.removeEventListener("keypress", this, true);
+  },
+
+  /**
+   * Generic event handler.
+   *
+   * @param nsIDOMEvent aEvent
+   *        The DOM event object.
+   */
+  handleEvent: function Highlighter_handleEvent(aEvent)
+  {
+    switch (aEvent.type) {
+      case "click":
+        this.handleClick(aEvent);
+        break;
+      case "mousemove":
+        this.handleMouseMove(aEvent);
+        break;
+      case "resize":
+      case "scroll":
+        this.computeZoomFactor();
+        this.brieflyDisableTransitions();
+        this.invalidateSize();
+        break;
+      case "dblclick":
+      case "mousedown":
+      case "mouseup":
+        aEvent.stopPropagation();
+        aEvent.preventDefault();
+        break;
+        break;
+      case "keypress":
+        switch (aEvent.keyCode) {
+          case this.chromeWin.KeyEvent.DOM_VK_RETURN:
+            this.locked ? this.unlock() : this.lock();
+            aEvent.preventDefault();
+            aEvent.stopPropagation();
+            break;
+          case this.chromeWin.KeyEvent.DOM_VK_LEFT:
+            let node;
+            if (this.node) {
+              node = this.node.parentNode;
+            } else {
+              node = this.defaultSelection;
+            }
+            if (node && this.isNodeHighlightable(node)) {
+              this.highlight(node);
+            }
+            aEvent.preventDefault();
+            aEvent.stopPropagation();
+            break;
+          case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
+            if (this.node) {
+              // Find the first child that is highlightable.
+              for (let i = 0; i < this.node.childNodes.length; i++) {
+                node = this.node.childNodes[i];
+                if (node && this.isNodeHighlightable(node)) {
+                  break;
+                }
+              }
+            } else {
+              node = this.defaultSelection;
+            }
+            if (node && this.isNodeHighlightable(node)) {
+              this.highlight(node, true);
+            }
+            aEvent.preventDefault();
+            aEvent.stopPropagation();
+            break;
+          case this.chromeWin.KeyEvent.DOM_VK_UP:
+            if (this.node) {
+              // Find a previous sibling that is highlightable.
+              node = this.node.previousSibling;
+              while (node && !this.isNodeHighlightable(node)) {
+                node = node.previousSibling;
+              }
+            } else {
+              node = this.defaultSelection;
+            }
+            if (node && this.isNodeHighlightable(node)) {
+              this.highlight(node, true);
+            }
+            aEvent.preventDefault();
+            aEvent.stopPropagation();
+            break;
+          case this.chromeWin.KeyEvent.DOM_VK_DOWN:
+            if (this.node) {
+              // Find a next sibling that is highlightable.
+              node = this.node.nextSibling;
+              while (node && !this.isNodeHighlightable(node)) {
+                node = node.nextSibling;
+              }
+            } else {
+              node = this.defaultSelection;
+            }
+            if (node && this.isNodeHighlightable(node)) {
+              this.highlight(node, true);
+            }
+            aEvent.preventDefault();
+            aEvent.stopPropagation();
+            break;
+        }
+    }
+  },
+
+  /**
+   * Disable the CSS transitions for a short time to avoid laggy animations
+   * during scrolling or resizing.
+   */
+  brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
+  {
+   if (this.transitionDisabler) {
+     this.chromeWin.clearTimeout(this.transitionDisabler);
+   } else {
+     this.veilContainer.setAttribute("disable-transitions", "true");
+     this.nodeInfo.container.setAttribute("disable-transitions", "true");
+   }
+   this.transitionDisabler =
+     this.chromeWin.setTimeout(function() {
+       this.veilContainer.removeAttribute("disable-transitions");
+       this.nodeInfo.container.removeAttribute("disable-transitions");
+       this.transitionDisabler = null;
+     }.bind(this), 500);
+  },
+
+  /**
+   * Handle clicks.
+   *
+   * @param nsIDOMEvent aEvent
+   *        The DOM event.
+   */
+  handleClick: function Highlighter_handleClick(aEvent)
+  {
+    // Stop inspection when the user clicks on a node.
+    if (aEvent.button == 0) {
+      let win = aEvent.target.ownerDocument.defaultView;
+      this.lock();
+      win.focus();
+    }
+    aEvent.preventDefault();
+    aEvent.stopPropagation();
+  },
+
+  /**
+   * Handle mousemoves in panel.
+   *
+   * @param nsiDOMEvent aEvent
+   *        The MouseEvent triggering the method.
+   */
+  handleMouseMove: function Highlighter_handleMouseMove(aEvent)
+  {
+    let element = LayoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
+      aEvent.clientX, aEvent.clientY);
+    if (element && element != this.node) {
+      this.highlight(element);
+    }
+  },
+};
+
+///////////////////////////////////////////////////////////////////////////
+
--- a/browser/devtools/highlighter/inspector.jsm
+++ b/browser/devtools/highlighter/inspector.jsm
@@ -47,37 +47,21 @@ const Ci = Components.interfaces;
 const Cr = Components.results;
 
 var EXPORTED_SYMBOLS = ["InspectorUI"];
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/TreePanel.jsm");
 Cu.import("resource:///modules/devtools/CssRuleView.jsm");
-
-const INSPECTOR_INVISIBLE_ELEMENTS = {
-  "head": true,
-  "base": true,
-  "basefont": true,
-  "isindex": true,
-  "link": true,
-  "meta": true,
-  "script": true,
-  "style": true,
-  "title": true,
-};
+Cu.import("resource:///modules/highlighter.jsm");
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
 
 // Inspector notifications dispatched through the nsIObserverService.
 const INSPECTOR_NOTIFICATIONS = {
-  // Fires once the Inspector highlights an element in the page.
-  HIGHLIGHTING: "inspector-highlighting",
-
-  // Fires once the Inspector stops highlighting any element.
-  UNHIGHLIGHTING: "inspector-unhighlighting",
-
   // Fires once the Inspector completes the initialization and opens up on
   // screen.
   OPENED: "inspector-opened",
 
   // Fires once the Inspector is closed.
   CLOSED: "inspector-closed",
 
   // Fires once the Inspector is destroyed. Not fired on tab switch.
@@ -94,693 +78,16 @@ const INSPECTOR_NOTIFICATIONS = {
 
   // Event notifications for the attribute-value editor
   EDITOR_OPENED: "inspector-editor-opened",
   EDITOR_CLOSED: "inspector-editor-closed",
   EDITOR_SAVED: "inspector-editor-saved",
 };
 
 ///////////////////////////////////////////////////////////////////////////
-//// Highlighter
-
-/**
- * A highlighter mechanism.
- *
- * The highlighter is built dynamically once the Inspector is invoked:
- * <stack id="highlighter-container">
- *   <vbox id="highlighter-veil-container">...</vbox>
- *   <box id="highlighter-controls>...</vbox>
- * </stack>
- *
- * @param object aInspector
- *        The InspectorUI instance.
- */
-function Highlighter(aInspector)
-{
-  this.IUI = aInspector;
-  this._init();
-}
-
-Highlighter.prototype = {
-  _init: function Highlighter__init()
-  {
-    this.browser = this.IUI.browser;
-    this.chromeDoc = this.IUI.chromeDoc;
-
-    let stack = this.browser.parentNode;
-    this.win = this.browser.contentWindow;
-    this._highlighting = false;
-
-    this.highlighterContainer = this.chromeDoc.createElement("stack");
-    this.highlighterContainer.id = "highlighter-container";
-
-    this.veilContainer = this.chromeDoc.createElement("vbox");
-    this.veilContainer.id = "highlighter-veil-container";
-
-    // The controlsBox will host the different interactive
-    // elements of the highlighter (buttons, toolbars, ...).
-    let controlsBox = this.chromeDoc.createElement("box");
-    controlsBox.id = "highlighter-controls";
-    this.highlighterContainer.appendChild(this.veilContainer);
-    this.highlighterContainer.appendChild(controlsBox);
-
-    stack.appendChild(this.highlighterContainer);
-
-    // The veil will make the whole page darker except
-    // for the region of the selected box.
-    this.buildVeil(this.veilContainer);
-
-    this.buildInfobar(controlsBox);
-
-    if (!this.IUI.store.getValue(this.winID, "inspecting")) {
-      this.veilContainer.setAttribute("locked", true);
-      this.nodeInfo.container.setAttribute("locked", true);
-    }
-
-    this.browser.addEventListener("resize", this, true);
-    this.browser.addEventListener("scroll", this, true);
-
-    this.transitionDisabler = null;
-
-    this.computeZoomFactor();
-    this.handleResize();
-  },
-
-  /**
-   * Build the veil:
-   *
-   * <vbox id="highlighter-veil-container">
-   *   <box id="highlighter-veil-topbox" class="highlighter-veil"/>
-   *   <hbox id="highlighter-veil-middlebox">
-   *     <box id="highlighter-veil-leftbox" class="highlighter-veil"/>
-   *     <box id="highlighter-veil-transparentbox"/>
-   *     <box id="highlighter-veil-rightbox" class="highlighter-veil"/>
-   *   </hbox>
-   *   <box id="highlighter-veil-bottombox" class="highlighter-veil"/>
-   * </vbox>
-   *
-   * @param nsIDOMElement aParent
-   *        The container of the veil boxes.
-   */
-  buildVeil: function Highlighter_buildVeil(aParent)
-  {
-    // We will need to resize these boxes to surround a node.
-    // See highlightRectangle().
-
-    this.veilTopBox = this.chromeDoc.createElement("box");
-    this.veilTopBox.id = "highlighter-veil-topbox";
-    this.veilTopBox.className = "highlighter-veil";
-
-    this.veilMiddleBox = this.chromeDoc.createElement("hbox");
-    this.veilMiddleBox.id = "highlighter-veil-middlebox";
-
-    this.veilLeftBox = this.chromeDoc.createElement("box");
-    this.veilLeftBox.id = "highlighter-veil-leftbox";
-    this.veilLeftBox.className = "highlighter-veil";
-
-    this.veilTransparentBox = this.chromeDoc.createElement("box");
-    this.veilTransparentBox.id = "highlighter-veil-transparentbox";
-
-    // We don't need any references to veilRightBox and veilBottomBox.
-    // These boxes are automatically resized (flex=1)
-
-    let veilRightBox = this.chromeDoc.createElement("box");
-    veilRightBox.id = "highlighter-veil-rightbox";
-    veilRightBox.className = "highlighter-veil";
-
-    let veilBottomBox = this.chromeDoc.createElement("box");
-    veilBottomBox.id = "highlighter-veil-bottombox";
-    veilBottomBox.className = "highlighter-veil";
-
-    this.veilMiddleBox.appendChild(this.veilLeftBox);
-    this.veilMiddleBox.appendChild(this.veilTransparentBox);
-    this.veilMiddleBox.appendChild(veilRightBox);
-
-    aParent.appendChild(this.veilTopBox);
-    aParent.appendChild(this.veilMiddleBox);
-    aParent.appendChild(veilBottomBox);
-  },
-
-  /**
-   * Build the node Infobar.
-   *
-   * <box id="highlighter-nodeinfobar-container">
-   *   <box id="Highlighter-nodeinfobar-arrow-top"/>
-   *   <vbox id="highlighter-nodeinfobar">
-   *     <label id="highlighter-nodeinfobar-tagname"/>
-   *     <label id="highlighter-nodeinfobar-id"/>
-   *     <vbox id="highlighter-nodeinfobar-classes"/>
-   *   </vbox>
-   *   <box id="Highlighter-nodeinfobar-arrow-bottom"/>
-   * </box>
-   *
-   * @param nsIDOMElement aParent
-   *        The container of the infobar.
-   */
-  buildInfobar: function Highlighter_buildInfobar(aParent)
-  {
-    let container = this.chromeDoc.createElement("box");
-    container.id = "highlighter-nodeinfobar-container";
-    container.setAttribute("position", "top");
-    container.setAttribute("disabled", "true");
-
-    let nodeInfobar = this.chromeDoc.createElement("hbox");
-    nodeInfobar.id = "highlighter-nodeinfobar";
-
-    let arrowBoxTop = this.chromeDoc.createElement("box");
-    arrowBoxTop.className = "highlighter-nodeinfobar-arrow";
-    arrowBoxTop.id = "highlighter-nodeinfobar-arrow-top";
-
-    let arrowBoxBottom = this.chromeDoc.createElement("box");
-    arrowBoxBottom.className = "highlighter-nodeinfobar-arrow";
-    arrowBoxBottom.id = "highlighter-nodeinfobar-arrow-bottom";
-
-    let tagNameLabel = this.chromeDoc.createElement("label");
-    tagNameLabel.id = "highlighter-nodeinfobar-tagname";
-    tagNameLabel.className = "plain";
-
-    let idLabel = this.chromeDoc.createElement("label");
-    idLabel.id = "highlighter-nodeinfobar-id";
-    idLabel.className = "plain";
-
-    let classesBox = this.chromeDoc.createElement("hbox");
-    classesBox.id = "highlighter-nodeinfobar-classes";
-
-    nodeInfobar.appendChild(tagNameLabel);
-    nodeInfobar.appendChild(idLabel);
-    nodeInfobar.appendChild(classesBox);
-    container.appendChild(arrowBoxTop);
-    container.appendChild(nodeInfobar);
-    container.appendChild(arrowBoxBottom);
-
-    aParent.appendChild(container);
-
-    let barHeight = container.getBoundingClientRect().height;
-
-    this.nodeInfo = {
-      tagNameLabel: tagNameLabel,
-      idLabel: idLabel,
-      classesBox: classesBox,
-      container: container,
-      barHeight: barHeight,
-    };
-  },
-
-  /**
-   * Destroy the nodes.
-   */
-  destroy: function Highlighter_destroy()
-  {
-    this.IUI.win.clearTimeout(this.transitionDisabler);
-    this.browser.removeEventListener("scroll", this, true);
-    this.browser.removeEventListener("resize", this, true);
-    this.boundCloseEventHandler = null;
-    this._contentRect = null;
-    this._highlightRect = null;
-    this._highlighting = false;
-    this.veilTopBox = null;
-    this.veilLeftBox = null;
-    this.veilMiddleBox = null;
-    this.veilTransparentBox = null;
-    this.veilContainer = null;
-    this.node = null;
-    this.nodeInfo = null;
-    this.highlighterContainer.parentNode.removeChild(this.highlighterContainer);
-    this.highlighterContainer = null;
-    this.win = null
-    this.browser = null;
-    this.chromeDoc = null;
-    this.IUI = null;
-  },
-
-  /**
-   * Is the highlighter highlighting? Public method for querying the state
-   * of the highlighter.
-   */
-  get isHighlighting() {
-    return this._highlighting;
-  },
-
-  /**
-   * Highlight this.node, unhilighting first if necessary.
-   *
-   * @param boolean aScroll
-   *        Boolean determining whether to scroll or not.
-   */
-  highlight: function Highlighter_highlight(aScroll)
-  {
-    let rect = null;
-
-    if (this.node && this.isNodeHighlightable(this.node)) {
-
-      if (aScroll) {
-        this.node.scrollIntoView();
-      }
-
-      let clientRect = this.node.getBoundingClientRect();
-
-      // Go up in the tree of frames to determine the correct rectangle.
-      // clientRect is read-only, we need to be able to change properties.
-      rect = {top: clientRect.top,
-              left: clientRect.left,
-              width: clientRect.width,
-              height: clientRect.height};
-
-      let frameWin = this.node.ownerDocument.defaultView;
-
-      // We iterate through all the parent windows.
-      while (true) {
-
-        // Does the selection overflow on the right of its window?
-        let diffx = frameWin.innerWidth - (rect.left + rect.width);
-        if (diffx < 0) {
-          rect.width += diffx;
-        }
-
-        // Does the selection overflow on the bottom of its window?
-        let diffy = frameWin.innerHeight - (rect.top + rect.height);
-        if (diffy < 0) {
-          rect.height += diffy;
-        }
-
-        // Does the selection overflow on the left of its window?
-        if (rect.left < 0) {
-          rect.width += rect.left;
-          rect.left = 0;
-        }
-
-        // Does the selection overflow on the top of its window?
-        if (rect.top < 0) {
-          rect.height += rect.top;
-          rect.top = 0;
-        }
-
-        // Selection has been clipped to fit in its own window.
-
-        // Are we in the top-level window?
-        if (frameWin.parent === frameWin || !frameWin.frameElement) {
-          break;
-        }
-
-        // We are in an iframe.
-        // We take into account the parent iframe position and its
-        // offset (borders and padding).
-        let frameRect = frameWin.frameElement.getBoundingClientRect();
-
-        let [offsetTop, offsetLeft] =
-          this.IUI.getIframeContentOffset(frameWin.frameElement);
-
-        rect.top += frameRect.top + offsetTop;
-        rect.left += frameRect.left + offsetLeft;
-
-        frameWin = frameWin.parent;
-      }
-    }
-
-    this.highlightRectangle(rect);
-
-    this.moveInfobar();
-
-    if (this._highlighting) {
-      Services.obs.notifyObservers(null,
-        INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, null);
-    }
-  },
-
-  /**
-   * Highlight the given node.
-   *
-   * @param nsIDOMNode aNode
-   *        a DOM element to be highlighted
-   * @param object aParams
-   *        extra parameters object
-   */
-  highlightNode: function Highlighter_highlightNode(aNode, aParams)
-  {
-    this.node = aNode;
-    this.updateInfobar();
-    this.highlight(aParams && aParams.scroll);
-  },
-
-  /**
-   * Highlight a rectangular region.
-   *
-   * @param object aRect
-   *        The rectangle region to highlight.
-   * @returns boolean
-   *          True if the rectangle was highlighted, false otherwise.
-   */
-  highlightRectangle: function Highlighter_highlightRectangle(aRect)
-  {
-    if (!aRect) {
-      this.unhighlight();
-      return;
-    }
-
-    let oldRect = this._contentRect;
-
-    if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
-        aRect.width == oldRect.width && aRect.height == oldRect.height) {
-      return this._highlighting; // same rectangle
-    }
-
-    // adjust rect for zoom scaling
-    let aRectScaled = {};
-    for (let prop in aRect) {
-      aRectScaled[prop] = aRect[prop] * this.zoom;
-    }
-
-    if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
-        aRectScaled.width > 0 && aRectScaled.height > 0) {
-
-      this.veilTransparentBox.style.visibility = "visible";
-
-      // The bottom div and the right div are flexibles (flex=1).
-      // We don't need to resize them.
-      this.veilTopBox.style.height = aRectScaled.top + "px";
-      this.veilLeftBox.style.width = aRectScaled.left + "px";
-      this.veilMiddleBox.style.height = aRectScaled.height + "px";
-      this.veilTransparentBox.style.width = aRectScaled.width + "px";
-
-      this._highlighting = true;
-    } else {
-      this.unhighlight();
-    }
-
-    this._contentRect = aRect; // save orig (non-scaled) rect
-    this._highlightRect = aRectScaled; // and save the scaled rect.
-
-    return this._highlighting;
-  },
-
-  /**
-   * Clear the highlighter surface.
-   */
-  unhighlight: function Highlighter_unhighlight()
-  {
-    this._highlighting = false;
-    this.veilMiddleBox.style.height = 0;
-    this.veilTransparentBox.style.width = 0;
-    this.veilTransparentBox.style.visibility = "hidden";
-    Services.obs.notifyObservers(null,
-      INSPECTOR_NOTIFICATIONS.UNHIGHLIGHTING, null);
-  },
-
-  /**
-   * Update node information (tagName#id.class) 
-   */
-  updateInfobar: function Highlighter_updateInfobar()
-  {
-    // Tag name
-    this.nodeInfo.tagNameLabel.textContent = this.node.tagName;
-
-    // ID
-    this.nodeInfo.idLabel.textContent = this.node.id ? "#" + this.node.id : "";
-
-    // Classes
-    let classes = this.nodeInfo.classesBox;
-    while (classes.hasChildNodes()) {
-      classes.removeChild(classes.firstChild);
-    }
-
-    if (this.node.className) {
-      let fragment = this.chromeDoc.createDocumentFragment();
-      for (let i = 0; i < this.node.classList.length; i++) {
-        let classLabel = this.chromeDoc.createElement("label");
-        classLabel.className = "highlighter-nodeinfobar-class plain";
-        classLabel.textContent = "." + this.node.classList[i];
-        fragment.appendChild(classLabel);
-      }
-      classes.appendChild(fragment);
-    }
-  },
-
-  /**
-   * Move the Infobar to the right place in the highlighter.
-   */
-  moveInfobar: function Highlighter_moveInfobar()
-  {
-    if (this._highlightRect) {
-      let winHeight = this.win.innerHeight * this.zoom;
-      let winWidth = this.win.innerWidth * this.zoom;
-
-      let rect = {top: this._highlightRect.top,
-                  left: this._highlightRect.left,
-                  width: this._highlightRect.width,
-                  height: this._highlightRect.height};
-
-      rect.top = Math.max(rect.top, 0);
-      rect.left = Math.max(rect.left, 0);
-      rect.width = Math.max(rect.width, 0);
-      rect.height = Math.max(rect.height, 0);
-
-      rect.top = Math.min(rect.top, winHeight);
-      rect.left = Math.min(rect.left, winWidth);
-
-      this.nodeInfo.container.removeAttribute("disabled");
-      // Can the bar be above the node?
-      if (rect.top < this.nodeInfo.barHeight) {
-        // No. Can we move the toolbar under the node?
-        if (rect.top + rect.height +
-            this.nodeInfo.barHeight > winHeight) {
-          // No. Let's move it inside.
-          this.nodeInfo.container.style.top = rect.top + "px";
-          this.nodeInfo.container.setAttribute("position", "overlap");
-        } else {
-          // Yes. Let's move it under the node.
-          this.nodeInfo.container.style.top = rect.top + rect.height + "px";
-          this.nodeInfo.container.setAttribute("position", "bottom");
-        }
-      } else {
-        // Yes. Let's move it on top of the node.
-        this.nodeInfo.container.style.top =
-          rect.top - this.nodeInfo.barHeight + "px";
-        this.nodeInfo.container.setAttribute("position", "top");
-      }
-
-      let barWidth = this.nodeInfo.container.getBoundingClientRect().width;
-      let left = rect.left + rect.width / 2 - barWidth / 2;
-
-      // Make sure the whole infobar is visible
-      if (left < 0) {
-        left = 0;
-        this.nodeInfo.container.setAttribute("hide-arrow", "true");
-      } else {
-        if (left + barWidth > winWidth) {
-          left = winWidth - barWidth;
-          this.nodeInfo.container.setAttribute("hide-arrow", "true");
-        } else {
-          this.nodeInfo.container.removeAttribute("hide-arrow");
-        }
-      }
-      this.nodeInfo.container.style.left = left + "px";
-    } else {
-      this.nodeInfo.container.style.left = "0";
-      this.nodeInfo.container.style.top = "0";
-      this.nodeInfo.container.setAttribute("position", "top");
-      this.nodeInfo.container.setAttribute("hide-arrow", "true");
-    }
-  },
-
-  /**
-   * Return the midpoint of a line from pointA to pointB.
-   *
-   * @param object aPointA
-   *        An object with x and y properties.
-   * @param object aPointB
-   *        An object with x and y properties.
-   * @returns object
-   *          An object with x and y properties.
-   */
-  midPoint: function Highlighter_midPoint(aPointA, aPointB)
-  {
-    let pointC = { };
-    pointC.x = (aPointB.x - aPointA.x) / 2 + aPointA.x;
-    pointC.y = (aPointB.y - aPointA.y) / 2 + aPointA.y;
-    return pointC;
-  },
-
-  /**
-   * Return the node under the highlighter rectangle. Useful for testing.
-   * Calculation based on midpoint of diagonal from top left to bottom right
-   * of panel.
-   *
-   * @returns nsIDOMNode|null
-   *          Returns the node under the current highlighter rectangle. Null is
-   *          returned if there is no node highlighted.
-   */
-  get highlitNode()
-  {
-    // Not highlighting? Bail.
-    if (!this._highlighting || !this._contentRect) {
-      return null;
-    }
-
-    let a = {
-      x: this._contentRect.left,
-      y: this._contentRect.top
-    };
-
-    let b = {
-      x: a.x + this._contentRect.width,
-      y: a.y + this._contentRect.height
-    };
-
-    // Get midpoint of diagonal line.
-    let midpoint = this.midPoint(a, b);
-
-    return this.IUI.elementFromPoint(this.win.document, midpoint.x,
-      midpoint.y);
-  },
-
-  /**
-   * Is the specified node highlightable?
-   *
-   * @param nsIDOMNode aNode
-   *        the DOM element in question
-   * @returns boolean
-   *          True if the node is highlightable or false otherwise.
-   */
-  isNodeHighlightable: function Highlighter_isNodeHighlightable(aNode)
-  {
-    if (aNode.nodeType != aNode.ELEMENT_NODE) {
-      return false;
-    }
-    let nodeName = aNode.nodeName.toLowerCase();
-    return !INSPECTOR_INVISIBLE_ELEMENTS[nodeName];
-  },
-
-  /**
-   * Store page zoom factor.
-   */
-  computeZoomFactor: function Highlighter_computeZoomFactor() {
-    this.zoom =
-      this.win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-      .getInterface(Components.interfaces.nsIDOMWindowUtils)
-      .screenPixelsPerCSSPixel;
-  },
-
-  /////////////////////////////////////////////////////////////////////////
-  //// Event Handling
-
-  attachInspectListeners: function Highlighter_attachInspectListeners()
-  {
-    this.browser.addEventListener("mousemove", this, true);
-    this.browser.addEventListener("click", this, true);
-    this.browser.addEventListener("dblclick", this, true);
-    this.browser.addEventListener("mousedown", this, true);
-    this.browser.addEventListener("mouseup", this, true);
-  },
-
-  detachInspectListeners: function Highlighter_detachInspectListeners()
-  {
-    this.browser.removeEventListener("mousemove", this, true);
-    this.browser.removeEventListener("click", this, true);
-    this.browser.removeEventListener("dblclick", this, true);
-    this.browser.removeEventListener("mousedown", this, true);
-    this.browser.removeEventListener("mouseup", this, true);
-  },
-
-
-  /**
-   * Generic event handler.
-   *
-   * @param nsIDOMEvent aEvent
-   *        The DOM event object.
-   */
-  handleEvent: function Highlighter_handleEvent(aEvent)
-  {
-    switch (aEvent.type) {
-      case "click":
-        this.handleClick(aEvent);
-        break;
-      case "mousemove":
-        this.handleMouseMove(aEvent);
-        break;
-      case "resize":
-        this.computeZoomFactor();
-        this.brieflyDisableTransitions();
-        this.handleResize(aEvent);
-        break;
-      case "dblclick":
-      case "mousedown":
-      case "mouseup":
-        aEvent.stopPropagation();
-        aEvent.preventDefault();
-        break;
-      case "scroll":
-        this.brieflyDisableTransitions();
-        this.highlight();
-        break;
-    }
-  },
-
-  /**
-   * Disable the CSS transitions for a short time to avoid laggy animations
-   * during scrolling or resizing.
-   */
-  brieflyDisableTransitions: function Highlighter_brieflyDisableTransitions()
-  {
-   if (this.transitionDisabler) {
-     this.IUI.win.clearTimeout(this.transitionDisabler);
-   } else {
-     this.veilContainer.setAttribute("disable-transitions", "true");
-     this.nodeInfo.container.setAttribute("disable-transitions", "true");
-   }
-   this.transitionDisabler =
-     this.IUI.win.setTimeout(function() {
-       this.veilContainer.removeAttribute("disable-transitions");
-       this.nodeInfo.container.removeAttribute("disable-transitions");
-       this.transitionDisabler = null;
-     }.bind(this), 500);
-  },
-
-  /**
-   * Handle clicks.
-   *
-   * @param nsIDOMEvent aEvent
-   *        The DOM event.
-   */
-  handleClick: function Highlighter_handleClick(aEvent)
-  {
-    // Stop inspection when the user clicks on a node.
-    if (aEvent.button == 0) {
-      let win = aEvent.target.ownerDocument.defaultView;
-      this.IUI.stopInspecting();
-      win.focus();
-    }
-    aEvent.preventDefault();
-    aEvent.stopPropagation();
-  },
-
-  /**
-   * Handle mousemoves in panel when InspectorUI.inspecting is true.
-   *
-   * @param nsiDOMEvent aEvent
-   *        The MouseEvent triggering the method.
-   */
-  handleMouseMove: function Highlighter_handleMouseMove(aEvent)
-  {
-    let element = this.IUI.elementFromPoint(aEvent.target.ownerDocument,
-      aEvent.clientX, aEvent.clientY);
-    if (element && element != this.node) {
-      this.IUI.inspectNode(element);
-    }
-  },
-
-  /**
-   * Handle window resize events.
-   */
-  handleResize: function Highlighter_handleResize()
-  {
-    this.highlight();
-  },
-};
-
-///////////////////////////////////////////////////////////////////////////
 //// InspectorUI
 
 /**
  * Main controller class for the Inspector.
  *
  * @constructor
  * @param nsIDOMWindow aWindow
  *        The chrome window for which the Inspector instance is created.
@@ -970,18 +277,21 @@ InspectorUI.prototype = {
 
     // initialize the HTML Breadcrumbs
     this.breadcrumbs = new HTMLBreadcrumbs(this);
 
     this.isDirty = false;
 
     this.progressListener = new InspectorProgressListener(this);
 
+    this.chromeWin.addEventListener("keypress", this, false);
+
     // initialize the highlighter
-    this.initializeHighlighter();
+    this.highlighter = new Highlighter(this.chromeWin);
+    this.highlighterReady();
   },
 
   /**
    * Register the Rule View in the Sidebar.
    */
   registerRuleView: function IUI_registerRuleView()
   {
     let isOpen = this.isRuleViewOpen.bind(this);
@@ -1008,27 +318,16 @@ InspectorUI.prototype = {
    * Register and initialize any included tools.
    */
   initTools: function IUI_initTools()
   {
     // Extras go here.
   },
 
   /**
-   * Initialize highlighter.
-   */
-  initializeHighlighter: function IUI_initializeHighlighter()
-  {
-    this.highlighter = new Highlighter(this);
-    this.browser.addEventListener("keypress", this, true);
-    this.highlighter.highlighterContainer.addEventListener("keypress", this, true);
-    this.highlighterReady();
-  },
-
-  /**
    * Initialize the InspectorStore.
    */
   initializeStore: function IUI_initializeStore()
   {
     // First time opened, add the TabSelect listener
     if (this.store.isEmpty()) {
       this.tabbrowser.tabContainer.addEventListener("TabSelect", this, false);
     }
@@ -1091,31 +390,29 @@ InspectorUI.prototype = {
       this.store.setValue(this.winID, "inspecting", this.inspecting);
       this.store.setValue(this.winID, "isDirty", this.isDirty);
     }
 
     if (this.store.isEmpty()) {
       this.tabbrowser.tabContainer.removeEventListener("TabSelect", this, false);
     }
 
+    this.chromeWin.removeEventListener("keypress", this, false);
+
     this.stopInspecting();
-    this.browser.removeEventListener("keypress", this, true);
 
     this.saveToolState(this.winID);
     this.toolsDo(function IUI_toolsHide(aTool) {
       this.unregisterTool(aTool);
     }.bind(this));
 
     // close the sidebar
     this.hideSidebar();
 
     if (this.highlighter) {
-      this.highlighter.highlighterContainer.removeEventListener("keypress",
-                                                                this,
-                                                                true);
       this.highlighter.destroy();
       this.highlighter = null;
     }
 
     if (this.breadcrumbs) {
       this.breadcrumbs.destroy();
       this.breadcrumbs = null;
     }
@@ -1142,52 +439,44 @@ InspectorUI.prototype = {
   startInspecting: function IUI_startInspecting()
   {
     // if currently editing an attribute value, starting
     // "live inspection" mode closes the editor
     if (this.treePanel && this.treePanel.editingContext)
       this.treePanel.closeEditor();
 
     this.inspectToolbutton.checked = true;
-    this.highlighter.attachInspectListeners();
 
     this.inspecting = true;
     this.toolsDim(true);
-    this.highlighter.veilContainer.removeAttribute("locked");
-    this.highlighter.nodeInfo.container.removeAttribute("locked");
+    this.highlighter.unlock();
   },
 
   /**
    * Stop inspecting webpage, detach page listeners, disable highlighter
    * event listeners.
    * @param aPreventScroll
    *        Prevent scroll in the HTML tree?
    */
   stopInspecting: function IUI_stopInspecting(aPreventScroll)
   {
     if (!this.inspecting) {
       return;
     }
 
     this.inspectToolbutton.checked = false;
-    // Detach event listeners from content window and child windows to disable
-    // highlighting. We still want to be notified if the user presses "ESCAPE"
-    // to close the inspector, or "RETURN" to unlock the node, so we don't 
-    // remove the "keypress" event until the highlighter is removed.
-    this.highlighter.detachInspectListeners();
 
     this.inspecting = false;
     this.toolsDim(false);
-    if (this.highlighter.node) {
-      this.select(this.highlighter.node, true, true, !aPreventScroll);
+    if (this.highlighter.getNode()) {
+      this.select(this.highlighter.getNode(), true, true, !aPreventScroll);
     } else {
       this.select(null, true, true);
     }
-    this.highlighter.veilContainer.setAttribute("locked", true);
-    this.highlighter.nodeInfo.container.setAttribute("locked", true);
+    this.highlighter.lock();
   },
 
   /**
    * Select an object in the tree view.
    * @param aNode
    *        node to inspect
    * @param forceUpdate
    *        force an update?
@@ -1202,17 +491,17 @@ InspectorUI.prototype = {
       this.treePanel.closeEditor();
 
     if (!aNode)
       aNode = this.defaultSelection;
 
     if (forceUpdate || aNode != this.selection) {
       this.selection = aNode;
       if (!this.inspecting) {
-        this.highlighter.highlightNode(this.selection);
+        this.highlighter.highlight(this.selection);
       }
     }
 
     this.breadcrumbs.update();
     this.chromeWin.Tilt.update(aNode);
 
     this.toolsSelect(aScroll);
   },
@@ -1221,37 +510,53 @@ InspectorUI.prototype = {
    * Called when the highlighted node is changed by a tool.
    *
    * @param object aUpdater
    *        The tool that triggered the update (if any), that tool's
    *        onChanged will not be called.
    */
   nodeChanged: function IUI_nodeChanged(aUpdater)
   {
-    this.highlighter.highlight();
+    this.highlighter.invalidateSize();
     this.toolsOnChanged(aUpdater);
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// Event Handling
 
   highlighterReady: function IUI_highlighterReady()
   {
     // Setup the InspectorStore or restore state
     this.initializeStore();
 
+    let self = this;
+
+    this.highlighter.addListener("locked", function() {
+      self.stopInspecting();
+    });
+
+    this.highlighter.addListener("unlocked", function() {
+      self.startInspecting();
+    });
+
+    this.highlighter.addListener("nodeselected", function() {
+      self.select(self.highlighter.getNode(), false, false);
+    });
+
     if (this.store.getValue(this.winID, "inspecting")) {
       this.startInspecting();
     }
 
     this.restoreToolState(this.winID);
 
     this.win.focus();
     Services.obs.notifyObservers({wrappedJSObject: this},
                                  INSPECTOR_NOTIFICATIONS.OPENED, null);
+
+    this.highlighter.highlight();
   },
 
   /**
    * Main callback handler for events.
    *
    * @param event
    *        The event to be handled.
    */
@@ -1308,84 +613,16 @@ InspectorUI.prototype = {
         break;
       case "keypress":
         switch (event.keyCode) {
           case this.chromeWin.KeyEvent.DOM_VK_ESCAPE:
             this.closeInspectorUI(false);
             event.preventDefault();
             event.stopPropagation();
             break;
-          case this.chromeWin.KeyEvent.DOM_VK_RETURN:
-            this.toggleInspection();
-            event.preventDefault();
-            event.stopPropagation();
-            break;
-          case this.chromeWin.KeyEvent.DOM_VK_LEFT:
-            let node;
-            if (this.selection) {
-              node = this.selection.parentNode;
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.highlighter.isNodeHighlightable(node)) {
-              this.inspectNode(node, true);
-            }
-            event.preventDefault();
-            event.stopPropagation();
-            break;
-          case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
-            if (this.selection) {
-              // Find the first child that is highlightable.
-              for (let i = 0; i < this.selection.childNodes.length; i++) {
-                node = this.selection.childNodes[i];
-                if (node && this.highlighter.isNodeHighlightable(node)) {
-                  break;
-                }
-              }
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.highlighter.isNodeHighlightable(node)) {
-              this.inspectNode(node, true);
-            }
-            event.preventDefault();
-            event.stopPropagation();
-            break;
-          case this.chromeWin.KeyEvent.DOM_VK_UP:
-            if (this.selection) {
-              // Find a previous sibling that is highlightable.
-              node = this.selection.previousSibling;
-              while (node && !this.highlighter.isNodeHighlightable(node)) {
-                node = node.previousSibling;
-              }
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.highlighter.isNodeHighlightable(node)) {
-              this.inspectNode(node, true);
-            }
-            event.preventDefault();
-            event.stopPropagation();
-            break;
-          case this.chromeWin.KeyEvent.DOM_VK_DOWN:
-            if (this.selection) {
-              // Find a next sibling that is highlightable.
-              node = this.selection.nextSibling;
-              while (node && !this.highlighter.isNodeHighlightable(node)) {
-                node = node.nextSibling;
-              }
-            } else {
-              node = this.defaultSelection;
-            }
-            if (node && this.highlighter.isNodeHighlightable(node)) {
-              this.inspectNode(node, true);
-            }
-            event.preventDefault();
-            event.stopPropagation();
-            break;
         }
         break;
     }
   },
 
   /////////////////////////////////////////////////////////////////////////
   //// CssRuleView methods
 
@@ -1498,88 +735,23 @@ InspectorUI.prototype = {
    * @param aNode
    *        the element in the document to inspect
    * @param aScroll
    *        force scroll?
    */
   inspectNode: function IUI_inspectNode(aNode, aScroll)
   {
     this.select(aNode, true, true);
-    this.highlighter.highlightNode(aNode, { scroll: aScroll });
-  },
-
-  /**
-   * Find an element from the given coordinates. This method descends through
-   * frames to find the element the user clicked inside frames.
-   *
-   * @param DOMDocument aDocument the document to look into.
-   * @param integer aX
-   * @param integer aY
-   * @returns Node|null the element node found at the given coordinates.
-   */
-  elementFromPoint: function IUI_elementFromPoint(aDocument, aX, aY)
-  {
-    let node = aDocument.elementFromPoint(aX, aY);
-    if (node && node.contentDocument) {
-      if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
-        let rect = node.getBoundingClientRect();
-
-        // Gap between the iframe and its content window.
-        let [offsetTop, offsetLeft] = this.getIframeContentOffset(node);
-
-        aX -= rect.left + offsetLeft;
-        aY -= rect.top + offsetTop;
-
-        if (aX < 0 || aY < 0) {
-          // Didn't reach the content document, still over the iframe.
-          return node;
-        }
-      }
-      if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
-          node instanceof Ci.nsIDOMHTMLFrameElement) {
-        let subnode = this.elementFromPoint(node.contentDocument, aX, aY);
-        if (subnode) {
-          node = subnode;
-        }
-      }
-    }
-    return node;
+    this.highlighter.highlight(aNode, aScroll);
   },
 
   ///////////////////////////////////////////////////////////////////////////
   //// Utility functions
 
   /**
-   * Returns iframe content offset (iframe border + padding).
-   * Note: this function shouldn't need to exist, had the platform provided a
-   * suitable API for determining the offset between the iframe's content and
-   * its bounding client rect. Bug 626359 should provide us with such an API.
-   *
-   * @param aIframe
-   *        The iframe.
-   * @returns array [offsetTop, offsetLeft]
-   *          offsetTop is the distance from the top of the iframe and the
-   *            top of the content document.
-   *          offsetLeft is the distance from the left of the iframe and the
-   *            left of the content document.
-   */
-  getIframeContentOffset: function IUI_getIframeContentOffset(aIframe)
-  {
-    let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
-
-    let paddingTop = parseInt(style.getPropertyValue("padding-top"));
-    let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
-
-    let borderTop = parseInt(style.getPropertyValue("border-top-width"));
-    let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
-
-    return [borderTop + paddingTop, borderLeft + paddingLeft];
-  },
-
-  /**
    * Retrieve the unique ID of a window object.
    *
    * @param nsIDOMWindow aWindow
    * @returns integer ID
    */
   getWindowID: function IUI_getWindowID(aWindow)
   {
     if (!aWindow) {
@@ -1924,16 +1096,22 @@ InspectorUI.prototype = {
         }
       }.bind(this));
       this.sidebarTools.forEach(function(tool) {
         if (tool != activeSidebarTool)
           this.chromeDoc.getElementById(
             this.getToolbarButtonId(tool.id)).removeAttribute("checked");
       }.bind(this));
     }
+    if (this.store.getValue(this.winID, "inspecting")) {
+      this.highlighter.unlock();
+    } else {
+      this.highlighter.lock();
+    }
+
     Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.STATE_RESTORED, null);
   },
 
   /**
    * For each tool in the tools collection select the current node that is
    * selected in the highlighter
    * @param aScroll boolean
    *        Do you want to scroll the treepanel?
--- a/browser/devtools/highlighter/test/Makefile.in
+++ b/browser/devtools/highlighter/test/Makefile.in
@@ -61,18 +61,19 @@ include $(topsrcdir)/config/rules.mk
 		browser_inspector_bug_566084_location_changed.js \
 		browser_inspector_infobar.js \
 		browser_inspector_bug_690361.js \
 		browser_inspector_bug_672902_keyboard_shortcuts.js \
 		browser_inspector_keybindings.js \
 		browser_inspector_breadcrumbs.html \
 		browser_inspector_breadcrumbs.js \
 		browser_inspector_bug_699308_iframe_navigation.js \
-        browser_inspector_changes.js \
-        browser_inspector_ruleviewstore.js \
-        browser_inspector_duplicate_ruleview.js \
+		browser_inspector_changes.js \
+		browser_inspector_ruleviewstore.js \
+		browser_inspector_duplicate_ruleview.js \
+		head.js \
 		$(NULL)
 
 # Disabled due to constant failures
 # 		browser_inspector_treePanel_click.js \
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/devtools/highlighter/test/browser_inspector_breadcrumbs.js
+++ b/browser/devtools/highlighter/test/browser_inspector_breadcrumbs.js
@@ -43,32 +43,29 @@ function test()
 
   function runTests()
   {
     Services.obs.removeObserver(runTests,
       InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
 
     cursor = 0;
     executeSoon(function() {
-      Services.obs.addObserver(nodeSelected,
-        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
-
+      InspectorUI.highlighter.addListener("nodeselected", nodeSelected);
       InspectorUI.inspectNode(nodes[0].node);
     });
   }
 
   function nodeSelected()
   {
     executeSoon(function() {
       performTest();
       cursor++;
       if (cursor >= nodes.length) {
 
-        Services.obs.removeObserver(nodeSelected,
-          InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+        InspectorUI.highlighter.removeListener("nodeselected", nodeSelected);
         Services.obs.addObserver(finishUp,
           InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
 
         executeSoon(function() {
           InspectorUI.closeInspectorUI();
         });
       } else {
         let node = nodes[cursor].node;
--- a/browser/devtools/highlighter/test/browser_inspector_bug_665880.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_665880.js
@@ -29,27 +29,25 @@ function test()
   }
 
   function runObjectInspectionTest()
   {
     Services.obs.removeObserver(runObjectInspectionTest,
       InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
 
     executeSoon(function() {
-      Services.obs.addObserver(performTestComparison,
-        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+      InspectorUI.highlighter.addListener("nodeselected", performTestComparison);
 
       InspectorUI.inspectNode(objectNode);
     });
   }
 
   function performTestComparison()
   {
-    Services.obs.removeObserver(performTestComparison,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", performTestComparison);
 
     is(InspectorUI.selection, objectNode, "selection matches node");
 
     Services.obs.addObserver(finishUp,
       InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
     InspectorUI.closeInspectorUI();
   }
 
--- a/browser/devtools/highlighter/test/browser_inspector_bug_672902_keyboard_shortcuts.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_672902_keyboard_shortcuts.js
@@ -33,94 +33,79 @@ function test()
   }
 
   function findAndHighlightNode()
   {
     Services.obs.removeObserver(findAndHighlightNode,
                                 InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
 
     executeSoon(function() {
-      Services.obs.addObserver(highlightBodyNode,
-                               InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
-                               false);
+      InspectorUI.highlighter.addListener("nodeselected", highlightBodyNode);
       // Test that navigating around without a selected node gets us to the
       // body element.
       node = doc.querySelector("body");
       EventUtils.synthesizeKey("VK_RIGHT", { });
     });
   }
 
   function highlightBodyNode()
   {
-    Services.obs.removeObserver(highlightBodyNode,
-                                InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", highlightBodyNode);
     is(InspectorUI.selection, node, "selected body element");
 
     executeSoon(function() {
-      Services.obs.addObserver(highlightHeaderNode,
-                               InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
-                               false);
+      InspectorUI.highlighter.addListener("nodeselected", highlightHeaderNode);
       // Test that moving to the child works.
       node = doc.querySelector("h1");
       EventUtils.synthesizeKey("VK_RIGHT", { });
     });
   }
 
   function highlightHeaderNode()
   {
-    Services.obs.removeObserver(highlightHeaderNode,
-                                InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", highlightHeaderNode);
     is(InspectorUI.selection, node, "selected h1 element");
 
     executeSoon(function() {
-      Services.obs.addObserver(highlightParagraphNode,
-                               InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
-                               false);
+      InspectorUI.highlighter.addListener("nodeselected", highlightParagraphNode);
       // Test that moving to the next sibling works.
       node = doc.querySelector("p");
       EventUtils.synthesizeKey("VK_DOWN", { });
     });
   }
 
   function highlightParagraphNode()
   {
-    Services.obs.removeObserver(highlightParagraphNode,
-                                InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", highlightParagraphNode);
     is(InspectorUI.selection, node, "selected p element");
 
     executeSoon(function() {
-      Services.obs.addObserver(highlightHeaderNodeAgain,
-                               InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
-                               false);
+      InspectorUI.highlighter.addListener("nodeselected", highlightHeaderNodeAgain);
       // Test that moving to the previous sibling works.
       node = doc.querySelector("h1");
       EventUtils.synthesizeKey("VK_UP", { });
     });
   }
 
   function highlightHeaderNodeAgain()
   {
-    Services.obs.removeObserver(highlightHeaderNodeAgain,
-                                InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", highlightHeaderNodeAgain);
     is(InspectorUI.selection, node, "selected h1 element");
 
     executeSoon(function() {
-      Services.obs.addObserver(highlightParentNode,
-                               InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
-                               false);
+      InspectorUI.highlighter.addListener("nodeselected", highlightParentNode);
       // Test that moving to the parent works.
       node = doc.querySelector("body");
       EventUtils.synthesizeKey("VK_LEFT", { });
     });
   }
 
   function highlightParentNode()
   {
-    Services.obs.removeObserver(highlightParentNode,
-                                InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", highlightParentNode);
     is(InspectorUI.selection, node, "selected body element");
 
     // Test that locking works.
     EventUtils.synthesizeKey("VK_RETURN", { });
     executeSoon(isTheNodeLocked);
   }
 
   function isTheNodeLocked()
--- a/browser/devtools/highlighter/test/browser_inspector_bug_674871.js
+++ b/browser/devtools/highlighter/test/browser_inspector_bug_674871.js
@@ -52,45 +52,39 @@ function test()
   }
 
   function runTests()
   {
     Services.obs.removeObserver(runTests,
       InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
 
     executeSoon(function() {
-      Services.obs.addObserver(isTheIframeSelected,
-        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+      InspectorUI.highlighter.addListener("nodeselected", isTheIframeSelected);
 
       moveMouseOver(iframeNode, 1, 1);
     });
   }
 
   function isTheIframeSelected()
   {
-    Services.obs.removeObserver(isTheIframeSelected,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", isTheIframeSelected);
 
     is(InspectorUI.selection, iframeNode, "selection matches node");
     iframeNode.style.marginBottom = doc.defaultView.innerHeight + "px";
     doc.defaultView.scrollBy(0, 40);
 
     executeSoon(function() {
-      Services.obs.addObserver(isTheIframeContentSelected,
-        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
-
+      InspectorUI.highlighter.addListener("nodeselected", isTheIframeContentSelected);
       moveMouseOver(iframeNode, 40, 40);
     });
   }
 
   function isTheIframeContentSelected()
   {
-    Services.obs.removeObserver(isTheIframeContentSelected,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
-
+    InspectorUI.highlighter.removeListener("nodeselected", isTheIframeContentSelected);
     is(InspectorUI.selection, iframeBodyNode, "selection matches node");
     // 184 == 200 + 11(border) + 13(padding) - 40(scroll)
     is(InspectorUI.highlighter._highlightRect.height, 184,
       "highlighter height");
 
     Services.obs.addObserver(finishUp,
       InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
     InspectorUI.closeInspectorUI();
--- a/browser/devtools/highlighter/test/browser_inspector_editor.js
+++ b/browser/devtools/highlighter/test/browser_inspector_editor.js
@@ -53,17 +53,17 @@ function runEditorTests()
 
   // start the tests
   doNextStep();
 }
 
 function highlighterTrap()
 {
   // bug 696107
-  Services.obs.removeObserver(highlighterTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  InspectorUI.highlighter.removeListener("nodeselected", highlighterTrap);
   ok(false, "Highlighter moved. Shouldn't be here!");
   finishUp();
 }
 
 function doEditorTestSteps()
 {
   let treePanel = InspectorUI.treePanel;
   let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor");
@@ -110,32 +110,31 @@ function doEditorTestSteps()
 
   is(treePanel.editingContext.repObj, div, "editor session has correct reference to div");
   is(treePanel.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel");
   is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name");
 
   editorInput.value = "Hello World";
   editorInput.focus();
 
-  Services.obs.addObserver(highlighterTrap,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.addListener("nodeselected", highlighterTrap);
 
   // hit <enter> to save the textbox value
   executeSoon(function() {
     // Extra key to test that keyboard handlers have been removed. bug 696107.
     EventUtils.synthesizeKey("VK_LEFT", {}, attrValNode_id.ownerDocument.defaultView);
     EventUtils.synthesizeKey("VK_RETURN", {}, attrValNode_id.ownerDocument.defaultView);
   });
 
   // two `yield` statements, to trap both the "SAVED" and "CLOSED" events that will be triggered
   yield;
   yield; // End of Step 2
 
   // remove this from previous step
-  Services.obs.removeObserver(highlighterTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  InspectorUI.highlighter.removeListener("nodeselected", highlighterTrap);
 
   // Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value
   ok(!treePanel.editingContext, "Step 3: editor session ended");
   editorVisible = editor.classList.contains("editing");
   ok(!editorVisible, "editor popup hidden");
   attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue");
   ok(!attrValNodeHighlighted, "`id` attribute-value node is no longer editor-highlighted");
   is(div.getAttribute("id"), "Hello World", "`id` attribute-value successfully updated");
--- a/browser/devtools/highlighter/test/browser_inspector_highlighter.js
+++ b/browser/devtools/highlighter/test/browser_inspector_highlighter.js
@@ -98,47 +98,43 @@ function runSelectionTests(subject)
 {
   Services.obs.removeObserver(runSelectionTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   is(subject.wrappedJSObject, InspectorUI,
      "InspectorUI accessible in the observer");
 
   executeSoon(function() {
-    Services.obs.addObserver(performTestComparisons,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.highlighter.addListener("nodeselected", performTestComparisons);
     EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
   });
 }
 
 function performTestComparisons(evt)
 {
-  Services.obs.removeObserver(performTestComparisons,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  InspectorUI.highlighter.removeListener("nodeselected", performTestComparisons);
 
   InspectorUI.stopInspecting();
-  ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
-  is(InspectorUI.highlighter.highlitNode, h1, "highlighter matches selection")
+  ok(isHighlighting(), "highlighter is highlighting");
+  is(getHighlitNode(), h1, "highlighter matches selection")
   is(InspectorUI.selection, h1, "selection matches node");
-  is(InspectorUI.selection, InspectorUI.highlighter.highlitNode, "selection matches highlighter");
+  is(InspectorUI.selection, getHighlitNode(), "selection matches highlighter");
 
 
   div = doc.querySelector("div#checkOutThisWickedSpread");
 
   executeSoon(function() {
-    Services.obs.addObserver(finishTestComparisons,
-        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.highlighter.addListener("nodeselected", finishTestComparisons);
     InspectorUI.inspectNode(div);
   });
 }
 
 function finishTestComparisons()
 {
-  Services.obs.removeObserver(finishTestComparisons,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  InspectorUI.highlighter.removeListener("nodeselected", finishTestComparisons);
 
   // get dimensions of div element
   let divDims = div.getBoundingClientRect();
   let divWidth = divDims.width;
   let divHeight = divDims.height;
 
   // get dimensions of transparent veil box over element
   let veilBoxDims = 
--- a/browser/devtools/highlighter/test/browser_inspector_iframeTest.js
+++ b/browser/devtools/highlighter/test/browser_inspector_iframeTest.js
@@ -90,44 +90,42 @@ function setupIframeTests()
   InspectorUI.openInspectorUI();
 }
 
 function runIframeTests()
 {
   Services.obs.removeObserver(runIframeTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
-  Services.obs.addObserver(performTestComparisons1,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
 
-  executeSoon(moveMouseOver.bind(this, div1));
+  executeSoon(function() {
+    InspectorUI.highlighter.addListener("nodeselected", performTestComparisons1);
+    moveMouseOver(div1)
+  });
 }
 
 function performTestComparisons1()
 {
-  Services.obs.removeObserver(performTestComparisons1,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.removeListener("nodeselected", performTestComparisons1);
 
   is(InspectorUI.selection, div1, "selection matches div1 node");
-  is(InspectorUI.highlighter.highlitNode, div1, "highlighter matches selection");
+  is(getHighlitNode(), div1, "highlighter matches selection");
 
   executeSoon(function() {
-    Services.obs.addObserver(performTestComparisons2,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.highlighter.addListener("nodeselected", performTestComparisons2);
     moveMouseOver(div2);
   });
 }
 
 function performTestComparisons2()
 {
-  Services.obs.removeObserver(performTestComparisons2,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.removeListener("nodeselected", performTestComparisons2);
 
   is(InspectorUI.selection, div2, "selection matches div2 node");
-  is(InspectorUI.highlighter.highlitNode, div2, "highlighter matches selection");
+  is(getHighlitNode(), div2, "highlighter matches selection");
 
   finish();
 }
 
 function test() {
   waitForExplicitFinish();
 
   gBrowser.selectedTab = gBrowser.addTab();
--- a/browser/devtools/highlighter/test/browser_inspector_initialization.js
+++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js
@@ -167,29 +167,29 @@ function openInspectorForContextTest()
     InspectorUI.openInspectorUI(salutation);
   });
 }
 
 function inspectNodesFromContextTestWhileOpen()
 {
   Services.obs.removeObserver(inspectNodesFromContextTestWhileOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
   Services.obs.addObserver(inspectNodesFromContextTestTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
-  Services.obs.addObserver(inspectNodesFromContextTestHighlight, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.addListener("nodeselected", inspectNodesFromContextTestHighlight);
   is(InspectorUI.selection, salutation, "Inspector is highlighting salutation");
   closing = doc.getElementById("closing");
   ok(closing, "we have the closing statement");
   executeSoon(function() {
     InspectorUI.openInspectorUI(closing);
   });
 }
 
 function inspectNodesFromContextTestHighlight()
 {
   winId = InspectorUI.winID;
-  Services.obs.removeObserver(inspectNodesFromContextTestHighlight, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  InspectorUI.highlighter.removeListener("nodeselected", inspectNodesFromContextTestHighlight);
   Services.obs.addObserver(finishInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.DESTROYED, false);
   is(InspectorUI.selection, closing, "InspectorUI.selection is header");
   executeSoon(function() {
     InspectorUI.closeInspectorUI();
   });
 }
 
 function inspectNodesFromContextTestTrap()
--- a/browser/devtools/highlighter/test/browser_inspector_keybindings.js
+++ b/browser/devtools/highlighter/test/browser_inspector_keybindings.js
@@ -27,28 +27,24 @@ function test()
   }
 
   function highlightNode()
   {
     Services.obs.removeObserver(highlightNode,
       InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
 
     executeSoon(function() {
-      Services.obs.addObserver(lockNode,
-        InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
-
+      InspectorUI.highlighter.addListener("nodeselected", lockNode);
       InspectorUI.inspectNode(node);
     });
   }
 
   function lockNode()
   {
-    Services.obs.removeObserver(lockNode,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
-
+    InspectorUI.highlighter.removeListener("nodeselected", lockNode);
     EventUtils.synthesizeKey("VK_RETURN", { });
 
     executeSoon(isTheNodeLocked);
   }
 
   function isTheNodeLocked()
   {
     is(InspectorUI.selection, node, "selection matches node");
--- a/browser/devtools/highlighter/test/browser_inspector_registertools.js
+++ b/browser/devtools/highlighter/test/browser_inspector_registertools.js
@@ -91,23 +91,23 @@ function setupHighlighterTests()
 
 function inspectorOpen()
 {
   info("we received the inspector-opened notification");
   Services.obs.removeObserver(inspectorOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
   toolsLength = InspectorUI.tools.length;
   toolEvents = InspectorUI.toolEvents.length;
   info("tools registered");
-  Services.obs.addObserver(startToolTests, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.addListener("nodeselected", startToolTests);
   InspectorUI.inspectNode(h1);
 }
 
 function startToolTests(evt)
 {
-  Services.obs.removeObserver(startToolTests, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+  InspectorUI.highlighter.removeListener("nodeselected", startToolTests);
   InspectorUI.stopInspecting();
   info("Getting InspectorUI.tools");
   let tools = InspectorUI.tools;
 
   tool1 = InspectorUI.tools["tool_1"];
   tool2 = InspectorUI.tools["tool_2"];
   tool3 = InspectorUI.tools["tool_3"];
 
--- a/browser/devtools/highlighter/test/browser_inspector_scrolling.js
+++ b/browser/devtools/highlighter/test/browser_inspector_scrolling.js
@@ -68,32 +68,33 @@ function toggleInspector()
   Services.obs.addObserver(inspectNode, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.toggleInspectorUI();
 }
 
 function inspectNode()
 {
   Services.obs.removeObserver(inspectNode,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
-  Services.obs.addObserver(performScrollingTest,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+
+  InspectorUI.highlighter.addListener("nodeselected", performScrollingTest);
 
   executeSoon(function() {
     InspectorUI.inspectNode(div);
   });
 }
 
 function performScrollingTest()
 {
-  Services.obs.removeObserver(performScrollingTest,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.removeListener("nodeselected", performScrollingTest);
 
-  EventUtils.synthesizeMouseScroll(div, 10, 10,
-    {axis:"vertical", delta:50, type:"MozMousePixelScroll"},
-    iframe.contentWindow);
+  executeSoon(function() {
+    EventUtils.synthesizeMouseScroll(div, 10, 10,
+      {axis:"vertical", delta:50, type:"MozMousePixelScroll"},
+      iframe.contentWindow);
+  });
 
   gBrowser.selectedBrowser.addEventListener("scroll", function() {
     gBrowser.selectedBrowser.removeEventListener("scroll", arguments.callee,
       false);
 
     is(iframe.contentDocument.body.scrollTop, 50, "inspected iframe scrolled");
 
     div = iframe = doc = null;
--- a/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js
@@ -29,34 +29,31 @@ function test() {
   function runTests() {
     Services.obs.removeObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
     Services.obs.addObserver(testNode1, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false);
     InspectorUI.select(node1, true, true, true);
     InspectorUI.openTreePanel();
   }
 
   function testNode1() {
-    dump("testNode1\n");
     Services.obs.removeObserver(testNode1, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY);
     is(InspectorUI.selection, node1, "selection matches node");
-    is(InspectorUI.highlighter.node, node1, "selection matches node");
+    is(getHighlitNode(), node1, "selection matches node");
     testNode2();
   }
 
   function testNode2() {
-    dump("testNode2\n")
-    Services.obs.addObserver(testHighlightingNode2, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.highlighter.addListener("nodeselected", testHighlightingNode2);
     InspectorUI.treePanelSelect("node2");
   }
 
   function testHighlightingNode2() {
-    dump("testHighlightingNode2\n")
-    Services.obs.removeObserver(testHighlightingNode2, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
+    InspectorUI.highlighter.removeListener("nodeselected", testHighlightingNode2);
     is(InspectorUI.selection, node2, "selection matches node");
-    is(InspectorUI.highlighter.node, node2, "selection matches node");
+    is(getHighlitNode(), node2, "selection matches node");
     Services.obs.addObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
     InspectorUI.closeInspectorUI();
   }
 
   function finishUp() {
     Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
     doc = node1 = node2 = null;
     gBrowser.removeCurrentTab();
--- a/browser/devtools/highlighter/test/browser_inspector_treeSelection.js
+++ b/browser/devtools/highlighter/test/browser_inspector_treeSelection.js
@@ -71,31 +71,30 @@ function setupSelectionTests()
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
   InspectorUI.openInspectorUI();
 }
 
 function runSelectionTests()
 {
   Services.obs.removeObserver(runSelectionTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
-  Services.obs.addObserver(performTestComparisons,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+
   executeSoon(function() {
+    InspectorUI.highlighter.addListener("nodeselected", performTestComparisons);
     InspectorUI.inspectNode(h1);
   });
 }
 
 function performTestComparisons(evt)
 {
-  Services.obs.removeObserver(performTestComparisons,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.removeListener("nodeselected", performTestComparisons);
 
   is(h1, InspectorUI.selection, "selection matches node");
-  ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
-  is(InspectorUI.highlighter.highlitNode, h1, "highlighter highlighting correct node");
+  ok(isHighlighting(), "highlighter is highlighting");
+  is(getHighlitNode(), h1, "highlighter highlighting correct node");
 
   finishUp();
 }
 
 function finishUp() {
   InspectorUI.closeInspectorUI();
   doc = h1 = null;
   gBrowser.removeCurrentTab();
new file mode 100644
--- /dev/null
+++ b/browser/devtools/highlighter/test/head.js
@@ -0,0 +1,78 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is DevTools test code.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ *
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Paul Rouget <paul@mozilla.com> (Original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+const Cu = Components.utils;
+Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
+
+function isHighlighting()
+{
+  let veil = InspectorUI.highlighter.veilTransparentBox;
+  return !(veil.style.visibility == "hidden");
+}
+
+function getHighlitNode()
+{
+  let h = InspectorUI.highlighter;
+  if (!isHighlighting() || !h._contentRect)
+    return null;
+
+  let a = {
+    x: h._contentRect.left,
+    y: h._contentRect.top
+  };
+
+  let b = {
+    x: a.x + h._contentRect.width,
+    y: a.y + h._contentRect.height
+  };
+
+  // Get midpoint of diagonal line.
+  let midpoint = midPoint(a, b);
+
+  return LayoutHelpers.getElementFromPoint(h.win.document, midpoint.x,
+    midpoint.y);
+}
+
+
+function midPoint(aPointA, aPointB)
+{
+  let pointC = { };
+  pointC.x = (aPointB.x - aPointA.x) / 2 + aPointA.x;
+  pointC.y = (aPointB.y - aPointA.y) / 2 + aPointA.y;
+  return pointC;
+}
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -7,9 +7,10 @@ browser.jar:
     content/browser/splitview.css                 (styleeditor/splitview.css)
     content/browser/styleeditor.css               (styleeditor/styleeditor.css)
     content/browser/devtools/csshtmltree.xul      (styleinspector/csshtmltree.xul)
     content/browser/devtools/cssruleview.xul      (styleinspector/cssruleview.xul)
     content/browser/devtools/styleinspector.css   (styleinspector/styleinspector.css)
     content/browser/orion.js                      (sourceeditor/orion/orion.js)
     content/browser/orion.css                     (sourceeditor/orion/orion.css)
     content/browser/orion-mozilla.css             (sourceeditor/orion/mozilla.css)
+    content/browser/source-editor-overlay.xul     (sourceeditor/source-editor-overlay.xul)
 
--- a/browser/devtools/scratchpad/scratchpad.xul
+++ b/browser/devtools/scratchpad/scratchpad.xul
@@ -40,29 +40,31 @@
    - ***** END LICENSE BLOCK ***** -->
 #endif
 <!DOCTYPE window [
 <!ENTITY % scratchpadDTD SYSTEM "chrome://browser/locale/devtools/scratchpad.dtd" >
  %scratchpadDTD;
 ]>
 <?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
 <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
+<?xul-overlay href="chrome://browser/content/source-editor-overlay.xul"?>
 
 <window id="main-window"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         title="&window.title;"
         windowtype="devtools:scratchpad"
         screenX="4" screenY="4"
         width="640" height="480"
         persist="screenX screenY width height sizemode">
 
 <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
 <script type="application/javascript" src="chrome://browser/content/scratchpad.js"/>
 
 <commandset id="editMenuCommands"/>
+<commandset id="sourceEditorCommands"/>
 
 <commandset id="sp-commandset">
   <command id="sp-cmd-newWindow" oncommand="Scratchpad.openScratchpad();"/>
   <command id="sp-cmd-openFile" oncommand="Scratchpad.openFile();"/>
   <command id="sp-cmd-save" oncommand="Scratchpad.saveFile();"/>
   <command id="sp-cmd-saveas" oncommand="Scratchpad.saveFileAs();"/>
 
   <!-- TODO: bug 650340 - implement printFile()
@@ -137,16 +139,42 @@
   <key id="sp-key-errorConsole"
        key="&errorConsoleCmd.commandkey;"
        command="sp-cmd-errorConsole"
        modifiers="accel,shift"/>
   <key id="sp-key-webConsole"
        key="&webConsoleCmd.commandkey;"
        command="sp-cmd-webConsole"
        modifiers="accel,shift"/>
+  <key id="key_find"
+       key="&findCmd.key;"
+       command="cmd_find"
+       modifiers="accel"/>
+#ifdef XP_MACOSX
+  <key id="key_findAgain"
+       key="&findAgainCmd.key;"
+       command="cmd_findAgain"
+       modifiers="accel"/>
+  <key id="key_findPrevious"
+       key="&findPreviousCmd.key;"
+       command="cmd_findPrevious"
+       modifiers="accel,shift"/>
+#else
+  <key id="key_findAgain"
+       keycode="VK_F3"
+       command="cmd_findAgain"/>
+  <key id="key_findPrevious"
+       keycode="VK_F3"
+       command="cmd_findPrevious"
+       modifiers="shift"/>
+#endif
+  <key id="key_gotoLine"
+       key="&gotoLineCmd.key;"
+       command="cmd_gotoLine"
+       modifiers="accel"/>
 </keyset>
 
 
 <menubar id="sp-menubar">
   <menu id="sp-file-menu" label="&fileMenu.label;"
         accesskey="&fileMenu.accesskey;">
     <menupopup id="sp-menu-filepopup">
       <menuitem id="sp-menu-newscratchpad"
@@ -218,33 +246,33 @@
                 accesskey="&pasteCmd.accesskey;"
                 command="cmd_paste"/>
       <menuseparator/>
       <menuitem id="sp-menu-selectAll"
                 label="&selectAllCmd.label;"
                 key="key_selectAll"
                 accesskey="&selectAllCmd.accesskey;"
                 command="cmd_selectAll"/>
-
-      <!-- TODO: bug 650345 - implement search and replace
+      <menuseparator/>
       <menuitem id="sp-menu-find"
-                label="&findOnCmd.label;"
-                accesskey="&findOnCmd.accesskey;"
+                label="&findCmd.label;"
+                accesskey="&findCmd.accesskey;"
                 key="key_find"
-                disabled="true"
                 command="cmd_find"/>
       <menuitem id="sp-menu-findAgain"
                 label="&findAgainCmd.label;"
                 accesskey="&findAgainCmd.accesskey;"
                 key="key_findAgain"
-                disabled="true"
                 command="cmd_findAgain"/>
-      <menuseparator id="sp-execute-separator"/>
-      -->
-
+      <menuseparator/>
+      <menuitem id="sp-menu-gotoLine"
+                label="&gotoLineCmd.label;"
+                accesskey="&gotoLineCmd.accesskey;"
+                key="key_gotoLine"
+                command="cmd_gotoLine"/>
     </menupopup>
   </menu>
 
   <menu id="sp-execute-menu" label="&executeMenu.label;"
         accesskey="&executeMenu.accesskey;">
     <menupopup id="sp-menu_executepopup">
       <menuitem id="sp-text-run"
                 label="&run.label;"
--- a/browser/devtools/scratchpad/test/Makefile.in
+++ b/browser/devtools/scratchpad/test/Makefile.in
@@ -53,15 +53,17 @@ include $(topsrcdir)/config/rules.mk
 		browser_scratchpad_ui.js \
 		browser_scratchpad_bug_646070_chrome_context_pref.js \
 		browser_scratchpad_bug_660560_tab.js \
 		browser_scratchpad_open.js \
 		browser_scratchpad_restore.js \
 		browser_scratchpad_bug_679467_falsy.js \
 		browser_scratchpad_bug_699130_edit_ui_updates.js \
 		browser_scratchpad_bug_669612_unsaved.js \
-		head.js \
 		browser_scratchpad_bug_653427_confirm_close.js \
 		browser_scratchpad_bug684546_reset_undo.js \
 		browser_scratchpad_bug690552_display_outputs_errors.js \
+		browser_scratchpad_bug650345_find_ui.js \
+		browser_scratchpad_bug714942_goto_line_ui.js \
+		head.js \
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug650345_find_ui.js
@@ -0,0 +1,97 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function browserLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", browserLoad, true);
+    openScratchpad(runTests);
+  }, true);
+
+  content.location = "data:text/html,<p>test the Find feature in Scratchpad";
+}
+
+function runTests(aWindow, aScratchpad)
+{
+  let editor = aScratchpad.editor;
+  let text = "foobar bug650345\nBug650345 bazbaz\nfoobar omg\ntest";
+  editor.setText(text);
+
+  let needle = "foobar";
+  editor.setSelection(0, needle.length);
+
+  let oldPrompt = Services.prompt;
+  Services.prompt = {
+    prompt: function() { return true; },
+  };
+
+  let findKey = "F";
+  info("test Ctrl/Cmd-" + findKey + " (find)");
+  EventUtils.synthesizeKey(findKey, {accelKey: true}, aWindow);
+  let selection = editor.getSelection();
+  let newIndex = text.indexOf(needle, needle.length);
+  is(selection.start, newIndex, "selection.start is correct");
+  is(selection.end, newIndex + needle.length, "selection.end is correct");
+
+  info("test cmd_find");
+  aWindow.goDoCommand("cmd_find");
+  selection = editor.getSelection();
+  is(selection.start, 0, "selection.start is correct");
+  is(selection.end, needle.length, "selection.end is correct");
+
+  let findNextKey = Services.appinfo.OS == "Darwin" ? "G" : "VK_F3";
+  let findNextKeyOptions = Services.appinfo.OS == "Darwin" ?
+                           {accelKey: true} : {};
+
+  info("test " + findNextKey + " (findNext)");
+  EventUtils.synthesizeKey(findNextKey, findNextKeyOptions, aWindow);
+  selection = editor.getSelection();
+  is(selection.start, newIndex, "selection.start is correct");
+  is(selection.end, newIndex + needle.length, "selection.end is correct");
+
+  info("test cmd_findAgain");
+  aWindow.goDoCommand("cmd_findAgain");
+  selection = editor.getSelection();
+  is(selection.start, 0, "selection.start is correct");
+  is(selection.end, needle.length, "selection.end is correct");
+
+  let findPreviousKey = Services.appinfo.OS == "Darwin" ? "G" : "VK_F3";
+  let findPreviousKeyOptions = Services.appinfo.OS == "Darwin" ?
+                           {accelKey: true, shiftKey: true} : {shiftKey: true};
+
+  info("test " + findPreviousKey + " (findPrevious)");
+  EventUtils.synthesizeKey(findPreviousKey, findPreviousKeyOptions, aWindow);
+  selection = editor.getSelection();
+  is(selection.start, newIndex, "selection.start is correct");
+  is(selection.end, newIndex + needle.length, "selection.end is correct");
+
+  info("test cmd_findPrevious");
+  aWindow.goDoCommand("cmd_findPrevious");
+  selection = editor.getSelection();
+  is(selection.start, 0, "selection.start is correct");
+  is(selection.end, needle.length, "selection.end is correct");
+
+  needle = "BAZbaz";
+  newIndex = text.toLowerCase().indexOf(needle.toLowerCase());
+
+  Services.prompt = {
+    prompt: function(aWindow, aTitle, aMessage, aValue) {
+      aValue.value = needle;
+      return true;
+    },
+  };
+
+  info("test Ctrl/Cmd-" + findKey + " (find) with a custom value");
+  EventUtils.synthesizeKey(findKey, {accelKey: true}, aWindow);
+  selection = editor.getSelection();
+  is(selection.start, newIndex, "selection.start is correct");
+  is(selection.end, newIndex + needle.length, "selection.end is correct");
+
+  Services.prompt = oldPrompt;
+
+  finish();
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug714942_goto_line_ui.js
@@ -0,0 +1,45 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function test()
+{
+  waitForExplicitFinish();
+
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function browserLoad() {
+    gBrowser.selectedBrowser.removeEventListener("load", browserLoad, true);
+    openScratchpad(runTests);
+  }, true);
+
+  content.location = "data:text/html,<p>test the 'Jump to line' feature in Scratchpad";
+}
+
+function runTests(aWindow, aScratchpad)
+{
+  let editor = aScratchpad.editor;
+  let text = "foobar bug650345\nBug650345 bazbaz\nfoobar omg\ntest";
+  editor.setText(text);
+  editor.setCaretOffset(0);
+
+  let oldPrompt = Services.prompt;
+  let desiredValue = null;
+  Services.prompt = {
+    prompt: function(aWindow, aTitle, aMessage, aValue) {
+      aValue.value = desiredValue;
+      return true;
+    },
+  };
+
+  desiredValue = 3;
+  EventUtils.synthesizeKey("J", {accelKey: true}, aWindow);
+  is(editor.getCaretOffset(), 34, "caret offset is correct");
+
+  desiredValue = 2;
+  aWindow.goDoCommand("cmd_gotoLine")
+  is(editor.getCaretOffset(), 17, "caret offset is correct (again)");
+
+  Services.prompt = oldPrompt;
+
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/LayoutHelpers.jsm
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Mozilla LayoutHelpers Module.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Rob Campbell <rcampbell@mozilla.com> (original author)
+ *   Mihai Șucan <mihai.sucan@gmail.com>
+ *   Julian Viereck <jviereck@mozilla.com>
+ *   Paul Rouget <paul@mozilla.com>
+ *   Kyle Simpson <ksimpson@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+const Cu = Components.utils;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+var EXPORTED_SYMBOLS = ["LayoutHelpers"];
+
+LayoutHelpers = {
+
+  /**
+   * Compute the position and the dimensions for the visible portion
+   * of a node, relativalely to the root window.
+   *
+   * @param nsIDOMNode aNode
+   *        a DOM element to be highlighted
+   */
+  getDirtyRect: function LH_getDirectyRect(aNode) {
+    let frameWin = aNode.ownerDocument.defaultView;
+    let clientRect = aNode.getBoundingClientRect();
+
+    // Go up in the tree of frames to determine the correct rectangle.
+    // clientRect is read-only, we need to be able to change properties.
+    rect = {top: clientRect.top,
+            left: clientRect.left,
+            width: clientRect.width,
+            height: clientRect.height};
+
+    // We iterate through all the parent windows.
+    while (true) {
+
+      // Does the selection overflow on the right of its window?
+      let diffx = frameWin.innerWidth - (rect.left + rect.width);
+      if (diffx < 0) {
+        rect.width += diffx;
+      }
+
+      // Does the selection overflow on the bottom of its window?
+      let diffy = frameWin.innerHeight - (rect.top + rect.height);
+      if (diffy < 0) {
+        rect.height += diffy;
+      }
+
+      // Does the selection overflow on the left of its window?
+      if (rect.left < 0) {
+        rect.width += rect.left;
+        rect.left = 0;
+      }
+
+      // Does the selection overflow on the top of its window?
+      if (rect.top < 0) {
+        rect.height += rect.top;
+        rect.top = 0;
+      }
+
+      // Selection has been clipped to fit in its own window.
+
+      // Are we in the top-level window?
+      if (frameWin.parent === frameWin || !frameWin.frameElement) {
+        break;
+      }
+
+      // We are in an iframe.
+      // We take into account the parent iframe position and its
+      // offset (borders and padding).
+      let frameRect = frameWin.frameElement.getBoundingClientRect();
+
+      let [offsetTop, offsetLeft] =
+        this.getIframeContentOffset(frameWin.frameElement);
+
+      rect.top += frameRect.top + offsetTop;
+      rect.left += frameRect.left + offsetLeft;
+
+      frameWin = frameWin.parent;
+    }
+
+    return rect;
+  },
+
+  /**
+   * Returns iframe content offset (iframe border + padding).
+   * Note: this function shouldn't need to exist, had the platform provided a
+   * suitable API for determining the offset between the iframe's content and
+   * its bounding client rect. Bug 626359 should provide us with such an API.
+   *
+   * @param aIframe
+   *        The iframe.
+   * @returns array [offsetTop, offsetLeft]
+   *          offsetTop is the distance from the top of the iframe and the
+   *            top of the content document.
+   *          offsetLeft is the distance from the left of the iframe and the
+   *            left of the content document.
+   */
+  getIframeContentOffset: function LH_getIframeContentOffset(aIframe) {
+    let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
+
+    let paddingTop = parseInt(style.getPropertyValue("padding-top"));
+    let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
+
+    let borderTop = parseInt(style.getPropertyValue("border-top-width"));
+    let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
+
+    return [borderTop + paddingTop, borderLeft + paddingLeft];
+  },
+
+  /**
+   * Apply the page zoom factor.
+   */
+  getZoomedRect: function LH_getZoomedRect(aWin, aRect) {
+    // get page zoom factor, if any
+    let zoom =
+      aWin.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+        .getInterface(Components.interfaces.nsIDOMWindowUtils)
+        .screenPixelsPerCSSPixel;
+
+    // adjust rect for zoom scaling
+    let aRectScaled = {};
+    for (let prop in aRect) {
+      aRectScaled[prop] = aRect[prop] * zoom;
+    }
+
+    return aRectScaled;
+  },
+
+
+  /**
+   * Find an element from the given coordinates. This method descends through
+   * frames to find the element the user clicked inside frames.
+   *
+   * @param DOMDocument aDocument the document to look into.
+   * @param integer aX
+   * @param integer aY
+   * @returns Node|null the element node found at the given coordinates.
+   */
+  getElementFromPoint: function LH_elementFromPoint(aDocument, aX, aY)
+  {
+    let node = aDocument.elementFromPoint(aX, aY);
+    if (node && node.contentDocument) {
+      if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
+        let rect = node.getBoundingClientRect();
+
+        // Gap between the iframe and its content window.
+        let [offsetTop, offsetLeft] = LayoutHelpers.getIframeContentOffset(node);
+
+        aX -= rect.left + offsetLeft;
+        aY -= rect.top + offsetTop;
+
+        if (aX < 0 || aY < 0) {
+          // Didn't reach the content document, still over the iframe.
+          return node;
+        }
+      }
+      if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
+          node instanceof Ci.nsIDOMHTMLFrameElement) {
+        let subnode = this.getElementFromPoint(node.contentDocument, aX, aY);
+        if (subnode) {
+          node = subnode;
+        }
+      }
+    }
+    return node;
+  },
+};
--- a/browser/devtools/shared/Makefile.in
+++ b/browser/devtools/shared/Makefile.in
@@ -49,8 +49,9 @@ ifdef ENABLE_TESTS
 	DIRS += test
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	$(NSINSTALL) $(srcdir)/Templater.jsm $(FINAL_TARGET)/modules/devtools
 	$(NSINSTALL) $(srcdir)/Promise.jsm $(FINAL_TARGET)/modules/devtools
+	$(NSINSTALL) $(srcdir)/LayoutHelpers.jsm $(FINAL_TARGET)/modules/devtools
--- a/browser/devtools/sourceeditor/Makefile.in
+++ b/browser/devtools/sourceeditor/Makefile.in
@@ -47,11 +47,12 @@ include $(DEPTH)/config/autoconf.mk
 ifdef ENABLE_TESTS
 	DIRS += test
 endif
 
 EXTRA_JS_MODULES = \
 	source-editor.jsm \
 	source-editor-orion.jsm \
 	source-editor-textarea.jsm \
+	source-editor-ui.jsm \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -39,16 +39,17 @@
 
 "use strict";
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/source-editor-ui.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "clipboardHelper",
                                    "@mozilla.org/widget/clipboardhelper;1",
                                    "nsIClipboardHelper");
 
 const ORION_SCRIPT = "chrome://browser/content/orion.js";
 const ORION_IFRAME = "data:text/html;charset=utf8,<!DOCTYPE html>" +
   "<html style='height:100%' dir='ltr'>" +
@@ -121,16 +122,18 @@ function SourceEditor() {
   // Update the SourceEditor defaults from user preferences.
 
   SourceEditor.DEFAULTS.TAB_SIZE =
     Services.prefs.getIntPref(SourceEditor.PREFS.TAB_SIZE);
   SourceEditor.DEFAULTS.EXPAND_TAB =
     Services.prefs.getBoolPref(SourceEditor.PREFS.EXPAND_TAB);
 
   this._onOrionSelection = this._onOrionSelection.bind(this);
+
+  this.ui = new SourceEditorUI(this);
 }
 
 SourceEditor.prototype = {
   _view: null,
   _iframe: null,
   _model: null,
   _undoStack: null,
   _linesRuler: null,
@@ -139,16 +142,23 @@ SourceEditor.prototype = {
   _annotationModel: null,
   _dragAndDrop: null,
   _mode: null,
   _expandTab: null,
   _tabSize: null,
   _iframeWindow: null,
 
   /**
+   * The Source Editor user interface manager.
+   * @type object
+   *       An instance of the SourceEditorUI.
+   */
+  ui: null,
+
+  /**
    * The editor container element.
    * @type nsIDOMElement
    */
   parentElement: null,
 
   /**
    * Initialize the editor.
    *
@@ -199,16 +209,17 @@ SourceEditor.prototype = {
     this._iframe.addEventListener("load", onIframeLoad, true);
 
     this._iframe.setAttribute("src", ORION_IFRAME);
 
     aElement.appendChild(this._iframe);
     this.parentElement = aElement;
     this._config = aConfig;
     this._onReadyCallback = aCallback;
+    this.ui.init();
   },
 
   /**
    * The editor iframe load event handler.
    * @private
    */
   _onIframeLoad: function SE__onIframeLoad()
   {
@@ -267,21 +278,32 @@ SourceEditor.prototype = {
 
     this.setMode(config.mode || SourceEditor.DEFAULTS.MODE);
 
     this._undoStack = new UndoStack(this._view,
       config.undoLimit || SourceEditor.DEFAULTS.UNDO_LIMIT);
 
     this._dragAndDrop = new TextDND(this._view, this._undoStack);
 
-    this._view.setAction("undo", this.undo.bind(this));
-    this._view.setAction("redo", this.redo.bind(this));
-    this._view.setAction("tab", this._doTab.bind(this));
-    this._view.setAction("Unindent Lines", this._doUnindentLines.bind(this));
-    this._view.setAction("enter", this._doEnter.bind(this));
+    let actions = {
+      "undo": [this.undo, this],
+      "redo": [this.redo, this],
+      "tab": [this._doTab, this],
+      "Unindent Lines": [this._doUnindentLines, this],
+      "enter": [this._doEnter, this],
+      "Find...": [this.ui.find, this.ui],
+      "Find Next Occurrence": [this.ui.findNext, this.ui],
+      "Find Previous Occurrence": [this.ui.findPrevious, this.ui],
+      "Goto Line...": [this.ui.gotoLine, this.ui],
+    };
+
+    for (let name in actions) {
+      let action = actions[name];
+      this._view.setAction(name, action[0].bind(action[1]));
+    }
 
     let keys = (config.keys || []).concat(DEFAULT_KEYBINDINGS);
     keys.forEach(function(aKey) {
       let binding = new KeyBinding(aKey.code, aKey.accel, aKey.shift, aKey.alt);
       this._view.setKeyBinding(binding, aKey.action);
 
       if (aKey.callback) {
         this._view.setAction(aKey.action, aKey.callback);
@@ -291,16 +313,17 @@ SourceEditor.prototype = {
 
   /**
    * The Orion "Load" event handler. This is called when the Orion editor
    * completes the initialization.
    * @private
    */
   _onOrionLoad: function SE__onOrionLoad()
   {
+    this.ui.onReady();
     if (this._onReadyCallback) {
       this._onReadyCallback(this);
       this._onReadyCallback = null;
     }
   },
 
   /**
    * The "tab" editor action implementation. This adds support for expanded tabs
@@ -878,23 +901,27 @@ SourceEditor.prototype = {
   destroy: function SE_destroy()
   {
     if (Services.appinfo.OS == "Linux") {
       this._view.removeEventListener("Selection", this._onOrionSelection);
     }
     this._onOrionSelection = null;
 
     this._view.destroy();
+    this.ui.destroy();
+    this.ui = null;
+
     this.parentElement.removeChild(this._iframe);
     this.parentElement = null;
     this._iframeWindow = null;
     this._iframe = null;
     this._undoStack = null;
     this._styler = null;
     this._linesRuler = null;
     this._dragAndDrop = null;
     this._annotationModel = null;
     this._annotationStyler = null;
     this._view = null;
     this._model = null;
     this._config = null;
+    this._lastFind = null;
   },
 };
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/source-editor-overlay.xul
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Source Editor.
+   -
+   - The Initial Developer of the Original Code is
+   - The Mozilla Foundation.
+   - Portions created by the Initial Developer are Copyright (C) 2012
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Mihai Sucan <mihai.sucan@gmail.com> (original author)
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - 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 ***** -->
+
+<overlay id="sourceEditorOverlay"
+         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <commandset id="sourceEditorCommands">
+    <command id="cmd_find" oncommand="goDoCommand('cmd_find')"/>
+    <command id="cmd_findAgain" oncommand="goDoCommand('cmd_findAgain')" disabled="true"/>
+    <command id="cmd_findPrevious" oncommand="goDoCommand('cmd_findPrevious')" disabled="true"/>
+    <command id="cmd_gotoLine" oncommand="goDoCommand('cmd_gotoLine')"/>
+  </commandset>
+</overlay>
--- a/browser/devtools/sourceeditor/source-editor-textarea.jsm
+++ b/browser/devtools/sourceeditor/source-editor-textarea.jsm
@@ -39,19 +39,33 @@
 
 "use strict";
 
 const Cu = Components.utils;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/source-editor-ui.jsm");
 
 const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 
+/**
+ * Default key bindings in the textarea editor.
+ */
+const DEFAULT_KEYBINDINGS = [
+  {
+    _action: "_doTab",
+    keyCode: Ci.nsIDOMKeyEvent.DOM_VK_TAB,
+    shiftKey: false,
+    accelKey: false,
+    altKey: false,
+  },
+];
+
 var EXPORTED_SYMBOLS = ["SourceEditor"];
 
 /**
  * The SourceEditor object constructor. The SourceEditor component allows you to
  * provide users with an editor tailored to the specific needs of editing source
  * code, aimed primarily at web developers.
  *
  * The editor used here is a simple textarea. This is used as a fallback
@@ -64,28 +78,37 @@ function SourceEditor() {
 
   SourceEditor.DEFAULTS.TAB_SIZE =
     Services.prefs.getIntPref(SourceEditor.PREFS.TAB_SIZE);
   SourceEditor.DEFAULTS.EXPAND_TAB =
     Services.prefs.getBoolPref(SourceEditor.PREFS.EXPAND_TAB);
 
   this._listeners = {};
   this._lastSelection = {};
+
+  this.ui = new SourceEditorUI(this);
 }
 
 SourceEditor.prototype = {
   _textbox: null,
   _editor: null,
   _listeners: null,
   _lineDelimiter: null,
   _editActionListener: null,
   _expandTab: null,
   _tabSize: null,
 
   /**
+   * The Source Editor user interface manager.
+   * @type object
+   *       An instance of the SourceEditorUI.
+   */
+  ui: null,
+
+  /**
    * The editor container element.
    * @type nsIDOMElement
    */
   parentElement: null,
 
   /**
    * Initialize the editor.
    *
@@ -131,17 +154,17 @@ SourceEditor.prototype = {
     this._textbox.style.MozTabSize = this._tabSize;
 
     this._textbox.setAttribute("value", aConfig.placeholderText || "");
     this._textbox.setAttribute("class", "monospace");
     this._textbox.style.direction = "ltr";
     this._textbox.readOnly = aConfig.readOnly;
 
     // Make sure that the SourceEditor Selection events are fired properly.
-    // Also make sure that the Tab key inserts spaces when expandTab is true.
+    // Also make sure that the configured keyboard bindings work.
     this._textbox.addEventListener("select", this._onSelect.bind(this), false);
     this._textbox.addEventListener("keypress", this._onKeyPress.bind(this), false);
     this._textbox.addEventListener("keyup", this._onSelect.bind(this), false);
     this._textbox.addEventListener("click", this._onSelect.bind(this), false);
 
     // Mimic the mode change.
     this.setMode(aConfig.mode || SourceEditor.DEFAULTS.MODE);
 
@@ -156,39 +179,71 @@ SourceEditor.prototype = {
     this._editActionListener = new EditActionListener(this);
     this._editor.addEditActionListener(this._editActionListener);
 
     this._lineDelimiter = win.navigator.platform.indexOf("Win") > -1 ?
                           "\r\n" : "\n";
 
     this._config = aConfig;
 
+    for each (let key in DEFAULT_KEYBINDINGS) {
+      for (let prop in key) {
+        if (prop == "accelKey") {
+          let newProp = Services.appinfo.OS == "Darwin" ? "metaKey" : "ctrlKey";
+          key[newProp] = key[prop];
+          delete key[prop];
+          break;
+        }
+      }
+    }
+
+    this.ui.init();
+    this.ui.onReady();
+
     if (aCallback) {
       aCallback(this);
     }
   },
 
   /**
-   * The textbox keypress event handler allows users to indent code using the
-   * Tab key.
+   * The textbox keypress event handler calls the configured action for keyboard
+   * event.
    *
    * @private
    * @param nsIDOMEvent aEvent
    *        The DOM object for the event.
+   * @see DEFAULT_KEYBINDINGS
    */
   _onKeyPress: function SE__onKeyPress(aEvent)
   {
-    if (aEvent.keyCode != aEvent.DOM_VK_TAB || aEvent.shiftKey ||
-        aEvent.metaKey || aEvent.ctrlKey || aEvent.altKey) {
-      return;
+    for each (let key in DEFAULT_KEYBINDINGS) {
+      let matched = true;
+      for (let prop in key) {
+        if (prop.charAt(0) != "_" && aEvent[prop] !== key[prop]) {
+          matched = false;
+          break;
+        }
+      }
+      if (matched) {
+        let context = key._context ? this[key._context] : this;
+        context[key._action].call(context);
+        aEvent.preventDefault();
+        break;
+      }
     }
+  },
 
-    aEvent.preventDefault();
-
-    let caret = this.getCaretOffset();
+  /**
+   * The Tab keypress event handler. This allows the user to indent the code
+   * with spaces, when expandTab is true.
+   */
+  _doTab: function SE__doTab()
+  {
+    let selection = this.getSelection();
+    let caret = selection.start;
     let indent = "\t";
 
     if (this._expandTab) {
       let text = this._textbox.value;
       let lineStart = caret;
       while (lineStart > 0) {
         let c = text.charAt(lineStart - 1);
         if (c == "\r" || c == "\n") {
@@ -196,18 +251,18 @@ SourceEditor.prototype = {
         }
         lineStart--;
       }
       let offset = caret - lineStart;
       let spaces = this._tabSize - (offset % this._tabSize);
       indent = (new Array(spaces + 1)).join(" ");
     }
 
-    this.setText(indent, caret, caret);
-    this.setCaretOffset(caret + indent.length);
+    this.setText(indent, selection.start, selection.end);
+    this.setCaretOffset(selection.start + indent.length);
   },
 
   /**
    * The textbox keyup, click and select event handler tracks selection
    * changes. This method invokes the SourceEditor Selection event handlers.
    *
    * @see SourceEditor.EVENTS.SELECTION
    * @private
@@ -611,18 +666,18 @@ SourceEditor.prototype = {
    * @param number [aColumn=0]
    *        Optional. The new caret column location. Columns start from 0.
    */
   setCaretPosition: function SE_setCaretPosition(aLine, aColumn)
   {
     aColumn = aColumn || 0;
 
     let text = this._textbox.value;
-    let i = 0, n = text.length, c0, c1;
-    let line = 0, col = 0;
+    let i = -1, n = text.length, c0, c1;
+    let line = 0, col = -1;
     while (i < n) {
       c1 = text.charAt(i++);
       if (line < aLine && (c1 == "\r" || (c0 != "\r" && c1 == "\n"))) {
         // Count lines and reset the column only until we reach the desired line
         // such that if the desired column is out of boundaries we will stop
         // after the given number of characters from the line start.
         line++;
         col = 0;
@@ -704,24 +759,29 @@ SourceEditor.prototype = {
         if (aListener.domType) {
           aListener.target.removeEventListener(aListener.domType,
                                                aListener.handler, false);
         }
       }, this);
     }
 
     this._editor.removeEditActionListener(this._editActionListener);
+
+    this.ui.destroy();
+    this.ui = null;
+
     this.parentElement.removeChild(this._textbox);
     this.parentElement = null;
     this._editor = null;
     this._textbox = null;
     this._config = null;
     this._listeners = null;
     this._lastSelection = null;
     this._editActionListener = null;
+    this._lastFind = null;
   },
 };
 
 /**
  * The nsIEditActionListener for the nsIEditor of the xul:textbox used by the
  * SourceEditor. This listener traces text changes such that SourceEditor
  * TextChanged event handlers get their events.
  *
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/source-editor-ui.jsm
@@ -0,0 +1,288 @@
+/* vim:set ts=2 sw=2 sts=2 et tw=80:
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Source Editor component.
+ *
+ * The Initial Developer of the Original Code is
+ * The Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mihai Sucan <mihai.sucan@gmail.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 *****/
+
+"use strict";
+
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+
+var EXPORTED_SYMBOLS = ["SourceEditorUI"];
+
+/**
+ * The Source Editor component user interface.
+ */
+function SourceEditorUI(aEditor)
+{
+  this.editor = aEditor;
+}
+
+SourceEditorUI.prototype = {
+  /**
+   * Initialize the user interface. This is called by the SourceEditor.init()
+   * method.
+   */
+  init: function SEU_init()
+  {
+    this._ownerWindow = this.editor.parentElement.ownerDocument.defaultView;
+  },
+
+  /**
+   * The UI onReady function is executed once the Source Editor completes
+   * initialization and it is ready for usage. Currently this code sets up the
+   * nsIController.
+   */
+  onReady: function SEU_onReady()
+  {
+    if (this._ownerWindow.controllers) {
+      this._controller = new SourceEditorController(this.editor);
+      this._ownerWindow.controllers.insertControllerAt(0, this._controller);
+    }
+  },
+
+  /**
+   * The "go to line" command UI. This displays a prompt that allows the user to
+   * input the line number to jump to.
+   */
+  gotoLine: function SEU_gotoLine()
+  {
+    let oldLine = this.editor.getCaretPosition ?
+                  this.editor.getCaretPosition().line : null;
+    let newLine = {value: oldLine !== null ? oldLine + 1 : ""};
+
+    let result = Services.prompt.prompt(this._ownerWindow,
+      SourceEditorUI.strings.GetStringFromName("gotoLineCmd.promptTitle"),
+      SourceEditorUI.strings.GetStringFromName("gotoLineCmd.promptMessage"),
+      newLine, null, {});
+
+    newLine.value = parseInt(newLine.value);
+    if (result && !isNaN(newLine.value) && --newLine.value != oldLine) {
+      if (this.editor.getLineCount) {
+        let lines = this.editor.getLineCount() - 1;
+        this.editor.setCaretPosition(Math.max(0, Math.min(lines, newLine.value)));
+      } else {
+        this.editor.setCaretPosition(Math.max(0, newLine.value));
+      }
+    }
+
+    return true;
+  },
+
+  /**
+   * The "find" command UI. This displays a prompt that allows the user to input
+   * the string to search for in the code. By default the current selection is
+   * used as a search string, or the last search string.
+   */
+  find: function SEU_find()
+  {
+    let str = {value: this.editor.getSelectedText()};
+    if (!str.value && this.editor.lastFind) {
+      str.value = this.editor.lastFind.str;
+    }
+
+    let result = Services.prompt.prompt(this._ownerWindow,
+      SourceEditorUI.strings.GetStringFromName("findCmd.promptTitle"),
+      SourceEditorUI.strings.GetStringFromName("findCmd.promptMessage"),
+      str, null, {});
+
+    if (result && str.value) {
+      let start = this.editor.getSelection().end;
+      let pos = this.editor.find(str.value, {ignoreCase: true, start: start});
+      if (pos == -1) {
+        this.editor.find(str.value, {ignoreCase: true});
+      }
+      this._onFind();
+    }
+
+    return true;
+  },
+
+  /**
+   * Find the next occurrence of the last search string.
+   */
+  findNext: function SEU_findNext()
+  {
+    let lastFind = this.editor.lastFind;
+    if (lastFind) {
+      this.editor.findNext(true);
+      this._onFind();
+    }
+
+    return true;
+  },
+
+  /**
+   * Find the previous occurrence of the last search string.
+   */
+  findPrevious: function SEU_findPrevious()
+  {
+    let lastFind = this.editor.lastFind;
+    if (lastFind) {
+      this.editor.findPrevious(true);
+      this._onFind();
+    }
+
+    return true;
+  },
+
+  /**
+   * This executed after each find/findNext/findPrevious operation.
+   * @private
+   */
+  _onFind: function SEU__onFind()
+  {
+    let lastFind = this.editor.lastFind;
+    if (lastFind && lastFind.index > -1) {
+      this.editor.setSelection(lastFind.index, lastFind.index + lastFind.str.length);
+    }
+
+    if (this._ownerWindow.goUpdateCommand) {
+      this._ownerWindow.goUpdateCommand("cmd_findAgain");
+      this._ownerWindow.goUpdateCommand("cmd_findPrevious");
+    }
+  },
+
+  /**
+   * Destroy the SourceEditorUI instance. This is called by the
+   * SourceEditor.destroy() method.
+   */
+  destroy: function SEU_destroy()
+  {
+    this._ownerWindow = null;
+    this.editor = null;
+    this._controller = null;
+  },
+};
+
+/**
+ * The Source Editor nsIController implements features that need to be available
+ * from XUL commands.
+ *
+ * @constructor
+ * @param object aEditor
+ *        SourceEditor object instance for which the controller is instanced.
+ */
+function SourceEditorController(aEditor)
+{
+  this._editor = aEditor;
+}
+
+SourceEditorController.prototype = {
+  /**
+   * Check if a command is supported by the controller.
+   *
+   * @param string aCommand
+   *        The command name you want to check support for.
+   * @return boolean
+   *         True if the command is supported, false otherwise.
+   */
+  supportsCommand: function SEC_supportsCommand(aCommand)
+  {
+    let result;
+
+    switch (aCommand) {
+      case "cmd_find":
+      case "cmd_findAgain":
+      case "cmd_findPrevious":
+      case "cmd_gotoLine":
+        result = true;
+        break;
+      default:
+        result = false;
+        break;
+    }
+
+    return result;
+  },
+
+  /**
+   * Check if a command is enabled or not.
+   *
+   * @param string aCommand
+   *        The command name you want to check if it is enabled or not.
+   * @return boolean
+   *         True if the command is enabled, false otherwise.
+   */
+  isCommandEnabled: function SEC_isCommandEnabled(aCommand)
+  {
+    let result;
+
+    switch (aCommand) {
+      case "cmd_find":
+      case "cmd_gotoLine":
+        result = true;
+        break;
+      case "cmd_findAgain":
+      case "cmd_findPrevious":
+        result = this._editor.lastFind && this._editor.lastFind.lastFound != -1;
+        break;
+      default:
+        result = false;
+        break;
+    }
+
+    return result;
+  },
+
+  /**
+   * Perform a command.
+   *
+   * @param string aCommand
+   *        The command name you want to execute.
+   * @return void
+   */
+  doCommand: function SEC_doCommand(aCommand)
+  {
+    switch (aCommand) {
+      case "cmd_find":
+        this._editor.ui.find();
+        break;
+      case "cmd_findAgain":
+        this._editor.ui.findNext();
+        break;
+      case "cmd_findPrevious":
+        this._editor.ui.findPrevious();
+        break;
+      case "cmd_gotoLine":
+        this._editor.ui.gotoLine();
+        break;
+    }
+  },
+
+  onEvent: function() { }
+};
--- a/browser/devtools/sourceeditor/source-editor.jsm
+++ b/browser/devtools/sourceeditor/source-editor.jsm
@@ -36,22 +36,28 @@
  *
  * ***** END LICENSE BLOCK *****/
 
 "use strict";
 
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource:///modules/source-editor-ui.jsm");
 
 const PREF_EDITOR_COMPONENT = "devtools.editor.component";
+const SOURCEEDITOR_L10N = "chrome://browser/locale/devtools/sourceeditor.properties";
 
 var component = Services.prefs.getCharPref(PREF_EDITOR_COMPONENT);
 var obj = {};
 try {
+  if (component == "ui") {
+    throw new Error("The UI editor component is not available.");
+  }
   Cu.import("resource:///modules/source-editor-" + component + ".jsm", obj);
 } catch (ex) {
   Cu.reportError(ex);
   Cu.reportError("SourceEditor component failed to load: " + component);
 
   // If the component does not exist, clear the user pref back to the default.
   Services.prefs.clearUserPref(PREF_EDITOR_COMPONENT);
 
@@ -61,16 +67,20 @@ try {
 }
 
 // Export the SourceEditor.
 var SourceEditor = obj.SourceEditor;
 var EXPORTED_SYMBOLS = ["SourceEditor"];
 
 // Add the constants used by all SourceEditors.
 
+XPCOMUtils.defineLazyGetter(SourceEditorUI, "strings", function() {
+  return Services.strings.createBundle(SOURCEEDITOR_L10N);
+});
+
 /**
  * Known SourceEditor preferences.
  */
 SourceEditor.PREFS = {
   TAB_SIZE: "devtools.editor.tabsize",
   EXPAND_TAB: "devtools.editor.expandtab",
   COMPONENT: PREF_EDITOR_COMPONENT,
 };
@@ -137,8 +147,166 @@ SourceEditor.EVENTS = {
    * object properties:
    *   - oldValue - the old selection range.
    *   - newValue - the new selection range.
    * Both ranges are objects which hold two properties: start and end.
    */
   SELECTION: "Selection",
 };
 
+/**
+ * Extend a destination object with properties from a source object.
+ *
+ * @param object aDestination
+ * @param object aSource
+ */
+function extend(aDestination, aSource)
+{
+  for (let name in aSource) {
+    if (!aDestination.hasOwnProperty(name)) {
+      aDestination[name] = aSource[name];
+    }
+  }
+}
+
+/**
+ * Add methods common to all components.
+ */
+extend(SourceEditor.prototype, {
+  _lastFind: null,
+
+  /**
+   * Find a string in the editor.
+   *
+   * @param string aString
+   *        The string you want to search for. If |aString| is not given the
+   *        currently selected text is used.
+   * @param object [aOptions]
+   *        Optional find options:
+   *        - start: (integer) offset to start searching from. Default: 0 if
+   *        backwards is false. If backwards is true then start = text.length.
+   *        - ignoreCase: (boolean) tells if you want the search to be case
+   *        insensitive or not. Default: false.
+   *        - backwards: (boolean) tells if you want the search to go backwards
+   *        from the given |start| offset. Default: false.
+   * @return integer
+   *        The offset where the string was found.
+   */
+  find: function SE_find(aString, aOptions)
+  {
+    if (typeof(aString) != "string") {
+      return -1;
+    }
+
+    aOptions = aOptions || {};
+
+    let str = aOptions.ignoreCase ? aString.toLowerCase() : aString;
+
+    let text = this.getText();
+    if (aOptions.ignoreCase) {
+      text = text.toLowerCase();
+    }
+
+    let index = aOptions.backwards ?
+                text.lastIndexOf(str, aOptions.start) :
+                text.indexOf(str, aOptions.start);
+
+    let lastFoundIndex = index;
+    if (index == -1 && this.lastFind && this.lastFind.index > -1 &&
+        this.lastFind.str === aString &&
+        this.lastFind.ignoreCase === !!aOptions.ignoreCase) {
+      lastFoundIndex = this.lastFind.index;
+    }
+
+    this._lastFind = {
+      str: aString,
+      index: index,
+      lastFound: lastFoundIndex,
+      ignoreCase: !!aOptions.ignoreCase,
+    };
+
+    return index;
+  },
+
+  /**
+   * Find the next occurrence of the last search operation.
+   *
+   * @param boolean aWrap
+   *        Tells if you want to restart the search from the beginning of the
+   *        document if the string is not found.
+   * @return integer
+   *        The offset where the string was found.
+   */
+  findNext: function SE_findNext(aWrap)
+  {
+    if (!this.lastFind && this.lastFind.lastFound == -1) {
+      return -1;
+    }
+
+    let options = {
+      start: this.lastFind.lastFound + this.lastFind.str.length,
+      ignoreCase: this.lastFind.ignoreCase,
+    };
+
+    let index = this.find(this.lastFind.str, options);
+    if (index == -1 && aWrap) {
+      options.start = 0;
+      index = this.find(this.lastFind.str, options);
+    }
+
+    return index;
+  },
+
+  /**
+   * Find the previous occurrence of the last search operation.
+   *
+   * @param boolean aWrap
+   *        Tells if you want to restart the search from the end of the
+   *        document if the string is not found.
+   * @return integer
+   *        The offset where the string was found.
+   */
+  findPrevious: function SE_findPrevious(aWrap)
+  {
+    if (!this.lastFind && this.lastFind.lastFound == -1) {
+      return -1;
+    }
+
+    let options = {
+      start: this.lastFind.lastFound - this.lastFind.str.length,
+      ignoreCase: this.lastFind.ignoreCase,
+      backwards: true,
+    };
+
+    let index;
+    if (options.start > 0) {
+      index = this.find(this.lastFind.str, options);
+    } else {
+      index = this._lastFind.index = -1;
+    }
+
+    if (index == -1 && aWrap) {
+      options.start = this.getCharCount() - 1;
+      index = this.find(this.lastFind.str, options);
+    }
+
+    return index;
+  },
+});
+
+/**
+ * Retrieve the last find operation result. This object holds the following
+ * properties:
+ *   - str: the last search string.
+ *   - index: stores the result of the most recent find operation. This is the
+ *   index in the text where |str| was found or -1 otherwise.
+ *   - lastFound: tracks the index where |str| was last found, throughout
+ *   multiple find operations. This can be -1 if |str| was never found in the
+ *   document.
+ *   - ignoreCase: tells if the search was case insensitive or not.
+ * @type object
+ */
+Object.defineProperty(SourceEditor.prototype, "lastFind", {
+  get: function() { return this._lastFind; },
+  enumerable: true,
+  configurable: true,
+});
+
--- a/browser/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -48,12 +48,13 @@ include $(topsrcdir)/config/rules.mk
 		browser_sourceeditor_initialization.js \
 		browser_bug684862_paste_html.js \
 		browser_bug687573_vscroll.js \
 		browser_bug687568_pagescroll.js \
 		browser_bug687580_drag_and_drop.js \
 		browser_bug684546_reset_undo.js \
 		browser_bug695035_middle_click_paste.js \
 		browser_bug687160_line_api.js \
+		browser_bug650345_find.js \
 		head.js \
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_bug650345_find.js
@@ -0,0 +1,147 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+Cu.import("resource:///modules/source-editor.jsm");
+
+let testWin;
+let editor;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  const windowUrl = "data:text/xml,<?xml version='1.0'?>" +
+    "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
+    " title='test for bug 650345' width='600' height='500'><hbox flex='1'/></window>";
+  const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+  testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+  testWin.addEventListener("load", function onWindowLoad() {
+    testWin.removeEventListener("load", onWindowLoad, false);
+    waitForFocus(initEditor, testWin);
+  }, false);
+}
+
+function initEditor()
+{
+  let hbox = testWin.document.querySelector("hbox");
+  editor = new SourceEditor();
+  editor.init(hbox, {}, editorLoaded);
+}
+
+function editorLoaded()
+{
+  editor.focus();
+
+  let text = "foobar bug650345\nBug650345 bazbaz\nfoobar omg\ntest";
+  editor.setText(text);
+
+  let needle = "foobar";
+  is(editor.find(), -1, "find() works");
+  ok(!editor.lastFind, "no editor.lastFind yet");
+
+  is(editor.find(needle), 0, "find('" + needle + "') works");
+  is(editor.lastFind.str, needle, "lastFind.str is correct");
+  is(editor.lastFind.index, 0, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
+  is(editor.lastFind.ignoreCase, false, "lastFind.ignoreCase is correct");
+
+  let newIndex = text.indexOf(needle, needle.length);
+  is(editor.findNext(), newIndex, "findNext() works");
+  is(editor.lastFind.str, needle, "lastFind.str is correct");
+  is(editor.lastFind.index, newIndex, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
+  is(editor.lastFind.ignoreCase, false, "lastFind.ignoreCase is correct");
+
+  is(editor.findNext(), -1, "findNext() works again");
+  is(editor.lastFind.index, -1, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
+
+  is(editor.findPrevious(), 0, "findPrevious() works");
+  is(editor.lastFind.index, 0, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
+
+  is(editor.findPrevious(), -1, "findPrevious() works again");
+  is(editor.lastFind.index, -1, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
+
+  is(editor.findNext(), newIndex, "findNext() works");
+  is(editor.lastFind.index, newIndex, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
+
+  is(editor.findNext(true), 0, "findNext(true) works");
+  is(editor.lastFind.index, 0, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
+
+  is(editor.findNext(true), newIndex, "findNext(true) works again");
+  is(editor.lastFind.index, newIndex, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
+
+  is(editor.findPrevious(true), 0, "findPrevious(true) works");
+  is(editor.lastFind.index, 0, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, 0, "lastFind.lastFound is correct");
+
+  is(editor.findPrevious(true), newIndex, "findPrevious(true) works again");
+  is(editor.lastFind.index, newIndex, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
+
+  needle = "error";
+  is(editor.find(needle), -1, "find('" + needle + "') works");
+  is(editor.lastFind.str, needle, "lastFind.str is correct");
+  is(editor.lastFind.index, -1, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, -1, "lastFind.lastFound is correct");
+  is(editor.lastFind.ignoreCase, false, "lastFind.ignoreCase is correct");
+
+  is(editor.findNext(), -1, "findNext() works");
+  is(editor.lastFind.str, needle, "lastFind.str is correct");
+  is(editor.lastFind.index, -1, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, -1, "lastFind.lastFound is correct");
+  is(editor.findNext(true), -1, "findNext(true) works");
+
+  is(editor.findPrevious(), -1, "findPrevious() works");
+  is(editor.findPrevious(true), -1, "findPrevious(true) works");
+
+  needle = "bug650345";
+  newIndex = text.indexOf(needle);
+
+  is(editor.find(needle), newIndex, "find('" + needle + "') works");
+  is(editor.findNext(), -1, "findNext() works");
+  is(editor.findNext(true), newIndex, "findNext(true) works");
+  is(editor.findPrevious(), -1, "findPrevious() works");
+  is(editor.findPrevious(true), newIndex, "findPrevious(true) works");
+  is(editor.lastFind.index, newIndex, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, newIndex, "lastFind.lastFound is correct");
+
+  is(editor.find(needle, {ignoreCase: 1}), newIndex,
+     "find('" + needle + "', {ignoreCase: 1}) works");
+  is(editor.lastFind.ignoreCase, true, "lastFind.ignoreCase is correct");
+
+  let newIndex2 = text.toLowerCase().indexOf(needle, newIndex + needle.length);
+  is(editor.findNext(), newIndex2, "findNext() works");
+  is(editor.findNext(), -1, "findNext() works");
+  is(editor.lastFind.index, -1, "lastFind.index is correct");
+  is(editor.lastFind.lastFound, newIndex2, "lastFind.lastFound is correct");
+
+  is(editor.findNext(true), newIndex, "findNext(true) works");
+
+  is(editor.findPrevious(), -1, "findPrevious() works");
+  is(editor.findPrevious(true), newIndex2, "findPrevious(true) works");
+  is(editor.findPrevious(), newIndex, "findPrevious() works again");
+
+  needle = "foobar";
+  newIndex = text.indexOf(needle, 2);
+  is(editor.find(needle, {start: 2}), newIndex,
+     "find('" + needle + "', {start:2}) works");
+  is(editor.findNext(), -1, "findNext() works");
+  is(editor.findNext(true), 0, "findNext(true) works");
+
+  editor.destroy();
+
+  testWin.close();
+  testWin = editor = null;
+
+  waitForFocus(finish, window);
+}
--- a/browser/devtools/tilt/Tilt.jsm
+++ b/browser/devtools/tilt/Tilt.jsm
@@ -266,17 +266,17 @@ Tilt.prototype = {
   },
 
   /**
    * Returns true if this tool is enabled.
    */
   get enabled()
   {
     return (TiltVisualizer.Prefs.enabled &&
-           (TiltVisualizer.Prefs.forceEnabled || TiltGL.isWebGLSupported()));
+           (TiltGL.isWebGLForceEnabled() || TiltGL.isWebGLSupported()));
   },
 
   /**
    * Gets the ID of the current window object to identify the visualizer.
    */
   get currentWindowId()
   {
     let gBrowser = this.chromeWindow.gBrowser;
--- a/browser/devtools/tilt/TiltGL.jsm
+++ b/browser/devtools/tilt/TiltGL.jsm
@@ -32,25 +32,26 @@
  * use your version of this file under the terms of the MPL, indicate your
  * 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 *****/
 
-/*global Components, TiltMath, TiltUtils, mat4 */
+/*global Components, Services, TiltMath, TiltUtils, mat4 */
 "use strict";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 const WEBGL_CONTEXT_NAME = "experimental-webgl";
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource:///modules/devtools/TiltMath.jsm");
 Cu.import("resource:///modules/devtools/TiltUtils.jsm");
 
 let EXPORTED_SYMBOLS = ["TiltGL"];
 
 /**
  * Module containing thin wrappers around low-level WebGL functions.
  */
@@ -1549,16 +1550,21 @@ TiltGL.ColorShader = {
     "uniform vec4 fill;",
 
     "void main() {",
     "    gl_FragColor = fill;",
     "}"
   ].join("\n")
 };
 
+TiltGL.isWebGLForceEnabled = function TGL_isWebGLForceEnabled()
+{
+  return Services.prefs.getBoolPref("webgl.force-enabled");
+};
+
 /**
  * Tests if the WebGL OpenGL or Angle renderer is available using the
  * GfxInfo service.
  *
  * @return {Boolean} true if WebGL is available
  */
 TiltGL.isWebGLSupported = function TGL_isWebGLSupported()
 {
--- a/browser/devtools/tilt/TiltVisualizer.jsm
+++ b/browser/devtools/tilt/TiltVisualizer.jsm
@@ -1544,38 +1544,23 @@ TiltVisualizer.Prefs = {
 
   set enabled(value)
   {
     TiltUtils.Preferences.set("enabled", "boolean", value);
     this._enabled = value;
   },
 
   /**
-   * Specifies if Tilt should be enabled even if WebGL may not be available.
-   */
-  get forceEnabled()
-  {
-    return this._forceEnabled;
-  },
-
-  set forceEnabled(value)
-  {
-    TiltUtils.Preferences.set("force-enabled", "boolean", value);
-    this._forceEnabled = value;
-  },
-
-  /**
    * Loads the preferences.
    */
   load: function TVC_load()
   {
     let prefs = TiltUtils.Preferences;
 
     TiltVisualizer.Prefs._enabled = prefs.get("enabled", "boolean");
-    TiltVisualizer.Prefs._forceEnabled = prefs.get("force-enabled", "boolean");
   }
 };
 
 /**
  * A custom visualization shader.
  *
  * @param {Attribute} vertexPosition: the vertex position
  * @param {Attribute} vertexTexCoord: texture coordinates used by the sampler
--- a/browser/devtools/tilt/test/Makefile.in
+++ b/browser/devtools/tilt/test/Makefile.in
@@ -46,16 +46,17 @@ include $(topsrcdir)/config/rules.mk
 
 _BROWSER_TEST_FILES = \
 	head.js \
 	browser_tilt_01_lazy_getter.js \
 	browser_tilt_02_notifications.js \
 	browser_tilt_03_tab_switch.js \
 	browser_tilt_04_initialization.js \
 	browser_tilt_05_destruction-esc.js \
+	browser_tilt_05_destruction-url.js \
 	browser_tilt_05_destruction.js \
 	browser_tilt_arcball.js \
 	browser_tilt_controller.js \
 	browser_tilt_gl01.js \
 	browser_tilt_gl02.js \
 	browser_tilt_gl03.js \
 	browser_tilt_gl04.js \
 	browser_tilt_gl05.js \
--- a/browser/devtools/tilt/test/browser_tilt_04_initialization.js
+++ b/browser/devtools/tilt/test/browser_tilt_04_initialization.js
@@ -26,21 +26,16 @@ function test() {
       "The unique window identifiers should match for the same window.");
 
     createTilt({
       onInspectorOpen: function() {
         initialActiveElement = document.activeElement;
 
         is(Tilt.visualizers[id], null,
           "A instance of the visualizer shouldn't be initialized yet.");
-
-        is(typeof TiltVisualizer.Prefs.enabled, "boolean",
-          "The 'enabled' pref should have been loaded by now.");
-        is(typeof TiltVisualizer.Prefs.forceEnabled, "boolean",
-          "The 'force-enabled' pref should have been loaded by now.");
       },
       onTiltOpen: function(instance)
       {
         is(document.activeElement, instance.presenter.canvas,
           "The visualizer canvas should be focused on initialization.");
 
         ok(Tilt.visualizers[id] instanceof TiltVisualizer,
           "A new instance of the visualizer wasn't created properly.");
new file mode 100644
--- /dev/null
+++ b/browser/devtools/tilt/test/browser_tilt_05_destruction-url.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/*global ok, is, info, waitForExplicitFinish, finish, gBrowser */
+/*global isTiltEnabled, isWebGLSupported, createTab, createTilt */
+/*global Services, EventUtils, Tilt, TiltUtils, InspectorUI, TILT_DESTROYED */
+"use strict";
+
+function test() {
+  if (!isTiltEnabled()) {
+    info("Skipping destruction test because Tilt isn't enabled.");
+    return;
+  }
+  if (!isWebGLSupported()) {
+    info("Skipping destruction test because WebGL isn't supported.");
+    return;
+  }
+
+  waitForExplicitFinish();
+
+  createTab(function() {
+    createTilt({
+      onTiltOpen: function()
+      {
+        Services.obs.addObserver(cleanup, TILT_DESTROYED, false);
+        window.content.location = "about:mozilla";
+      }
+    });
+  });
+}
+
+function cleanup() {
+  let id = TiltUtils.getWindowId(gBrowser.selectedBrowser.contentWindow);
+
+  is(Tilt.visualizers[id], null,
+    "The current instance of the visualizer wasn't destroyed properly.");
+
+  Services.obs.removeObserver(cleanup, TILT_DESTROYED);
+  gBrowser.removeCurrentTab();
+  finish();
+}
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_bug_653531_highlighter_console_helper.js
@@ -87,29 +87,27 @@ function setupHighlighterTests()
 }
 
 function runSelectionTests()
 {
   Services.obs.removeObserver(runSelectionTests,
     InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
 
   executeSoon(function() {
-    Services.obs.addObserver(performTestComparisons,
-      InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+    InspectorUI.highlighter.addListener("nodeselected", performTestComparisons);
     EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content);
   });
 }
 
 function performTestComparisons(evt)
 {
-  Services.obs.removeObserver(performTestComparisons,
-    InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
+  InspectorUI.highlighter.removeListener("nodeselected", performTestComparisons);
 
   InspectorUI.stopInspecting();
-  ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
+  is(InspectorUI.highlighter.node, h1, "node selected");
   is(InspectorUI.selection, h1, "selection matches node");
 
   HUDService.activateHUDForContext(gBrowser.selectedTab);
   let hudId = HUDService.getHudIdByWindow(content);
   let hud = HUDService.hudReferences[hudId];
   let jsterm = hud.jsterm;
   outputNode = hud.outputNode;
 
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -141,16 +141,17 @@
 @BINPATH@/components/dom_system_b2g.xpt
 #endif
 @BINPATH@/components/dom_battery.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_geolocation.xpt
+@BINPATH@/components/dom_network.xpt
 @BINPATH@/components/dom_notification.xpt
 @BINPATH@/components/dom_html.xpt
 @BINPATH@/components/dom_indexeddb.xpt
 @BINPATH@/components/dom_offline.xpt
 @BINPATH@/components/dom_json.xpt
 @BINPATH@/components/dom_range.xpt
 @BINPATH@/components/dom_sidebar.xpt
 @BINPATH@/components/dom_sms.xpt
--- a/browser/installer/windows/nsis/maintenanceservice_installer.nsi
+++ b/browser/installer/windows/nsis/maintenanceservice_installer.nsi
@@ -116,19 +116,18 @@ ShowUnInstDetails nevershow
 !define MUI_UNICON setup.ico
 !define MUI_WELCOMEPAGE_TITLE_3LINES
 !define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
 
 ;Interface Settings
 !define MUI_ABORTWARNING
 
 ; Uninstaller Pages
-!insertmacro MUI_UNPAGE_WELCOME
+!insertmacro MUI_UNPAGE_CONFIRM
 !insertmacro MUI_UNPAGE_INSTFILES
-!insertmacro MUI_UNPAGE_FINISH
 
 ################################################################################
 # Language
 
 !insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
 !verbose push
 !verbose 3
 !include "overrideLocale.nsh"
@@ -252,16 +251,18 @@ FunctionEnd
 Section "Uninstall"
   ; Delete the service so that no updates will be attempted
   nsExec::Exec '"$INSTDIR\maintenanceservice.exe" uninstall'
 
   Push "$INSTDIR\maintenanceservice.exe"
   Call un.RenameDelete
   Push "$INSTDIR\maintenanceservice_tmp.exe"
   Call un.RenameDelete
+  Push "$INSTDIR\maintenanceservice.old"
+  Call un.RenameDelete
   Push "$INSTDIR\Uninstall.exe"
   Call un.RenameDelete
   RMDir /REBOOTOK "$INSTDIR"
 
   DeleteRegKey HKLM "${MaintUninstallKey}"
 
   SetRegView 64
   DeleteRegValue HKLM "Software\Mozilla\MaintenanceService" "Installed"
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -212,18 +212,23 @@ can reach it easily. -->
 <!ENTITY scratchpad.accesskey         "s">
 <!ENTITY scratchpad.keycode           "VK_F4">
 <!ENTITY scratchpad.keytext           "F4">
 
 <!ENTITY inspectPanelTitle.label        "HTML">
 <!ENTITY inspectButton.label            "Inspect">
 <!ENTITY inspectButton.accesskey        "I">
 <!ENTITY inspectCloseButton.tooltiptext "Close Inspector">
-<!ENTITY inspect3DButton.label        "3D">
-<!ENTITY inspect3DButton.accesskey    "M">
+
+<!-- LOCALIZATION NOTE (inspect3DViewButton.label): This button shows an
+  -  alternate view for the Inspector, creating a 3D visualization of the
+  -  webpage. -->
+<!ENTITY inspect3DViewButton.label     "3D View">
+<!ENTITY inspect3DViewButton.accesskey "W">
+
 <!ENTITY inspectStyleButton.label     "Style">
 <!ENTITY inspectStyleButton.accesskey "S">
 
 <!-- LOCALIZATION NOTE (styleeditor.label): This menu item label appears
   -  in the Tools menu. -->
 <!ENTITY styleeditor.label            "Style Editor">
 <!ENTITY styleeditor.accesskey        "y">
 <!ENTITY styleeditor.keycode          "VK_F7">
--- a/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/scratchpad.dtd
@@ -62,16 +62,36 @@
 <!ENTITY pasteCmd.label               "Paste">
 <!ENTITY pasteCmd.key                 "V">
 <!ENTITY pasteCmd.accesskey           "P">
 
 <!ENTITY selectAllCmd.label           "Select All">
 <!ENTITY selectAllCmd.key             "A">
 <!ENTITY selectAllCmd.accesskey       "A">
 
+<!ENTITY findCmd.label                "Find…">
+<!ENTITY findCmd.key                  "F">
+<!ENTITY findCmd.accesskey            "F">
+
+<!ENTITY findAgainCmd.label           "Find Again…">
+<!-- LOCALIZATION NOTE (findAgainCmd.key): This key is used only on Macs.
+  -  Windows and Linux builds use the F3 key which is not localizable on purpose.
+  -->
+<!ENTITY findAgainCmd.key             "G">
+<!ENTITY findAgainCmd.accesskey       "g">
+<!-- LOCALIZATION NOTE (findPreviousCmd.key): This key is used only on Macs.
+  -  Windows and Linux builds use the Shift-F3 key which is not localizable on
+  -  purpose.
+  -->
+<!ENTITY findPreviousCmd.key          "G">
+
+<!ENTITY gotoLineCmd.label            "Jump to line…">
+<!ENTITY gotoLineCmd.key              "J">
+<!ENTITY gotoLineCmd.accesskey        "J">
+
 <!ENTITY run.label                    "Run">
 <!ENTITY run.accesskey                "R">
 <!ENTITY run.key                      "r">
 
 <!ENTITY inspect.label                "Inspect">
 <!ENTITY inspect.accesskey            "I">
 <!ENTITY inspect.key                  "i">
 
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/devtools/sourceeditor.properties
@@ -0,0 +1,30 @@
+# LOCALIZATION NOTE These strings are used inside the Source Editor component.
+# This component is used whenever source code is displayed for the purpose of
+# being edited, inside the Firefox developer tools - current examples are the
+# Scratchpad and the Style Editor tools.
+
+# LOCALIZATION NOTE The correct localization of this file might be to keep it
+# in English, or another language commonly spoken among web developers.
+# You want to make that choice consistent across the developer tools.
+# A good criteria is the language in which you'd find the best documentation
+# on web development on the web.
+
+# LOCALIZATION NOTE  (findCmd.promptTitle): This is the dialog title used
+# when the user wants to search for a string in the code. You can
+# access this feature by pressing Ctrl-F on Windows/Linux or Cmd-F on Mac.
+findCmd.promptTitle=Find…
+
+# LOCALIZATION NOTE  (gotoLineCmd.promptMessage): This is the message shown when
+# the user wants to search for a string in the code. You can
+# access this feature by pressing Ctrl-F on Windows/Linux or Cmd-F on Mac.
+findCmd.promptMessage=Search for:
+
+# LOCALIZATION NOTE  (gotoLineCmd.promptTitle): This is the dialog title used
+# when the user wants to jump to a specific line number in the code. You can
+# access this feature by pressing Ctrl-J on Windows/Linux or Cmd-J on Mac.
+gotoLineCmd.promptTitle=Go to line…
+
+# LOCALIZATION NOTE  (gotoLineCmd.promptMessage): This is the message shown when
+# the user wants to jump to a specific line number in the code. You can
+# access this feature by pressing Ctrl-J on Windows/Linux or Cmd-J on Mac.
+gotoLineCmd.promptMessage=Jump to line number:
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -22,16 +22,17 @@
     locale/browser/devtools/tilt.properties           (%chrome/browser/devtools/tilt.properties)
     locale/browser/devtools/scratchpad.properties     (%chrome/browser/devtools/scratchpad.properties)
     locale/browser/devtools/scratchpad.dtd            (%chrome/browser/devtools/scratchpad.dtd)
     locale/browser/devtools/styleeditor.properties    (%chrome/browser/devtools/styleeditor.properties)
     locale/browser/devtools/styleeditor.dtd           (%chrome/browser/devtools/styleeditor.dtd)
     locale/browser/devtools/styleinspector.properties (%chrome/browser/devtools/styleinspector.properties)
     locale/browser/devtools/styleinspector.dtd        (%chrome/browser/devtools/styleinspector.dtd)
     locale/browser/devtools/webConsole.dtd            (%chrome/browser/devtools/webConsole.dtd)
+    locale/browser/devtools/sourceeditor.properties   (%chrome/browser/devtools/sourceeditor.properties)
     locale/browser/openLocation.dtd                (%chrome/browser/openLocation.dtd)
     locale/browser/openLocation.properties         (%chrome/browser/openLocation.properties)
 *   locale/browser/pageInfo.dtd                    (%chrome/browser/pageInfo.dtd)
     locale/browser/pageInfo.properties             (%chrome/browser/pageInfo.properties)
     locale/browser/quitDialog.properties           (%chrome/browser/quitDialog.properties)
 *   locale/browser/safeMode.dtd                    (%chrome/browser/safeMode.dtd)
     locale/browser/sanitize.dtd                    (%chrome/browser/sanitize.dtd)
     locale/browser/search.properties               (%chrome/browser/search.properties)
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -1697,22 +1697,20 @@ toolbarbutton.chevron > .toolbarbutton-m
 .tab-throbber[progress] {
   list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
 }
 
 .tabbrowser-tab:not(:hover) > .tab-stack > .tab-content > .tab-icon-image:not([selected="true"]) {
   opacity: .8;
 }
 
-/* Prevent overlapping of tabs during the close animation */
-.tab-label:not([fadein]):not([pinned]) {
-  margin-left: -16px;
-  margin-right: -16px;
-  -moz-transition: opacity 100ms ease-out,
-                   margin 30ms ease-out 80ms;
+.tabbrowser-tab:not([pinned]):not([fadein]) {
+  -moz-transition: min-width 200ms ease-out /* copied from browser/base/content/browser.css */,
+                   max-width 250ms ease-out /* copied from browser/base/content/browser.css */,
+                   opacity 50ms ease-out 100ms /* hide the tab for the last 100ms of the max-width transition */;
 }
 
 .tab-stack {
   /* ensure stable tab height with and without toolbarbuttons on the tab bar */
   height: 26px;
 }
 
 .tabbrowser-tab,
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -141,16 +141,17 @@ MOZ_INSTALLER	= @MOZ_INSTALLER@
 MOZ_MAINTENANCE_SERVICE	= @MOZ_MAINTENANCE_SERVICE@
 MOZ_UPDATER	= @MOZ_UPDATER@
 MOZ_UPDATE_CHANNEL	= @MOZ_UPDATE_CHANNEL@
 MOZ_UPDATE_PACKAGING	= @MOZ_UPDATE_PACKAGING@
 MOZ_DISABLE_PARENTAL_CONTROLS = @MOZ_DISABLE_PARENTAL_CONTROLS@
 NS_ENABLE_TSF = @NS_ENABLE_TSF@
 MOZ_SPELLCHECK = @MOZ_SPELLCHECK@
 MOZ_ANDROID_HISTORY = @MOZ_ANDROID_HISTORY@
+MOZ_WEBSMS_BACKEND = @MOZ_WEBSMS_BACKEND@
 MOZ_JAVA_COMPOSITOR = @MOZ_JAVA_COMPOSITOR@
 MOZ_PROFILELOCKING = @MOZ_PROFILELOCKING@
 MOZ_FEEDS = @MOZ_FEEDS@
 MOZ_TOOLKIT_SEARCH = @MOZ_TOOLKIT_SEARCH@
 MOZ_PLACES = @MOZ_PLACES@
 MOZ_SAFE_BROWSING = @MOZ_SAFE_BROWSING@
 MOZ_URL_CLASSIFIER = @MOZ_URL_CLASSIFIER@
 MOZ_ZIPWRITER = @MOZ_ZIPWRITER@
--- a/configure.in
+++ b/configure.in
@@ -4624,16 +4624,17 @@ MOZ_DISABLE_DOMCRYPTO=
 NSS_DISABLE_DBM=
 NECKO_WIFI=1
 NECKO_COOKIES=1
 NECKO_PROTOCOLS_DEFAULT="about data file ftp http res viewsource websocket wyciwyg device"
 USE_ARM_KUSER=
 BUILD_CTYPES=1
 MOZ_USE_NATIVE_POPUP_WINDOWS=
 MOZ_ANDROID_HISTORY=
+MOZ_WEBSMS_BACKEND=1
 MOZ_GRAPHITE=1
 
 case "${target}" in
 *darwin*)
     ACCESSIBILITY=
     ;;
 *)
     ACCESSIBILITY=1
@@ -5460,16 +5461,28 @@ AC_SUBST(MOZ_DBUS_GLIB_LIBS)
 dnl ========================================================
 dnl = Enable Android History instead of Places
 dnl ========================================================
 if test -n "$MOZ_ANDROID_HISTORY"; then
      dnl Do this if defined in confvars.sh
      AC_DEFINE(MOZ_ANDROID_HISTORY)
 fi
 
+dnl ========================================================
+dnl = Disable WebSMS backend
+dnl ========================================================
+MOZ_ARG_DISABLE_BOOL(websms-backend,
+[  --disable-websms-backend
+                           Disable WebSMS backend],
+    MOZ_WEBSMS_BACKEND=,
+    MOZ_WEBSMS_BACKEND=1)
+
+if test $MOZ_WEBSMS_BACKEND -eq 1; then
+    AC_DEFINE(MOZ_WEBSMS_BACKEND)
+fi
 
 dnl ========================================================
 dnl = Build Personal Security Manager
 dnl ========================================================
 MOZ_ARG_DISABLE_BOOL(crypto,
 [  --disable-crypto        Disable crypto support (Personal Security Manager)],
     MOZ_PSM=,
     MOZ_PSM=1 )
@@ -8440,16 +8453,17 @@ AC_SUBST(MOZ_DIRECTX_SDK_PATH)
 AC_SUBST(MOZ_DIRECTX_SDK_CPU_SUFFIX)
 AC_SUBST(MOZ_D3DX9_VERSION)
 AC_SUBST(MOZ_D3DX9_CAB)
 AC_SUBST(MOZ_D3DCOMPILER_CAB)
 AC_SUBST(MOZ_D3DX9_DLL)
 AC_SUBST(MOZ_D3DCOMPILER_DLL)
 
 AC_SUBST(MOZ_ANDROID_HISTORY)
+AC_SUBST(MOZ_WEBSMS_BACKEND)
 AC_SUBST(ENABLE_STRIP)
 AC_SUBST(PKG_SKIP_STRIP)
 AC_SUBST(USE_ELF_DYNSTR_GC)
 AC_SUBST(USE_ELF_HACK)
 AC_SUBST(INCREMENTAL_LINKER)
 AC_SUBST(MOZ_COMPONENTS_VERSION_SCRIPT_LDFLAGS)
 AC_SUBST(MOZ_COMPONENT_NSPR_LIBS)
 
--- a/content/canvas/src/Makefile.in
+++ b/content/canvas/src/Makefile.in
@@ -35,16 +35,18 @@
 #
 # ***** END LICENSE BLOCK *****
 
 DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
+FAIL_ON_WARNINGS = 1
+
 include $(DEPTH)/config/autoconf.mk
 
 MODULE		= content
 LIBRARY_NAME	= gkconcvs_s
 LIBXUL_LIBRARY  = 1
 
 EXPORTS = \
 	CustomQS_Canvas2D.h \
@@ -55,18 +57,16 @@ CPPSRCS	= \
 	CanvasImageCache.cpp \
 	CanvasUtils.cpp \
 	nsCanvasRenderingContext2D.cpp \
 	nsCanvasRenderingContext2DAzure.cpp \
 	DocumentRendererParent.cpp \
 	DocumentRendererChild.cpp \
 	$(NULL)
 
-# Canvas 3D Pieces
-
 ifdef MOZ_WEBGL
 
 CPPSRCS += \
 	WebGLContext.cpp \
 	WebGLContextGL.cpp \
 	WebGLContextUtils.cpp \
 	WebGLContextValidate.cpp \
 	WebGLExtensionStandardDerivatives.cpp \
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -496,19 +496,19 @@ WebGLContext::SetDimensions(PRInt32 widt
     // know if creating the new context will succeed.
     DestroyResourcesAndContext();
 
     // Get some prefs for some preferred/overriden things
     NS_ENSURE_TRUE(Preferences::GetRootBranch(), NS_ERROR_FAILURE);
 
     bool forceOSMesa =
         Preferences::GetBool("webgl.force_osmesa", false);
+#ifdef XP_WIN
     bool preferEGL =
         Preferences::GetBool("webgl.prefer-egl", false);
-#ifdef XP_WIN
     bool preferOpenGL =
         Preferences::GetBool("webgl.prefer-native-gl", false);
 #endif
     bool forceEnabled =
         Preferences::GetBool("webgl.force-enabled", false);
     bool disabled =
         Preferences::GetBool("webgl.disabled", false);
     bool verbose =
@@ -561,43 +561,52 @@ WebGLContext::SetDimensions(PRInt32 widt
         gfxInfo &&
         NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_MSAA, &status))) {
         if (status == nsIGfxInfo::FEATURE_NO_INFO || forceMSAA) {
             PRUint32 msaaLevel = Preferences::GetUint("webgl.msaa-level", 2);
             format.samples = msaaLevel*msaaLevel;
         }
     }
 
+#ifdef XP_WIN
     if (PR_GetEnv("MOZ_WEBGL_PREFER_EGL")) {
         preferEGL = true;
     }
+#endif
 
     // Ask GfxInfo about what we should use
     bool useOpenGL = true;
+
+#ifdef XP_WIN
     bool useANGLE = true;
+#endif
 
     if (gfxInfo && !forceEnabled) {
         if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_OPENGL, &status))) {
             if (status != nsIGfxInfo::FEATURE_NO_INFO) {
                 useOpenGL = false;
             }
         }
+#ifdef XP_WIN
         if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_WEBGL_ANGLE, &status))) {
             if (status != nsIGfxInfo::FEATURE_NO_INFO) {
                 useANGLE = false;
             }
         }
+#endif
     }
 
+#ifdef XP_WIN
     // allow forcing GL and not EGL/ANGLE
     if (PR_GetEnv("MOZ_WEBGL_FORCE_OPENGL")) {
         preferEGL = false;
         useANGLE = false;
         useOpenGL = true;
     }
+#endif
 
     // if we're forcing osmesa, do it first
     if (forceOSMesa) {
         gl = gl::GLContextProviderOSMesa::CreateOffscreen(gfxIntSize(width, height), format);
         if (!gl || !InitAndValidateGL()) {
             LogMessage("OSMesa forced, but creating context failed -- aborting!");
             return NS_ERROR_FAILURE;
         }
@@ -609,45 +618,42 @@ WebGLContext::SetDimensions(PRInt32 widt
     if (!gl && (preferEGL || useANGLE) && !preferOpenGL) {
         gl = gl::GLContextProviderEGL::CreateOffscreen(gfxIntSize(width, height), format);
         if (gl) {
             if (InitAndValidateGL()) {
                 if (useANGLE) {
                     gl->SetFlushGuaranteesResolve(true);
                 }
             } else {
-                gl = nsnull;
+                LogMessage("Error during ANGLE OpenGL ES initialization");
+                return NS_ERROR_FAILURE;
             }
         }
     }
-
-    // if it failed, then try the default provider, whatever that is
-    if (!gl && useOpenGL) {
-        gl = gl::GLContextProvider::CreateOffscreen(gfxIntSize(width, height), format);
-        if (gl && !InitAndValidateGL()) {
-            gl = nsnull;
-        }
-    }
-#else
-    // other platforms just use whatever the default is
-    if (!gl && useOpenGL) {
-        gl = gl::GLContextProvider::CreateOffscreen(gfxIntSize(width, height), format);
-        if (gl && !InitAndValidateGL()) {
-            gl = nsnull;
-        }
-    }
 #endif
 
+    // try the default provider, whatever that is
+    if (!gl && useOpenGL) {
+        gl = gl::GLContextProvider::CreateOffscreen(gfxIntSize(width, height), format);
+        if (gl && !InitAndValidateGL()) {
+            LogMessage("Error during OpenGL initialization");
+            return NS_ERROR_FAILURE;
+        }
+    }
+
     // finally, try OSMesa
     if (!gl) {
         gl = gl::GLContextProviderOSMesa::CreateOffscreen(gfxIntSize(width, height), format);
-        if (!gl || !InitAndValidateGL()) {
-            gl = nsnull;
-        } else {
-            LogMessage("Using software rendering via OSMesa (THIS WILL BE SLOW)");
+        if (gl) {
+            if (!InitAndValidateGL()) {
+                LogMessage("Error during OSMesa initialization");
+                return NS_ERROR_FAILURE;
+            } else {
+                LogMessage("Using software rendering via OSMesa (THIS WILL BE SLOW)");
+            }
         }
     }
 
     if (!gl) {
         LogMessage("Can't get a usable WebGL context");
         return NS_ERROR_FAILURE;
     }
 
@@ -1013,20 +1019,22 @@ void
 WebGLContext::ForceClearFramebufferWithDefaultValues(PRUint32 mask, const nsIntRect& viewportRect)
 {
     MakeContextCurrent();
 
     bool initializeColorBuffer = 0 != (mask & LOCAL_GL_COLOR_BUFFER_BIT);
     bool initializeDepthBuffer = 0 != (mask & LOCAL_GL_DEPTH_BUFFER_BIT);
     bool initializeStencilBuffer = 0 != (mask & LOCAL_GL_STENCIL_BUFFER_BIT);
 
+    // fun GL fact: no need to worry about the viewport here, glViewport is just setting up a coordinates transformation,
+    // it doesn't affect glClear at all
+
     // prepare GL state for clearing
     gl->fDisable(LOCAL_GL_SCISSOR_TEST);
     gl->fDisable(LOCAL_GL_DITHER);
-    gl->PushViewportRect(viewportRect);
 
     if (initializeColorBuffer) {
         gl->fColorMask(1, 1, 1, 1);
         gl->fClearColor(0.f, 0.f, 0.f, 0.f);
     }
 
     if (initializeDepthBuffer) {
         gl->fDepthMask(1);
@@ -1059,18 +1067,16 @@ WebGLContext::ForceClearFramebufferWithD
     }
 
     if (initializeStencilBuffer) {
         gl->fStencilMaskSeparate(LOCAL_GL_FRONT, mStencilWriteMaskFront);
         gl->fStencilMaskSeparate(LOCAL_GL_BACK, mStencilWriteMaskBack);
         gl->fClearStencil(mStencilClearValue);
     }
 
-    gl->PopViewportRect();
-
     if (mDitherEnabled)
         gl->fEnable(LOCAL_GL_DITHER);
     else
         gl->fDisable(LOCAL_GL_DITHER);
 
     if (mScissorTestEnabled)
         gl->fEnable(LOCAL_GL_SCISSOR_TEST);
     else
--- a/content/canvas/src/WebGLContextGL.cpp
+++ b/content/canvas/src/WebGLContextGL.cpp
@@ -1444,17 +1444,17 @@ WebGLContext::UndoFakeVertexAttrib0()
         return;
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mAttribBuffers[0].buf ? mAttribBuffers[0].buf->GLName() : 0);
     gl->fVertexAttribPointer(0,
                              mAttribBuffers[0].size,
                              mAttribBuffers[0].type,
                              mAttribBuffers[0].normalized,
                              mAttribBuffers[0].stride,
-                             (const GLvoid *) mAttribBuffers[0].byteOffset);
+                             reinterpret_cast<const GLvoid *>(mAttribBuffers[0].byteOffset));
 
     gl->fBindBuffer(LOCAL_GL_ARRAY_BUFFER, mBoundArrayBuffer ? mBoundArrayBuffer->GLName() : 0);
 }
 
 bool
 WebGLContext::NeedFakeBlack()
 {
     // handle this case first, it's the generic case
@@ -1703,17 +1703,17 @@ WebGLContext::DrawElements(WebGLenum mod
         EnsureBackbufferClearedAsNeeded();
     }
 
     BindFakeBlackTextures();
     if (!DoFakeVertexAttrib0(checked_maxIndexPlusOne.value()))
         return NS_OK;
 
     SetupRobustnessTimer();
-    gl->fDrawElements(mode, count, type, (GLvoid*) (byteOffset));
+    gl->fDrawElements(mode, count, type, reinterpret_cast<GLvoid*>(byteOffset));
 
     UndoFakeVertexAttrib0();
     UnbindFakeBlackTextures();
 
     mBackbufferClearingStatus = BackbufferClearingStatus::HasBeenDrawnTo;
     Invalidate();
 
     return NS_OK;
@@ -4695,17 +4695,17 @@ WebGLContext::VertexAttribPointer(WebGLu
     vd.byteOffset = byteOffset;
     vd.type = type;
     vd.normalized = normalized;
 
     MakeContextCurrent();
 
     gl->fVertexAttribPointer(index, size, type, normalized,
                              stride,
-                             (void*) (byteOffset));
+                             reinterpret_cast<void*>(byteOffset));
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
 WebGLContext::TexImage2D(PRInt32)
 {
     if (!IsContextStable())
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -1160,18 +1160,20 @@ nsCanvasRenderingContext2DAzure::Redraw(
 
   mIsEntireFrameInvalid = true;
 
   if (!mCanvasElement) {
     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
     return NS_OK;
   }
 
-  if (mThebesSurface)
-      mThebesSurface->MarkDirty();
+  if (!mThebesSurface)
+    mThebesSurface =
+      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+  mThebesSurface->MarkDirty();
 
   nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
 
   HTMLCanvasElement()->InvalidateCanvasContent(nsnull);
 
   return NS_OK;
 }
 
@@ -1190,18 +1192,20 @@ nsCanvasRenderingContext2DAzure::Redraw(
     return;
   }
 
   if (!mCanvasElement) {
     NS_ASSERTION(mDocShell, "Redraw with no canvas element or docshell!");
     return;
   }
 
-  if (mThebesSurface)
-      mThebesSurface->MarkDirty();
+  if (!mThebesSurface)
+    mThebesSurface =
+      gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(mTarget);
+  mThebesSurface->MarkDirty();
 
   nsSVGEffects::InvalidateDirectRenderingObservers(HTMLCanvasElement());
 
   gfxRect tmpR = ThebesRect(r);
   HTMLCanvasElement()->InvalidateCanvasContent(&tmpR);
 
   return;
 }
@@ -3076,18 +3080,16 @@ struct NS_STACK_CLASS nsCanvasBidiProces
       if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_FILL) {
         nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
           FillGlyphs(scaledFont, buffer,
                       nsCanvasRenderingContext2DAzure::GeneralPattern().
                         ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_FILL, mCtx->mTarget),
                       DrawOptions(mState->globalAlpha, mCtx->UsedOperation()));
       } else if (mOp == nsCanvasRenderingContext2DAzure::TEXT_DRAW_OPERATION_STROKE) {
         RefPtr<Path> path = scaledFont->GetPathForGlyphs(buffer, mCtx->mTarget);
-            
-        Matrix oldTransform = mCtx->mTarget->GetTransform();
 
         const ContextState& state = *mState;
         nsCanvasRenderingContext2DAzure::AdjustedTarget(mCtx)->
           Stroke(path, nsCanvasRenderingContext2DAzure::GeneralPattern().
                     ForStyle(mCtx, nsCanvasRenderingContext2DAzure::STYLE_STROKE, mCtx->mTarget),
                   StrokeOptions(state.lineWidth, state.lineJoin,
                                 state.lineCap, state.miterLimit,
                                 state.dash.Length(),
--- a/content/canvas/test/test_canvas.html
+++ b/content/canvas/test/test_canvas.html
@@ -13,16 +13,33 @@ function IsD2DEnabled() {
     try {
         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
         enabled = Components.classes["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).D2DEnabled;
     } catch(e) {}
     
     return enabled;
 }
 
+function IsMacOSX10_5orOlder() {
+    var is105orOlder = false;
+
+    if (navigator.platform.indexOf("Mac") == 0) {
+        netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+        var version = Components.classes["@mozilla.org/system-info;1"]
+                            .getService(Components.interfaces.nsIPropertyBag2)
+                            .getProperty("version");
+        // the next line is correct: Mac OS 10.6 corresponds to Darwin version 10 !
+        // Mac OS 10.5 would be Darwin version 9. the |version| string we've got here
+        // is the Darwin version.
+        is105orOlder = (parseFloat(version) < 10.0);
+    }
+    return is105orOlder;
+}
+
+
 function IsAzureEnabled() {
   var enabled = false;
 
   try {
     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
     enabled = Components.classes["@mozilla.org/gfx/info;1"].getService(Components.interfaces.nsIGfxInfo).AzureEnabled;
   } catch (e) { }
 
@@ -5773,20 +5790,22 @@ isPixel(ctx, 80,25, 0,255,0,255, 2);
 <script>
 
 
 function test_2d_gradient_interpolate_overlap() {
 
 var canvas = document.getElementById('c215');
 var ctx = canvas.getContext('2d');
 
-if (!IsD2DEnabled()) {
-    // Only run this on non-D2D. On D2D the different nature of how gradients
+if (!IsD2DEnabled() && !IsMacOSX10_5orOlder()) {
+    // On D2D the different nature of how gradients
     // are drawn makes it so we cannot guarantee these stops are completely
     // hard.
+
+    // On OS X 10.5 quartz is confused by the overlapping stops: Bug #715235
     canvas.width = 200;
     var g = ctx.createLinearGradient(0, 0, 200, 0);
     g.addColorStop(0, '#f00');
     g.addColorStop(0, '#ff0');
     g.addColorStop(0.25, '#00f');
     g.addColorStop(0.25, '#0f0');
     g.addColorStop(0.25, '#0f0');
     g.addColorStop(0.25, '#0f0');
@@ -5830,22 +5849,25 @@ for (var p = 0; p < ps.length; ++p)
 {
         g.addColorStop(ps[p], '#0f0');
         for (var i = 0; i < 15; ++i)
                 g.addColorStop(ps[p], '#f00');
         g.addColorStop(ps[p], '#0f0');
 }
 ctx.fillStyle = g;
 ctx.fillRect(0, 0, 100, 50);
-isPixel(ctx, 1,25, 0,255,0,255, 0);
-isPixel(ctx, 30,25, 0,255,0,255, 0);
-isPixel(ctx, 40,25, 0,255,0,255, 0);
-isPixel(ctx, 60,25, 0,255,0,255, 0);
-isPixel(ctx, 80,25, 0,255,0,255, 0);
-
+
+if (!IsMacOSX10_5orOlder()) {
+    // On OS X 10.5 quartz is confused by the overlapping stops: Bug #715235
+    isPixel(ctx, 1,25, 0,255,0,255, 0);
+    isPixel(ctx, 30,25, 0,255,0,255, 0);
+    isPixel(ctx, 40,25, 0,255,0,255, 0);
+    isPixel(ctx, 60,25, 0,255,0,255, 0);
+    isPixel(ctx, 80,25, 0,255,0,255, 0);
+}
 
 }
 </script>
 
 <!-- [[[ test_2d.gradient.interpolate.solid.html ]]] -->
 
 <p>Canvas test: 2d.gradient.interpolate.solid</p>
 <canvas id="c217" width="100" height="50"><p class="fallback">FAIL (fallback content)</p></canvas>
--- a/content/html/content/src/nsHTMLTableElement.cpp
+++ b/content/html/content/src/nsHTMLTableElement.cpp
@@ -731,22 +731,24 @@ nsHTMLTableElement::InsertRow(PRInt32 aI
     if (newRow) {
       nsCOMPtr<nsIDOMNode> newRowNode(do_QueryInterface(newRow));
       nsCOMPtr<nsIDOMNode> retChild;
 
       // If index is -1 or equal to the number of rows, the new row
       // is appended.
       if (aIndex == -1 || PRUint32(aIndex) == rowCount) {
         rv = parent->AppendChild(newRowNode, getter_AddRefs(retChild));
+        NS_ENSURE_SUCCESS(rv, rv);
       }
       else
       {
         // insert the new row before the reference row we found above
         rv = parent->InsertBefore(newRowNode, refRow,
                                   getter_AddRefs(retChild));
+        NS_ENSURE_SUCCESS(rv, rv);
       }
 
       if (retChild) {
         CallQueryInterface(retChild, aValue);
       }
     }
   } else {
     // the row count was 0, so 
@@ -773,16 +775,17 @@ nsHTMLTableElement::InsertRow(PRInt32 aI
       nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tbody,
                                   getter_AddRefs(nodeInfo));
 
       nsCOMPtr<nsIContent> newRowGroup =
         NS_NewHTMLTableSectionElement(nodeInfo.forget());
 
       if (newRowGroup) {
         rv = AppendChildTo(newRowGroup, true);
+        NS_ENSURE_SUCCESS(rv, rv);
 
         rowGroup = do_QueryInterface(newRowGroup);
       }
     }
 
     if (rowGroup) {
       nsCOMPtr<nsINodeInfo> nodeInfo;
       nsContentUtils::NameChanged(mNodeInfo, nsGkAtoms::tr,
--- a/content/html/content/src/nsHTMLTableSectionElement.cpp
+++ b/content/html/content/src/nsHTMLTableSectionElement.cpp
@@ -186,18 +186,20 @@ nsHTMLTableSectionElement::InsertRow(PRI
   nsCOMPtr<nsIDOMNode> retChild;
 
   nsresult rv;
   if (doInsert) {
     nsCOMPtr<nsIDOMNode> refRow;
     rows->Item(aIndex, getter_AddRefs(refRow));
 
     rv = InsertBefore(rowNode, refRow, getter_AddRefs(retChild));
+    NS_ENSURE_SUCCESS(rv, rv);
   } else {
     rv = AppendChild(rowNode, getter_AddRefs(retChild));
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (retChild) {
     CallQueryInterface(retChild, aValue);
   }
 
   return NS_OK;
 }
--- a/dom/Makefile.in
+++ b/dom/Makefile.in
@@ -70,16 +70,17 @@ DIRS = \
   $(NULL)
 
 DIRS += \
   base \
   battery \
   sms \
   src \
   locales \
+  network \
   plugins/base \
   plugins/ipc \
   indexedDB \
   system \
   ipc \
   workers \
   $(NULL)
 
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -69,16 +69,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
 #include "SmsManager.h"
 #include "nsISmsService.h"
 #include "mozilla/Hal.h"
 #include "nsIWebNavigation.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "Connection.h"
 
 #ifdef MOZ_B2G_RIL
 #include "TelephonyFactory.h"
 #endif
 
 // This should not be in the namespace.
 DOMCI_DATA(Navigator, mozilla::dom::Navigator)
 
@@ -125,16 +126,17 @@ NS_INTERFACE_MAP_BEGIN(Navigator)
   NS_INTERFACE_MAP_ENTRY(nsIDOMClientInformation)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorGeolocation)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorBattery)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorDesktopNotification)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorSms)
 #ifdef MOZ_B2G_RIL
   NS_INTERFACE_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozNavigatorNetwork)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Navigator)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(Navigator)
 NS_IMPL_RELEASE(Navigator)
 
 void
 Navigator::Invalidate()
@@ -167,16 +169,21 @@ Navigator::Invalidate()
     mSmsManager = nsnull;
   }
 
 #ifdef MOZ_B2G_RIL
   if (mTelephony) {
     mTelephony = nsnull;
   }
 #endif
+
+  if (mConnection) {
+    mConnection->Shutdown();
+    mConnection = nsnull;
+  }
 }
 
 nsPIDOMWindow *
 Navigator::GetWindow()
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mWindow));
 
   return win;
@@ -989,23 +996,27 @@ Navigator::IsSmsAllowed() const
 
   // The current page hasn't been whitelisted.
   return false;
 }
 
 bool
 Navigator::IsSmsSupported() const
 {
-  nsCOMPtr<nsISmsService> smsService = do_GetService(SMSSERVICE_CONTRACTID);
+#ifdef MOZ_WEBSMS_BACKEND
+  nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(smsService, false);
 
   bool result = false;
   smsService->HasSupport(&result);
 
   return result;
+#else
+  return false;
+#endif
 }
 
 NS_IMETHODIMP
 Navigator::GetMozSms(nsIDOMMozSmsManager** aSmsManager)
 {
   *aSmsManager = nsnull;
 
   if (!mSmsManager) {
@@ -1054,16 +1065,43 @@ Navigator::GetMozTelephony(nsIDOMTelepho
   }
 
   telephony.forget(aTelephony);
   return NS_OK;
 }
 
 #endif // MOZ_B2G_RIL
 
+//*****************************************************************************
+//    Navigator::nsIDOMNavigatorNetwork
+//*****************************************************************************
+
+NS_IMETHODIMP
+Navigator::GetMozConnection(nsIDOMMozConnection** aConnection)
+{
+  *aConnection = nsnull;
+
+  if (!mConnection) {
+    nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+    NS_ENSURE_TRUE(window && window->GetDocShell(), NS_OK);
+
+    nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(window);
+    NS_ENSURE_TRUE(sgo, NS_OK);
+
+    nsIScriptContext* scx = sgo->GetContext();
+    NS_ENSURE_TRUE(scx, NS_OK);
+
+    mConnection = new network::Connection();
+    mConnection->Init(window, scx);
+  }
+
+  NS_ADDREF(*aConnection = mConnection);
+  return NS_OK;
+}
+
 PRInt64
 Navigator::SizeOf() const
 {
   PRInt64 size = sizeof(*this);
 
   // TODO: add SizeOf() to nsMimeTypeArray, bug 674113.
   size += mMimeTypes ? sizeof(*mMimeTypes.get()) : 0;
   // TODO: add SizeOf() to nsPluginArray, bug 674114.
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -44,24 +44,26 @@
 #define mozilla_dom_Navigator_h
 
 #include "nsIDOMNavigator.h"
 #include "nsIDOMNavigatorGeolocation.h"
 #include "nsIDOMNavigatorDesktopNotification.h"
 #include "nsIDOMClientInformation.h"
 #include "nsIDOMNavigatorBattery.h"
 #include "nsIDOMNavigatorSms.h"
+#include "nsIDOMNavigatorNetwork.h"
 #include "nsAutoPtr.h"
 #include "nsWeakReference.h"
 
 class nsPluginArray;
 class nsMimeTypeArray;
 class nsGeolocation;
 class nsDesktopNotificationCenter;
 class nsPIDOMWindow;
+class nsIDOMMozConnection;
 
 #ifdef MOZ_B2G_RIL
 #include "nsIDOMNavigatorTelephony.h"
 class nsIDOMTelephony;
 #endif
 
 //*****************************************************************************
 // Navigator: Script "navigator" object
@@ -73,41 +75,46 @@ namespace dom {
 namespace battery {
 class BatteryManager;
 } // namespace battery
 
 namespace sms {
 class SmsManager;
 } // namespace sms
 
-class Navigator : public nsIDOMNavigator,
-                  public nsIDOMClientInformation,
-                  public nsIDOMNavigatorGeolocation,
-                  public nsIDOMNavigatorDesktopNotification,
-                  public nsIDOMMozNavigatorBattery,
-                  public nsIDOMMozNavigatorSms
+namespace network {
+class Connection;
+} // namespace Connection;
+
+class Navigator : public nsIDOMNavigator
+                , public nsIDOMClientInformation
+                , public nsIDOMNavigatorGeolocation
+                , public nsIDOMNavigatorDesktopNotification
+                , public nsIDOMMozNavigatorBattery
+                , public nsIDOMMozNavigatorSms
 #ifdef MOZ_B2G_RIL
                 , public nsIDOMNavigatorTelephony
 #endif
+                , public nsIDOMMozNavigatorNetwork
 {
 public:
   Navigator(nsPIDOMWindow *aInnerWindow);
   virtual ~Navigator();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMNAVIGATOR
   NS_DECL_NSIDOMCLIENTINFORMATION
   NS_DECL_NSIDOMNAVIGATORGEOLOCATION
   NS_DECL_NSIDOMNAVIGATORDESKTOPNOTIFICATION
   NS_DECL_NSIDOMMOZNAVIGATORBATTERY
   NS_DECL_NSIDOMMOZNAVIGATORSMS
-
 #ifdef MOZ_B2G_RIL
   NS_DECL_NSIDOMNAVIGATORTELEPHONY
 #endif
+  NS_DECL_NSIDOMMOZNAVIGATORNETWORK
 
   static void Init();
 
   void Invalidate();
   nsPIDOMWindow *GetWindow();
 
   void RefreshMIMEArray();
 
@@ -128,16 +135,17 @@ private:
   nsRefPtr<nsPluginArray> mPlugins;
   nsRefPtr<nsGeolocation> mGeolocation;
   nsRefPtr<nsDesktopNotificationCenter> mNotification;
   nsRefPtr<battery::BatteryManager> mBatteryManager;
   nsRefPtr<sms::SmsManager> mSmsManager;
 #ifdef MOZ_B2G_RIL
   nsCOMPtr<nsIDOMTelephony> mTelephony;
 #endif
+  nsRefPtr<network::Connection> mConnection;
   nsWeakPtr mWindow;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 nsresult NS_GetNavigatorUserAgent(nsAString& aUserAgent);
 nsresult NS_GetNavigatorPlatform(nsAString& aPlatform);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -34,16 +34,21 @@
  * decision by deleting the provisions above and replace them with the notice
  * 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 ***** */
 
 #include "mozilla/Util.h"
+#include "SmsFilter.h" // On top because it includes basictypes.h.
+
+#ifdef XP_WIN
+#undef GetClassName
+#endif
 
 // JavaScript includes
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "jsprvtd.h"    // we are using private JS typedefs...
 #include "jsdbgapi.h"
 #include "WrapperFactory.h"
 #include "AccessCheck.h"
@@ -159,21 +164,16 @@
 #include "nsIDOMHTMLSelectElement.h"
 
 // HTMLEmbed/ObjectElement helper includes
 #include "nsNPAPIPluginInstance.h"
 #include "nsIObjectFrame.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIPluginHost.h"
 
-// Oh, did I mention that I hate Microsoft for doing this to me?
-#ifdef XP_WIN
-#undef GetClassName
-#endif
-
 // HTMLOptionsCollection includes
 #include "nsIDOMHTMLOptionElement.h"
 #include "nsIDOMHTMLOptionsCollection.h"
 
 // ContentList includes
 #include "nsContentList.h"
 #include "nsGenericElement.h"
 
@@ -509,17 +509,22 @@
 #include "nsWrapperCacheInlines.h"
 #include "dombindings.h"
 
 #include "nsIDOMBatteryManager.h"
 #include "BatteryManager.h"
 #include "nsIDOMSmsManager.h"
 #include "nsIDOMSmsMessage.h"
 #include "nsIDOMSmsEvent.h"
+#include "nsIDOMSmsRequest.h"
+#include "nsIDOMSmsFilter.h"
+#include "nsIDOMSmsCursor.h"
 #include "nsIPrivateDOMEvent.h"
+#include "nsIDOMConnection.h"
+#include "mozilla/dom/network/Utils.h"
 
 #ifdef MOZ_B2G_RIL
 #include "Telephony.h"
 #include "TelephonyCall.h"
 #include "CallEvent.h"
 #endif
 
 using namespace mozilla;
@@ -1406,16 +1411,28 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozSmsMessage, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozSmsEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
+  NS_DEFINE_CLASSINFO_DATA(MozSmsRequest, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(MozSmsFilter, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(MozSmsCursor, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  NS_DEFINE_CLASSINFO_DATA(MozConnection, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(CSSFontFaceRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CSSFontFaceStyleDecl, nsCSSStyleDeclSH,
                            ARRAY_SCRIPTABLE_FLAGS)
 
 #if defined(MOZ_MEDIA)
   NS_DEFINE_CLASSINFO_DATA(HTMLVideoElement, nsElementSH,
                            ELEMENT_SCRIPTABLE_FLAGS)
@@ -1632,16 +1649,17 @@ static const nsConstructorFuncMapData kC
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(Event)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(CustomEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(PopStateEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(HashChangeEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(PageTransitionEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(CloseEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(UIEvent)
   NS_DEFINE_EVENT_CONSTRUCTOR_FUNC_DATA(MouseEvent)
+  NS_DEFINE_CONSTRUCTOR_FUNC_DATA(MozSmsFilter, sms::SmsFilter::NewSmsFilter)
 };
 
 nsIXPConnect *nsDOMClassInfo::sXPConnect = nsnull;
 nsIScriptSecurityManager *nsDOMClassInfo::sSecMan = nsnull;
 bool nsDOMClassInfo::sIsInitialized = false;
 bool nsDOMClassInfo::sDisableDocumentAllSupport = false;
 bool nsDOMClassInfo::sDisableGlobalScopePollutionSupport = false;
 
@@ -2354,16 +2372,18 @@ nsDOMClassInfo::Init()
                                         Navigator::HasDesktopNotificationSupport())
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMClientInformation)
     DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorBattery,
                                         battery::BatteryManager::HasSupport())
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozNavigatorSms)
 #ifdef MOZ_B2G_RIL
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNavigatorTelephony)
 #endif
+    DOM_CLASSINFO_MAP_CONDITIONAL_ENTRY(nsIDOMMozNavigatorNetwork,
+                                        network::IsAPIEnabled())
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(Plugin, nsIDOMPlugin)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPlugin)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(PluginArray, nsIDOMPluginArray)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMPluginArray)
@@ -3981,16 +4001,33 @@ nsDOMClassInfo::Init()
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsMessage)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozSmsEvent, nsIDOMMozSmsEvent)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsEvent)
      DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(MozSmsRequest, nsIDOMMozSmsRequest)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsRequest)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(MozSmsFilter, nsIDOMMozSmsFilter)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsFilter)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(MozSmsCursor, nsIDOMMozSmsCursor)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsCursor)
+  DOM_CLASSINFO_MAP_END
+
+  DOM_CLASSINFO_MAP_BEGIN(MozConnection, nsIDOMMozConnection)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozConnection)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(CSSFontFaceRule, nsIDOMCSSFontFaceRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFaceRule)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(CSSFontFaceStyleDecl,
                                       nsIDOMCSSStyleDeclaration)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSStyleDeclaration)
   DOM_CLASSINFO_MAP_END
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -428,16 +428,21 @@ DOMCI_CLASS(GeoPositionCoords)
 DOMCI_CLASS(GeoPositionAddress)
 DOMCI_CLASS(GeoPositionError)
 
 DOMCI_CLASS(MozBatteryManager)
 
 DOMCI_CLASS(MozSmsManager)
 DOMCI_CLASS(MozSmsMessage)
 DOMCI_CLASS(MozSmsEvent)
+DOMCI_CLASS(MozSmsRequest)
+DOMCI_CLASS(MozSmsFilter)
+DOMCI_CLASS(MozSmsCursor)
+
+DOMCI_CLASS(MozConnection)
 
 // @font-face in CSS
 DOMCI_CLASS(CSSFontFaceRule)
 DOMCI_CLASS(CSSFontFaceStyleDecl)
 
 #if defined(MOZ_MEDIA)
 // WhatWG Video Element
 DOMCI_CLASS(HTMLVideoElement)
--- a/dom/dom-config.mk
+++ b/dom/dom-config.mk
@@ -1,11 +1,12 @@
 DOM_SRCDIRS = \
   dom/base \
   dom/battery \
+  dom/network/src \
   dom/sms/src \
   dom/src/events \
   dom/src/storage \
   dom/src/offline \
   dom/src/geolocation \
   dom/src/notification \
   dom/workers \
   content/xbl/src \
new file mode 100644
--- /dev/null
+++ b/dom/network/Makefile.in
@@ -0,0 +1,50 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org build system.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# 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 *****
+
+DEPTH            = ../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+PARALLEL_DIRS = interfaces src
+
+ifdef ENABLE_TESTS
+DIRS += tests
+endif
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/Makefile.in
@@ -0,0 +1,53 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org build system.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# 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 *****
+
+DEPTH            = ../../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+XPIDL_MODULE = dom_network
+
+include $(topsrcdir)/dom/dom-config.mk
+
+XPIDLSRCS = \
+  nsIDOMNavigatorNetwork.idl \
+  nsIDOMConnection.idl \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsIDOMConnection.idl
@@ -0,0 +1,48 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIDOMEventListener;
+
+[scriptable, uuid(8c6b574d-1135-4387-a6e3-6d8ba38d79a1)]
+interface nsIDOMMozConnection : nsISupports
+{
+  readonly attribute double  bandwidth;
+  readonly attribute boolean metered;
+
+           attribute nsIDOMEventListener onchange;
+};
new file mode 100644
--- /dev/null
+++ b/dom/network/interfaces/nsIDOMNavigatorNetwork.idl
@@ -0,0 +1,45 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIDOMMozConnection;
+
+[scriptable, uuid(1dd6773e-30dc-419b-9766-b05458fd96c8)]
+interface nsIDOMMozNavigatorNetwork : nsISupports
+{
+  readonly attribute nsIDOMMozConnection mozConnection;
+};
new file mode 100644
--- /dev/null
+++ b/dom/network/src/Connection.cpp
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include <limits>
+#include "mozilla/Hal.h"
+#include "Connection.h"
+#include "nsIDOMClassInfo.h"
+#include "mozilla/Preferences.h"
+#include "nsDOMEvent.h"
+#include "Constants.h"
+
+/**
+ * We have to use macros here because our leak analysis tool things we are
+ * leaking strings when we have |static const nsString|. Sad :(
+ */
+#define CHANGE_EVENT_NAME NS_LITERAL_STRING("change")
+
+DOMCI_DATA(MozConnection, mozilla::dom::network::Connection)
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+const char* Connection::sMeteredPrefName     = "dom.network.metered";
+const bool  Connection::sMeteredDefaultValue = false;
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Connection)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(Connection,
+                                                  nsDOMEventTargetWrapperCache)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(change)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(Connection,
+                                                nsDOMEventTargetWrapperCache)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(change)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(Connection)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozConnection)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozConnection)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozConnection)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache)
+
+NS_IMPL_ADDREF_INHERITED(Connection, nsDOMEventTargetWrapperCache)
+NS_IMPL_RELEASE_INHERITED(Connection, nsDOMEventTargetWrapperCache)
+
+Connection::Connection()
+  : mCanBeMetered(kDefaultCanBeMetered)
+  , mBandwidth(kDefaultBandwidth)
+{
+}
+
+void
+Connection::Init(nsPIDOMWindow *aWindow, nsIScriptContext* aScriptContext)
+{
+  // Those vars come from nsDOMEventTargetHelper.
+  mOwner = aWindow;
+  mScriptContext = aScriptContext;
+
+  hal::RegisterNetworkObserver(this);
+
+  hal::NetworkInformation networkInfo;
+  hal::GetCurrentNetworkInformation(&networkInfo);
+
+  UpdateFromNetworkInfo(networkInfo);
+}
+
+void
+Connection::Shutdown()
+{
+  hal::UnregisterNetworkObserver(this);
+}
+
+NS_IMETHODIMP
+Connection::GetBandwidth(double* aBandwidth)
+{
+  if (mBandwidth == kDefaultBandwidth) {
+    *aBandwidth = std::numeric_limits<double>::infinity();
+    return NS_OK;
+  }
+
+  *aBandwidth = mBandwidth;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+Connection::GetMetered(bool* aMetered)
+{
+  if (!mCanBeMetered) {
+    *aMetered = false;
+    return NS_OK;
+  }
+
+  *aMetered = Preferences::GetBool(sMeteredPrefName,
+                                   sMeteredDefaultValue);
+  return NS_OK;
+}
+
+NS_IMPL_EVENT_HANDLER(Connection, change)
+
+nsresult
+Connection::DispatchTrustedEventToSelf(const nsAString& aEventName)
+{
+  nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nsnull, nsnull);
+  nsresult rv = event->InitEvent(aEventName, false, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = event->SetTrusted(PR_TRUE);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  bool dummy;
+  rv = DispatchEvent(event, &dummy);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+void
+Connection::UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo)
+{
+  mBandwidth = aNetworkInfo.bandwidth();
+  mCanBeMetered = aNetworkInfo.canBeMetered();
+}
+
+void
+Connection::Notify(const hal::NetworkInformation& aNetworkInfo)
+{
+  double previousBandwidth = mBandwidth;
+  bool previousCanBeMetered = mCanBeMetered;
+
+  UpdateFromNetworkInfo(aNetworkInfo);
+
+  if (previousBandwidth == mBandwidth &&
+      previousCanBeMetered == mCanBeMetered) {
+    return;
+  }
+
+  DispatchTrustedEventToSelf(CHANGE_EVENT_NAME);
+}
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/dom/network/src/Connection.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef mozilla_dom_network_Connection_h
+#define mozilla_dom_network_Connection_h
+
+#include "nsIDOMConnection.h"
+#include "nsDOMEventTargetWrapperCache.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/Observer.h"
+#include "Types.h"
+
+namespace mozilla {
+
+namespace hal {
+class NetworkInformation;
+} // namespace hal
+
+namespace dom {
+namespace network {
+
+class Connection : public nsDOMEventTargetWrapperCache
+                 , public nsIDOMMozConnection
+                 , public NetworkObserver
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMMOZCONNECTION
+
+  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetWrapperCache::)
+
+  Connection();
+
+  void Init(nsPIDOMWindow *aWindow, nsIScriptContext* aScriptContext);
+  void Shutdown();
+
+  // For IObserver
+  void Notify(const hal::NetworkInformation& aNetworkInfo);
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Connection,
+                                           nsDOMEventTargetWrapperCache)
+
+private:
+  /**
+   * Dispatch a trusted non-cancellable and non-bubbling event to itself.
+   */
+  nsresult DispatchTrustedEventToSelf(const nsAString& aEventName);
+
+  /**
+   * Update the connection information stored in the object using a
+   * NetworkInformation object.
+   */
+  void UpdateFromNetworkInfo(const hal::NetworkInformation& aNetworkInfo);
+
+  /**
+   * If the connection is of a type that can be metered.
+   */
+  bool mCanBeMetered;
+
+  /**
+   * The connection bandwidth.
+   */
+  double mBandwidth;
+
+  NS_DECL_EVENT_HANDLER(change)
+
+  static const char* sMeteredPrefName;
+  static const bool  sMeteredDefaultValue;
+};
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_network_Connection_h
new file mode 100644
--- /dev/null
+++ b/dom/network/src/Constants.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef mozilla_dom_network_Constants_h__
+#define mozilla_dom_network_Constants_h__
+
+/**
+ * A set of constants to be used by network backends.
+ */
+namespace mozilla {
+namespace dom {
+namespace network {
+
+  static const double kDefaultBandwidth    = -1.0;
+  static const bool   kDefaultCanBeMetered = false;
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_network_Constants_h__
new file mode 100644
--- /dev/null
+++ b/dom/network/src/Makefile.in
@@ -0,0 +1,69 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org build system.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# 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 *****
+
+DEPTH            = ../../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = $(srcdir)
+
+include $(DEPTH)/config/autoconf.mk
+
+LIBRARY_NAME     = dom_network_s
+LIBXUL_LIBRARY   = 1
+FORCE_STATIC_LIB = 1
+
+include $(topsrcdir)/dom/dom-config.mk
+
+EXPORTS_NAMESPACES = mozilla/dom/network
+
+EXPORTS_mozilla/dom/network = \
+  Utils.h \
+  Types.h \
+  Constants.h \
+  $(NULL)
+
+CPPSRCS = \
+  Connection.cpp \
+  Utils.cpp \
+  $(NULL)
+
+LOCAL_INCLUDES = \
+  -I$(topsrcdir)/content/events/src \
+  $(NULL)
+
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/network/src/Types.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef mozilla_dom_network_Types_h
+#define mozilla_dom_network_Types_h
+
+namespace mozilla {
+namespace hal {
+class NetworkInformation;
+} // namespace hal
+
+template <class T>
+class Observer;
+
+typedef Observer<hal::NetworkInformation> NetworkObserver;
+
+} // namespace mozilla
+
+#endif // mozilla_dom_network_Types_h
new file mode 100644
--- /dev/null
+++ b/dom/network/src/Utils.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "Utils.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+/* extern */ bool
+IsAPIEnabled()
+{
+  return Preferences::GetBool("dom.network.enabled", true);
+}
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/network/src/Utils.h
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef mozilla_dom_network_Utils_h
+#define mozilla_dom_network_Utils_h
+
+namespace mozilla {
+namespace dom {
+namespace network {
+
+/**
+ * Returns whether the Network API is enabled.
+ * @return whether the Network API is enabled.
+ */
+extern bool IsAPIEnabled();
+
+} // namespace network
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_network_Utils_h
+
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/Makefile.in
@@ -0,0 +1,62 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is mozilla.org build system.
+#
+# The Initial Developer of the Original Code is Mozilla Foundation
+# Portions created by the Initial Developer are Copyright (C) 2011
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s):
+#   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# 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 *****
+
+DEPTH            = ../../..
+topsrcdir        = @top_srcdir@
+srcdir           = @srcdir@
+VPATH            = @srcdir@
+
+relativesrcdir   = dom/network/tests
+
+include $(DEPTH)/config/autoconf.mk
+
+DIRS = \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+
+_TEST_FILES = \
+  test_network_basics.html \
+  $(NULL)
+
+_CHROME_TEST_FILES = \
+  $(NULL)
+
+libs:: $(_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
+
+#libs:: $(_CHROME_TEST_FILES)
+#	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/network/tests/test_network_basics.html
@@ -0,0 +1,44 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test for Network API</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+/** Test for Network API **/
+
+function checkInterface(aInterface) {
+  ok(!(aInterface in window), aInterface + " should be prefixed");
+  ok(("Moz" + aInterface) in window, aInterface + " should be prefixed");
+}
+
+ok('mozConnection' in navigator, "navigator.mozConnection should exist");
+
+ok(navigator.mozConnection, "navigator.mozConnection returns an object");
+
+ok(navigator.mozConnection instanceof MozConnection,
+   "navigator.mozConnection is a MozConnection object");
+
+checkInterface("Connection");
+
+ok('bandwidth' in navigator.mozConnection,
+   "bandwidth should be a Connection attribute");
+is(navigator.mozConnection.bandwidth, Infinity,
+   "By default connection.bandwidth is equals to Infinity");
+
+ok('metered' in navigator.mozConnection,
+   "metered should be a Connection attribute");
+is(navigator.mozConnection.metered, false,
+   "By default the connection is not metered");
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -2502,17 +2502,17 @@ NPError NP_CALLBACK
       else {
         return NPERR_GENERIC_ERROR;
       }
     }
 #endif
 #ifdef MOZ_WIDGET_ANDROID
   case kRequestDrawingModel_ANPSetValue:
     if (inst)
-      inst->SetDrawingModel(NS_PTR_TO_INT32(result));
+      inst->SetANPDrawingModel(NS_PTR_TO_INT32(result));
     return NPERR_NO_ERROR;
   case kAcceptEvents_ANPSetValue:
     return NPERR_NO_ERROR;
 #endif
     default:
       return NPERR_GENERIC_ERROR;
   }
 }
--- a/dom/plugins/base/nsNPAPIPluginInstance.cpp
+++ b/dom/plugins/base/nsNPAPIPluginInstance.cpp
@@ -84,17 +84,17 @@ nsNPAPIPluginInstance::nsNPAPIPluginInst
 #ifdef NP_NO_QUICKDRAW
     mDrawingModel(NPDrawingModelCoreGraphics),
 #else
     mDrawingModel(NPDrawingModelQuickDraw),
 #endif
 #endif
 #ifdef MOZ_WIDGET_ANDROID
     mSurface(nsnull),
-    mDrawingModel(0),
+    mANPDrawingModel(0),
 #endif
     mRunning(NOT_STARTED),
     mWindowless(false),
     mWindowlessLocal(false),
     mTransparent(false),
     mCached(false),
     mUsesDOMForCursor(false),
     mInPluginInitCall(false),
@@ -726,20 +726,21 @@ void nsNPAPIPluginInstance::SetEventMode
     return;
   }
 
   owner->SetEventModel(aModel);
 }
 #endif
 
 #if defined(MOZ_WIDGET_ANDROID)
-void nsNPAPIPluginInstance::SetDrawingModel(PRUint32 aModel)
+void nsNPAPIPluginInstance::SetANPDrawingModel(PRUint32 aModel)
 {
-  mDrawingModel = aModel;
+  mANPDrawingModel = aModel;
 }
+
 class SurfaceGetter : public nsRunnable {
 public:
   SurfaceGetter(nsNPAPIPluginInstance* aInstance, NPPluginFuncs* aPluginFunctions, NPP_t aNPP) : 
     mInstance(aInstance), mPluginFunctions(aPluginFunctions), mNPP(aNPP) {
   }
   ~SurfaceGetter() {
   }
   nsresult Run() {
@@ -755,17 +756,17 @@ private:
   nsNPAPIPluginInstance* mInstance;
   NPP_t mNPP;
   NPPluginFuncs* mPluginFunctions;
 };
 
 
 void* nsNPAPIPluginInstance::GetJavaSurface()
 {
-  if (mDrawingModel != kSurface_ANPDrawingModel)
+  if (mANPDrawingModel != kSurface_ANPDrawingModel)
     return nsnull;
   
   return mSurface;
 }
 
 void nsNPAPIPluginInstance::SetJavaSurface(void* aSurface)
 {
   mSurface = aSurface;
@@ -780,17 +781,17 @@ void nsNPAPIPluginInstance::RequestJavaS
 
   ((SurfaceGetter*)mSurfaceGetter.get())->RequestSurface();
 }
 
 #endif
 
 nsresult nsNPAPIPluginInstance::GetDrawingModel(PRInt32* aModel)
 {
-#if defined(XP_MACOSX) || defined(MOZ_WIDGET_ANDROID)
+#if defined(XP_MACOSX)
   *aModel = (PRInt32)mDrawingModel;
   return NS_OK;
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
 nsresult nsNPAPIPluginInstance::IsRemoteDrawingCoreAnimation(bool* aDrawing)
--- a/dom/plugins/base/nsNPAPIPluginInstance.h
+++ b/dom/plugins/base/nsNPAPIPluginInstance.h
@@ -145,17 +145,18 @@ public:
   bool UsesDOMForCursor();
 
 #ifdef XP_MACOSX
   void SetDrawingModel(NPDrawingModel aModel);
   void SetEventModel(NPEventModel aModel);
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
-  void SetDrawingModel(PRUint32 aModel);
+  PRUint32 GetANPDrawingModel() { return mANPDrawingModel; }
+  void SetANPDrawingModel(PRUint32 aModel);
   void* GetJavaSurface();
   void SetJavaSurface(void* aSurface);
   void RequestJavaSurface();
 #endif
 
   nsresult NewStreamListener(const char* aURL, void* notifyData,
                              nsIPluginStreamListener** listener);
 
@@ -224,17 +225,17 @@ protected:
   // the browser.
   NPP_t mNPP;
 
 #ifdef XP_MACOSX
   NPDrawingModel mDrawingModel;
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
-  PRUint32 mDrawingModel;
+  PRUint32 mANPDrawingModel;
   nsCOMPtr<nsIRunnable> mSurfaceGetter;
 #endif
 
   enum {
     NOT_STARTED,
     RUNNING,
     DESTROYING,
     DESTROYED
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -2863,18 +2863,17 @@ void nsPluginInstanceOwner::Paint(const 
 
 void nsPluginInstanceOwner::Paint(gfxContext* aContext,
                                   const gfxRect& aFrameRect,
                                   const gfxRect& aDirtyRect)
 {
   if (!mInstance || !mObjectFrame)
     return;
 
-  PRInt32 model;
-  mInstance->GetDrawingModel(&model);
+  PRInt32 model = mInstance->GetANPDrawingModel();
 
   if (model == kSurface_ANPDrawingModel) {
     if (!AddPluginView(aFrameRect)) {
       NPRect rect;
       rect.left = rect.top = 0;
       rect.right = aFrameRect.width;
       rect.bottom = aFrameRect.height;
       InvalidateRect(&rect);
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -2575,17 +2575,17 @@ PluginInstanceChild::MaybeCreatePlatform
             return true;
         }
 #endif
         // For image layer surface we should always create helper surface
         createHelperSurface = true;
         // Check if we can create helper surface with non-default visual
         visual = gfxXlibSurface::FindVisual(screen,
             static_cast<gfxImageSurface*>(mCurrentSurface.get())->Format());
-        if (visual && defaultVisual != visual && !supportNonDefaultVisual) {
+        if (!visual || (defaultVisual != visual && !supportNonDefaultVisual)) {
             visual = defaultVisual;
             mDoAlphaExtraction = mIsTransparent;
         }
     }
 
     if (createHelperSurface) {
         if (!visual) {
             NS_ERROR("Need X falback surface, but visual failed");
--- a/dom/sms/interfaces/Makefile.in
+++ b/dom/sms/interfaces/Makefile.in
@@ -46,11 +46,15 @@ XPIDL_MODULE = dom_sms
 include $(topsrcdir)/dom/dom-config.mk
 
 XPIDLSRCS = \
   nsIDOMNavigatorSms.idl \
   nsIDOMSmsManager.idl \
   nsISmsService.idl \
   nsIDOMSmsMessage.idl \
   nsIDOMSmsEvent.idl \
+  nsISmsDatabaseService.idl \
+  nsIDOMSmsRequest.idl \
+  nsIDOMSmsFilter.idl \
+  nsIDOMSmsCursor.idl \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/dom/sms/interfaces/nsIDOMSmsCursor.idl
@@ -0,0 +1,48 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+interface nsIDOMMozSmsFilter;
+interface nsIDOMMozSmsMessage;
+
+[scriptable, function, uuid(77b41d7e-ccb1-4480-8322-2af7bc437a3c)]
+interface nsIDOMMozSmsCursor : nsISupports
+{
+  // Can be null if there is no more results.
+  readonly attribute nsIDOMMozSmsMessage message;
+                                    void continue();
+};
--- a/dom/sms/interfaces/nsIDOMSmsEvent.idl
+++ b/dom/sms/interfaces/nsIDOMSmsEvent.idl
@@ -34,13 +34,14 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 #include "nsIDOMEvent.idl"
 
 interface nsIDOMMozSmsMessage;
 
-[scriptable, uuid(34dda4c3-4683-4323-9ee3-2a7bfef7df3b)]
+[scriptable, uuid(fa8d1c86-85b1-4e5b-978c-12dd296cd1cc)]
 interface nsIDOMMozSmsEvent : nsIDOMEvent
 {
+  [binaryname(MessageMoz)]
   readonly attribute nsIDOMMozSmsMessage message;
 };
new file mode 100644
--- /dev/null
+++ b/dom/sms/interfaces/nsIDOMSmsFilter.idl
@@ -0,0 +1,57 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+[scriptable, builtinclass, uuid(7da08e45-ee81-4293-912b-2f2fea5b6935)]
+interface nsIDOMMozSmsFilter : nsISupports
+{
+  // A date that can return null.
+  [implicit_jscontext]
+  attribute jsval startDate;
+
+  // A date that can return null.
+  [implicit_jscontext]
+  attribute jsval endDate;
+
+  // An array of DOMString that can return null.
+  [implicit_jscontext]
+  attribute jsval numbers;
+
+  // A DOMString that can return and be set to "sent", "received" or null.
+  [Null(Empty)]
+  attribute DOMString delivery;
+};
--- a/dom/sms/interfaces/nsIDOMSmsManager.idl
+++ b/dom/sms/interfaces/nsIDOMSmsManager.idl
@@ -32,17 +32,33 @@
  * 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 ***** */
 
 #include "nsIDOMEventTarget.idl"
 
 interface nsIDOMEventListener;
+interface nsIDOMMozSmsRequest;
+interface nsIDOMMozSmsFilter;
 
-[scriptable, function, uuid(807d593c-09cb-4aa3-afa5-aa0a671bd0d3)]
+[scriptable, function, uuid(c9916dce-2947-41bb-95c2-818f792a020c)]
 interface nsIDOMMozSmsManager : nsIDOMEventTarget
 {
-  unsigned short getNumberOfMessagesForText(in DOMString text);
-            void send(in DOMString number, in DOMString message);
+  unsigned short      getNumberOfMessagesForText(in DOMString text);
+
+  // The first parameter can be either a DOMString (only one number) or an array
+  // of DOMStrings.
+  // The method returns a SmsRequest object if one number has been passed.
+  // An array of SmsRequest objects otherwise.
+  jsval send(in jsval number, in DOMString message);
+
+  [binaryname(GetMessageMoz)] nsIDOMMozSmsRequest getMessage(in long id);
+
+  // The parameter can be either a message id or a SmsMessage.
+  nsIDOMMozSmsRequest delete(in jsval param);
+
+  nsIDOMMozSmsRequest getMessages(in nsIDOMMozSmsFilter filter, in boolean reverse);
 
   attribute nsIDOMEventListener onreceived;
+  attribute nsIDOMEventListener onsent;
+  attribute nsIDOMEventListener ondelivered;
 };
new file mode 100644
--- /dev/null
+++ b/dom/sms/interfaces/nsIDOMSmsRequest.idl
@@ -0,0 +1,53 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "nsIDOMEventTarget.idl"
+
+interface nsIDOMEventListener;
+
+[scriptable, function, uuid(1b24469d-cfb7-4667-aaf0-c1d17289ae7c)]
+interface nsIDOMMozSmsRequest : nsIDOMEventTarget
+{
+  // Returns whether "processing" or "done".
+  readonly attribute DOMString           readyState;
+  // Can be null.
+  readonly attribute DOMString           error;
+  // Can be bool, nsIDOMSmsMessage, nsIDOMSmsIterator or null.
+  readonly attribute jsval               result;
+
+           attribute nsIDOMEventListener onsuccess;
+           attribute nsIDOMEventListener onerror;
+};
new file mode 100644
--- /dev/null
+++ b/dom/sms/interfaces/nsISmsDatabaseService.idl
@@ -0,0 +1,60 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *  Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "nsISupports.idl"
+
+%{C++
+#define SMS_DATABASE_SERVICE_CID \
+{ 0x2454c2a1, 0xefdd, 0x4d96,    \
+{ 0x83, 0xbd, 0x51, 0xa2, 0x9a, 0x21, 0xf5, 0xab } }
+#define SMS_DATABASE_SERVICE_CONTRACTID "@mozilla.org/sms/smsdatabaseservice;1"
+%}
+
+interface nsIDOMMozSmsFilter;
+
+[scriptable, function, uuid(3ddf7dc3-626c-47ee-8b41-3f55d5af49c9)]
+interface nsISmsDatabaseService : nsISupports
+{
+  // Takes some information required to save the message and returns its id.
+  long saveSentMessage(in DOMString aReceiver, in DOMString aBody, in unsigned long long aDate);
+
+  [binaryname(GetMessageMoz)] void getMessage(in long messageId, in long requestId, [optional] in unsigned long long processId);
+  void deleteMessage(in long messageId, in long requestId, [optional] in unsigned long long processId);
+
+  void createMessageList(in nsIDOMMozSmsFilter filter, in boolean reverse, in long requestId, [optional] in unsigned long long processId);
+  void getNextMessageInList(in long listId, in long requestId, in unsigned long long processId);
+  void clearMessageList(in long listId);
+};
--- a/dom/sms/interfaces/nsISmsService.idl
+++ b/dom/sms/interfaces/nsISmsService.idl
@@ -35,26 +35,27 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIDOMMozSmsMessage;
 
 %{C++
-#define NS_SMSSERVICE_CID { 0xbada3cb8, 0xa568, 0x4dff, { 0xb5, 0x43, 0x52, 0xbb, 0xb3, 0x14, 0x31, 0x21 } }
-#define SMSSERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1"
+#define SMS_SERVICE_CID { 0xbada3cb8, 0xa568, 0x4dff, { 0xb5, 0x43, 0x52, 0xbb, 0xb3, 0x14, 0x31, 0x21 } }
+#define SMS_SERVICE_CONTRACTID "@mozilla.org/sms/smsservice;1"
 %}
 
 [scriptable, builtinclass, uuid(a0fbbe74-5d61-4b7e-b7ab-9b5224f9e5e9)]
 interface nsISmsService : nsISupports
 {
   boolean        hasSupport();
   unsigned short getNumberOfMessagesForText(in DOMString text);
-            void send(in DOMString number, in DOMString message);
+            void send(in DOMString number, in DOMString message,
+                      in long requestId, [optional] in unsigned long long processId);
 
   [implicit_jscontext]
   nsIDOMMozSmsMessage createSmsMessage(in long      id,
                                        in DOMString delivery,
                                        in DOMString sender,
                                        in DOMString receiver,
                                        in DOMString body,
                                        in jsval     timestamp);
--- a/dom/sms/src/Constants.cpp
+++ b/dom/sms/src/Constants.cpp
@@ -34,13 +34,15 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 namespace mozilla {
 namespace dom {
 namespace sms {
 
-const char* kSmsReceivedObserverTopic = "sms-received";
+const char* kSmsReceivedObserverTopic  = "sms-received";
+const char* kSmsSentObserverTopic      = "sms-sent";
+const char* kSmsDeliveredObserverTopic = "sms-delivered";
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
--- a/dom/sms/src/Constants.h
+++ b/dom/sms/src/Constants.h
@@ -37,17 +37,19 @@
 
 #ifndef mozilla_dom_sms_Constants_h
 #define mozilla_dom_sms_Constants_h
 
 namespace mozilla {
 namespace dom {
 namespace sms {
 
-extern const char* kSmsReceivedObserverTopic; // Defined in the .cpp.
+extern const char* kSmsReceivedObserverTopic;  // Defined in the .cpp.
+extern const char* kSmsSentObserverTopic;      // Defined in the .cpp.
+extern const char* kSmsDeliveredObserverTopic; // Defined in the .cpp.
 
 #define DELIVERY_RECEIVED NS_LITERAL_STRING("received")
 #define DELIVERY_SENT     NS_LITERAL_STRING("sent")
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/sms/src/Makefile.in
+++ b/dom/sms/src/Makefile.in
@@ -58,36 +58,44 @@ FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/sms
 
 EXPORTS_mozilla/dom/sms = \
   SmsChild.h \
   SmsParent.h \
-  SmsServiceFactory.h \
+  SmsServicesFactory.h \
   Constants.h \
   Types.h \
   SmsMessage.h \
+  SmsRequestManager.h \
+  SmsRequest.h \
   $(NULL)
 
 CPPSRCS = \
   SmsManager.cpp \
   SmsService.cpp \
   SmsIPCService.cpp \
-  SmsServiceFactory.cpp \
+  SmsServicesFactory.cpp \
   SmsParent.cpp \
   SmsMessage.cpp \
   SmsEvent.cpp \
   Constants.cpp \
   SmsChild.cpp \
+  SmsDatabaseService.cpp \
+  SmsRequest.cpp \
+  SmsRequestManager.cpp \
+  SmsFilter.cpp \
+  SmsCursor.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(topsrcdir)/content/events/src \
+  -I$(topsrcdir)/dom/base \
   $(NULL)
 
 # Add VPATH to LOCAL_INCLUDES so we are going to include the correct backend
 # subdirectory (and the ipc one).
 LOCAL_INCLUDES += $(VPATH:%=-I%)
 
 ifdef MOZ_B2G_RIL
 LOCAL_INCLUDES += \
new file mode 100644
--- /dev/null
+++ b/dom/sms/src/SmsCursor.cpp
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "SmsCursor.h"
+#include "nsIDOMClassInfo.h"
+#include "nsDOMError.h"
+#include "nsIDOMSmsMessage.h"
+#include "nsIDOMSmsRequest.h"
+#include "SmsRequest.h"
+#include "SmsRequestManager.h"
+#include "nsISmsDatabaseService.h"
+
+DOMCI_DATA(MozSmsCursor, mozilla::dom::sms::SmsCursor)
+
+namespace mozilla {
+namespace dom {
+namespace sms {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SmsCursor)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsCursor)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozSmsCursor)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_2(SmsCursor, mRequest, mMessage)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SmsCursor)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SmsCursor)
+
+SmsCursor::SmsCursor()
+  : mListId(-1)
+{
+}
+
+SmsCursor::SmsCursor(PRInt32 aListId, nsIDOMMozSmsRequest* aRequest)
+  : mListId(aListId)
+  , mRequest(aRequest)
+{
+}
+
+SmsCursor::~SmsCursor()
+{
+  NS_ASSERTION(!mMessage, "mMessage shouldn't be set!");
+
+  if (mListId != -1) {
+    nsCOMPtr<nsISmsDatabaseService> smsDBService =
+      do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
+
+    if (!smsDBService) {
+      NS_ERROR("Can't find SmsDBService!");
+    }
+
+    smsDBService->ClearMessageList(mListId);
+  }
+}
+
+void
+SmsCursor::Disconnect()
+{
+  NS_ASSERTION(!mMessage, "mMessage shouldn't be set!");
+
+  mRequest = nsnull;
+  mListId = -1;
+}
+
+NS_IMETHODIMP
+SmsCursor::GetMessage(nsIDOMMozSmsMessage** aMessage)
+{
+  NS_IF_ADDREF(*aMessage = mMessage);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsCursor::Continue()
+{
+  // No message means we are waiting for a message or we got the last one.
+  if (!mMessage) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
+  mMessage = nsnull;
+  static_cast<SmsRequest*>(mRequest.get())->Reset();
+
+  PRInt32 requestId = SmsRequestManager::GetInstance()->AddRequest(mRequest);
+
+  nsCOMPtr<nsISmsDatabaseService> smsDBService =
+    do_GetService(SMS_DATABASE_SERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(smsDBService, NS_ERROR_FAILURE);
+
+  smsDBService->GetNextMessageInList(mListId, requestId, 0);
+
+  return NS_OK;
+}
+
+} // namespace sms
+} // namespace dom
+} // namespace mozilla
+
new file mode 100644
--- /dev/null
+++ b/dom/sms/src/SmsCursor.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#ifndef mozilla_dom_sms_SmsCursor_h
+#define mozilla_dom_sms_SmsCursor_h
+
+#include "nsIDOMSmsCursor.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsCOMPtr.h"
+
+class nsIDOMMozSmsMessage;
+class nsIDOMMozSmsRequest;
+
+namespace mozilla {
+namespace dom {
+namespace sms {
+
+class SmsCursor : public nsIDOMMozSmsCursor
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSIDOMMOZSMSCURSOR
+
+  NS_DECL_CYCLE_COLLECTION_CLASS(SmsCursor)
+
+  SmsCursor();
+  SmsCursor(PRInt32 aListId, nsIDOMMozSmsRequest* aRequest);
+
+  ~SmsCursor();
+
+  void SetMessage(nsIDOMMozSmsMessage* aMessage);
+
+  void Disconnect();
+
+private:
+  PRInt32                       mListId;
+  nsCOMPtr<nsIDOMMozSmsRequest> mRequest;
+  nsCOMPtr<nsIDOMMozSmsMessage> mMessage;
+};
+
+inline void
+SmsCursor::SetMessage(nsIDOMMozSmsMessage* aMessage)
+{
+  mMessage = aMessage;
+}
+
+} // namespace sms
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_sms_SmsCursor_h
--- a/dom/sms/src/SmsEvent.cpp
+++ b/dom/sms/src/SmsEvent.cpp
@@ -72,17 +72,17 @@ SmsEvent::Init(const nsAString& aEventTy
                                       aCancelableArg);
   NS_ENSURE_SUCCESS(rv, rv);
 
   mMessage = aMessage;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-SmsEvent::GetMessage(nsIDOMMozSmsMessage** aMessage)
+SmsEvent::GetMessageMoz(nsIDOMMozSmsMessage** aMessage)
 {
   NS_IF_ADDREF(*aMessage = mMessage);
   return NS_OK;
 }
 
 } // namespace sms
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/sms/src/SmsFilter.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Mounir Lamouri <mounir.lamouri@mozilla.com> (Original Author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * 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 ***** */
+
+#include "SmsFilter.h"
+#include "nsIDOMClassInfo.h"
+#include "Constants.h"
+#include "nsError.h"
+#include "Constants.h"
+#include "jsapi.h"
+#include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch.
+#include "js/Utility.h"
+#include "nsJSUtils.h"
+#include "nsDOMString.h"
+
+DOMCI_DATA(MozSmsFilter, mozilla::dom::sms::SmsFilter)
+
+namespace mozilla {
+namespace dom {
+namespace sms {
+
+NS_INTERFACE_MAP_BEGIN(SmsFilter)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozSmsFilter)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozSmsFilter)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(SmsFilter)
+NS_IMPL_RELEASE(SmsFilter)
+
+SmsFilter::SmsFilter()
+{
+  mData.startDate() = 0;
+  mData.endDate() = 0;
+  mData.delivery() = eDeliveryState_Unknown;
+}
+
+SmsFilter::SmsFilter(const SmsFilterData& aData)
+  : mData(aData)
+{
+}
+
+/* static */ nsresult
+SmsFilter::NewSmsFilter(nsISupports** aSmsFilter)
+{
+  NS_ADDREF(*aSmsFilter = new SmsFilter());
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsFilter::GetStartDate(JSContext* aCx, jsval* aStartDate)
+{
+  if (mData.startDate() == 0) {
+    *aStartDate = JSVAL_NULL;
+    return NS_OK;
+  }
+
+  aStartDate->setObjectOrNull(JS_NewDateObjectMsec(aCx, mData.startDate()));
+  NS_ENSURE_TRUE(aStartDate->isObject(), NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsFilter::SetStartDate(JSContext* aCx, const jsval& aStartDate)
+{
+  if (aStartDate == JSVAL_NULL) {
+    mData.startDate() = 0;
+    return NS_OK;
+  }
+
+  if (!aStartDate.isObject()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  JSObject& obj = aStartDate.toObject();
+  if (!JS_ObjectIsDate(aCx, &obj)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mData.startDate() = js_DateGetMsecSinceEpoch(aCx, &obj);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsFilter::GetEndDate(JSContext* aCx, jsval* aEndDate)
+{
+  if (mData.endDate() == 0) {
+    *aEndDate = JSVAL_NULL;
+    return NS_OK;
+  }
+
+  aEndDate->setObjectOrNull(JS_NewDateObjectMsec(aCx, mData.endDate()));
+  NS_ENSURE_TRUE(aEndDate->isObject(), NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsFilter::SetEndDate(JSContext* aCx, const jsval& aEndDate)
+{
+  if (aEndDate == JSVAL_NULL) {
+    mData.endDate() = 0;
+    return NS_OK;
+  }
+
+  if (!aEndDate.isObject()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  JSObject& obj = aEndDate.toObject();
+  if (!JS_ObjectIsDate(aCx, &obj)) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  mData.endDate() = js_DateGetMsecSinceEpoch(aCx, &obj);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsFilter::GetNumbers(JSContext* aCx, jsval* aNumbers)
+{
+  PRUint32 length = mData.numbers().Length();
+
+  if (length == 0) {
+    *aNumbers = JSVAL_NULL;
+    return NS_OK;
+  }
+
+  jsval* numbers = new jsval[length];
+
+  for (PRUint32 i=0; i<length; ++i) {
+    numbers[i].setString(JS_NewUCStringCopyN(aCx, mData.numbers()[i].get(),
+                                             mData.numbers()[i].Length()));
+  }
+
+  aNumbers->setObjectOrNull(JS_NewArrayObject(aCx, length, numbers));
+  NS_ENSURE_TRUE(aNumbers->isObject(), NS_ERROR_FAILURE);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+SmsFilter::SetNumbers(JSContext* aCx, const jsval& aNumbers)
+{
+  if (aNumbers == JSVAL_NULL) {
+    mData.numbers().Clear();