merge fx-team to m-c
authorRob Campbell <rcampbell@mozilla.com>
Thu, 08 Mar 2012 10:30:11 -0400
changeset 91394 75b24396b0ce85ad88afa8048662515c978bdd02
parent 91393 e3edd93da0fcbad7ef3085e7e8905bbcacf585d6 (current diff)
parent 91373 668fbc214c2ab4b578a9378a3ff9ba32ce4517ab (diff)
child 91395 8219e651919031b7eec9aa82c5c91b5d87db234a
child 91401 151a006fd5d113bf99eee94141bc046fb7553961
push id783
push userlsblakk@mozilla.com
push dateTue, 24 Apr 2012 17:33:42 +0000
treeherdermozilla-beta@11faed19f136 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone13.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge fx-team to m-c
browser/base/content/browser.js
browser/components/nsBrowserGlue.js
build/automation.py.in
intl/locale/public/nsICharsetAlias.h
intl/locale/src/nsCharsetAlias.h
intl/locale/src/nsCharsetAliasImp.cpp
mobile/android/base/AutoCompletePopup.java
toolkit/mozapps/readstrings/Makefile.in
toolkit/mozapps/readstrings/errors.h
toolkit/mozapps/readstrings/readstrings.cpp
toolkit/mozapps/readstrings/readstrings.h
xpcom/ds/nsDoubleHashtable.h
--- a/accessible/public/nsIAccessibleEditableText.idl
+++ b/accessible/public/nsIAccessibleEditableText.idl
@@ -38,17 +38,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIEditor;
 
-[scriptable, uuid(52837507-202d-4e72-a482-5f068a1fd720)]
+[scriptable, uuid(e242d495-5cde-4b1c-8c84-2525b14939f5)]
 interface nsIAccessibleEditableText : nsISupports
 {
   /**
    * Sets the attributes for the text between the two given indices. The old
    * attributes are replaced by the new list of attributes. For example,
    * sets font styles, such as italic, bold...
    *
    * @param startPos - start index of the text whose attributes are modified.
@@ -98,24 +98,9 @@ interface nsIAccessibleEditableText : ns
 
   /**
    * Pastes text from the clipboard.
    *
    * @param position - index at which to insert the text from the system
    *                   clipboard into the text represented by this object.
    */
   void pasteText (in long position);
-
-  /**
-   * Returns an editor associated with the accessible.
-   */
-  [noscript] readonly attribute nsIEditor associatedEditor;
 };
-
-/*
- Assumptions:
-
- selectAttributes method takes an nsISupports parameter.
-        'set' methods throw exception on failure.
- 'wstring' inputs are potentially multibyte (UTF-16 for
-        instance); 'string' and UTF-8 may be a better choice.
-
-*/
--- a/accessible/src/atk/nsApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.cpp
@@ -692,16 +692,26 @@ nsApplicationAccessibleWrap::Unload()
     }
     // if (sATKLib) {
     //     PR_UnloadLibrary(sATKLib);
     //     sATKLib = nsnull;
     // }
 }
 
 NS_IMETHODIMP
+nsApplicationAccessibleWrap::GetName(nsAString& aName)
+{
+  // ATK doesn't provide a way to obtain an application name (for example,
+  // Firefox or Thunderbird) like IA2 does. Thus let's return an application
+  // name as accessible name that was used to get a branding name (for example,
+  // Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
+  return GetAppName(aName);
+}
+
+NS_IMETHODIMP
 nsApplicationAccessibleWrap::GetNativeInterface(void **aOutAccessible)
 {
     *aOutAccessible = nsnull;
 
     if (!mAtkObject) {
         mAtkObject =
             reinterpret_cast<AtkObject *>
                             (g_object_new(MAI_TYPE_ATK_OBJECT, NULL));
--- a/accessible/src/atk/nsApplicationAccessibleWrap.h
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.h
@@ -52,16 +52,18 @@ public:
 public:
     nsApplicationAccessibleWrap();
     virtual ~nsApplicationAccessibleWrap();
 
     // nsAccessNode
     virtual bool Init();
 
     // nsAccessible
+    NS_IMETHOD GetName(nsAString &aName);
+
     virtual bool AppendChild(nsAccessible* aChild);
     virtual bool RemoveChild(nsAccessible* aChild);
 
     // return the atk object for app root accessible
     NS_IMETHOD GetNativeInterface(void **aOutAccessible);
 };
 
 #endif   /* __NS_APP_ROOT_ACCESSIBLE_H__ */
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -675,18 +675,17 @@ NotificationController::CreateTextChange
     return;
 
   nsHyperTextAccessible* textAccessible = container->AsHyperText();
   if (!textAccessible)
     return;
 
   // Don't fire event for the first html:br in an editor.
   if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
-    nsCOMPtr<nsIEditor> editor;
-    textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
+    nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
     if (editor) {
       bool isEmpty = false;
       editor->GetDocumentIsEmpty(&isEmpty);
       if (isEmpty)
         return;
     }
   }
 
--- a/accessible/src/base/nsAccessible.cpp
+++ b/accessible/src/base/nsAccessible.cpp
@@ -964,20 +964,18 @@ void nsAccessible::GetBoundsRect(nsRect&
   // This is an ancestor frame that will incompass all frames for this content node.
   // We need the relative parent so we can get absolute screen coordinates
   nsIFrame *ancestorFrame = firstFrame;
 
   while (ancestorFrame) {  
     *aBoundingFrame = ancestorFrame;
     // If any other frame type, we only need to deal with the primary frame
     // Otherwise, there may be more frames attached to the same content node
-    if (!nsCoreUtils::IsCorrectFrameType(ancestorFrame,
-                                         nsGkAtoms::inlineFrame) &&
-        !nsCoreUtils::IsCorrectFrameType(ancestorFrame,
-                                         nsGkAtoms::textFrame))
+    if (ancestorFrame->GetType() != nsGkAtoms::inlineFrame &&
+        ancestorFrame->GetType() != nsGkAtoms::textFrame)
       break;
     ancestorFrame = ancestorFrame->GetParent();
   }
 
   nsIFrame *iterFrame = firstFrame;
   nsCOMPtr<nsIContent> firstContent(mContent);
   nsIContent* iterContent = firstContent;
   PRInt32 depth = 0;
@@ -991,18 +989,17 @@ void nsAccessible::GetBoundsRect(nsRect&
     currFrameBounds +=
       iterFrame->GetParent()->GetOffsetToExternal(*aBoundingFrame);
 
     // Add this frame's bounds to total
     aTotalBounds.UnionRect(aTotalBounds, currFrameBounds);
 
     nsIFrame *iterNextFrame = nsnull;
 
-    if (nsCoreUtils::IsCorrectFrameType(iterFrame,
-                                        nsGkAtoms::inlineFrame)) {
+    if (iterFrame->GetType() == nsGkAtoms::inlineFrame) {
       // Only do deeper bounds search if we're on an inline frame
       // Inline frames can contain larger frames inside of them
       iterNextFrame = iterFrame->GetFirstPrincipalChild();
     }
 
     if (iterNextFrame) 
       ++depth;  // Child was found in code above this: We are going deeper in this iteration of the loop
     else {  
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -210,16 +210,21 @@ public:
 
   /**
    * Return the states of accessible, not taking into account ARIA states.
    * Use State() to get complete set of states.
    */
   virtual PRUint64 NativeState();
 
   /**
+   * Return bit set of invisible and offscreen states.
+   */
+  PRUint64 VisibilityState();
+
+  /**
    * Returns attributes for accessible without explicitly setted ARIA
    * attributes.
    */
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 
   /**
    * Used by ChildAtPoint() method to get direct or deepest child at point.
    */
@@ -697,18 +702,16 @@ protected:
   /**
    * Return ARIA role (helper method).
    */
   mozilla::a11y::role ARIARoleInternal();
 
   virtual nsIFrame* GetBoundsFrame();
   virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
 
-  PRUint64 VisibilityState(); 
-
   //////////////////////////////////////////////////////////////////////////////
   // Name helpers
 
   /**
    * Compute the name of HTML node.
    */
   nsresult GetHTMLName(nsAString& aName);
 
--- a/accessible/src/base/nsCoreUtils.cpp
+++ b/accessible/src/base/nsCoreUtils.cpp
@@ -498,27 +498,16 @@ nsCoreUtils::IsErrorPage(nsIDocument *aD
   uri->GetPath(path);
 
   NS_NAMED_LITERAL_CSTRING(neterror, "neterror");
   NS_NAMED_LITERAL_CSTRING(certerror, "certerror");
 
   return StringBeginsWith(path, neterror) || StringBeginsWith(path, certerror);
 }
 
-bool
-nsCoreUtils::IsCorrectFrameType(nsIFrame *aFrame, nsIAtom *aAtom)
-{
-  NS_ASSERTION(aFrame != nsnull,
-               "aFrame is null in call to IsCorrectFrameType!");
-  NS_ASSERTION(aAtom != nsnull,
-               "aAtom is null in call to IsCorrectFrameType!");
-  
-  return aFrame->GetType() == aAtom;
-}
-
 already_AddRefed<nsIDOMNode>
 nsCoreUtils::GetDOMNodeForContainer(nsIDocShellTreeItem *aContainer)
 {
   nsCOMPtr<nsIDocShell> shell = do_QueryInterface(aContainer);
 
   nsCOMPtr<nsIContentViewer> cv;
   shell->GetContentViewer(getter_AddRefs(cv));
 
--- a/accessible/src/base/nsCoreUtils.h
+++ b/accessible/src/base/nsCoreUtils.h
@@ -237,24 +237,16 @@ public:
   static bool IsTabDocument(nsIDocument* aDocumentNode);
 
   /**
    * Return true if the given document is an error page.
    */
   static bool IsErrorPage(nsIDocument *aDocument);
 
   /**
-   * Retrun true if the type of given frame equals to the given frame type.
-   *
-   * @param aFrame  the frame
-   * @param aAtom   the frame type
-   */
-  static bool IsCorrectFrameType(nsIFrame* aFrame, nsIAtom* aAtom);
-
-  /**
    * Return presShell for the document containing the given DOM node.
    */
   static nsIPresShell *GetPresShellFor(nsINode *aNode)
   {
     return aNode->OwnerDoc()->GetShell();
   }
 
   /**
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -328,18 +328,17 @@ nsDocAccessible::NativeState()
     state |= states::BUSY;
 
   nsIFrame* frame = GetFrame();
   if (!frame ||
       !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
     state |= states::INVISIBLE | states::OFFSCREEN;
   }
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   state |= editor ? states::EDITABLE : states::READONLY;
 
   return state;
 }
 
 // nsAccessible public method
 void
 nsDocAccessible::ApplyARIAState(PRUint64* aState)
@@ -548,47 +547,42 @@ nsDocAccessible::GetVirtualCursor(nsIAcc
     mVirtualCursor = new nsAccessiblePivot(this);
     mVirtualCursor->AddObserver(this);
   }
 
   NS_ADDREF(*aVirtualCursor = mVirtualCursor);
   return NS_OK;
 }
 
-// nsIAccessibleHyperText method
-NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+// nsHyperTextAccessible method
+already_AddRefed<nsIEditor>
+nsDocAccessible::GetEditor() const
 {
-  NS_ENSURE_ARG_POINTER(aEditor);
-  *aEditor = nsnull;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   // Check if document is editable (designMode="on" case). Otherwise check if
   // the html:body (for HTML document case) or document element is editable.
   if (!mDocument->HasFlag(NODE_IS_EDITABLE) &&
       !mContent->HasFlag(NODE_IS_EDITABLE))
-    return NS_OK;
+    return nsnull;
 
   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
   if (!editingSession)
-    return NS_OK; // No editing session interface
+    return nsnull; // No editing session interface
 
   nsCOMPtr<nsIEditor> editor;
   editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor));
-  if (!editor) {
-    return NS_OK;
-  }
-  bool isEditable;
+  if (!editor)
+    return nsnull;
+
+  bool isEditable = false;
   editor->GetIsDocumentEditable(&isEditable);
-  if (isEditable) {
-    NS_ADDREF(*aEditor = editor);
-  }
-  return NS_OK;
+  if (isEditable)
+    return editor.forget();
+
+  return nsnull;
 }
 
 // nsDocAccessible public method
 nsAccessible*
 nsDocAccessible::GetAccessible(nsINode* aNode) const
 {
   nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode);
 
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -128,18 +128,18 @@ public:
   virtual void ApplyARIAState(PRUint64* aState);
 
   virtual void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry);
 
 #ifdef DEBUG_ACCDOCMGR
   virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
 #endif
 
-  // nsIAccessibleText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsDocAccessible
 
   /**
    * Return presentation shell for this document accessible.
    */
   nsIPresShell* PresShell() const { return mPresShell; }
 
--- a/accessible/src/html/nsHTMLFormControlAccessible.cpp
+++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp
@@ -539,39 +539,40 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible:
     if ( element ) {
       return element->Focus();
     }
     return NS_ERROR_FAILURE;
   }
   return NS_ERROR_INVALID_ARG;
 }
 
-NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsHTMLTextFieldAccessible::GetEditor() const
 {
-  *aEditor = nsnull;
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent));
-  NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
+  if (!editableElt)
+    return nsnull;
 
   // nsGenericHTMLElement::GetEditor has a security check.
   // Make sure we're not restricted by the permissions of
   // whatever script is currently running.
   nsCOMPtr<nsIJSContextStack> stack =
     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
   bool pushed = stack && NS_SUCCEEDED(stack->Push(nsnull));
 
   nsCOMPtr<nsIEditor> editor;
-  nsresult rv = editableElt->GetEditor(aEditor);
+  editableElt->GetEditor(getter_AddRefs(editor));
 
   if (pushed) {
     JSContext* cx;
     stack->Pop(&cx);
     NS_ASSERTION(!cx, "context should be null");
   }
 
-  return rv;
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLTextFieldAccessible: Widgets
 
 bool
 nsHTMLTextFieldAccessible::IsWidget() const
 {
--- a/accessible/src/html/nsHTMLFormControlAccessible.h
+++ b/accessible/src/html/nsHTMLFormControlAccessible.h
@@ -133,18 +133,18 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& _retval); 
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 State();
   virtual PRUint64 NativeState();
 
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -42,16 +42,17 @@
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsDocAccessible.h"
 #include "nsTextAttrs.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsIClipboard.h"
+#include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
@@ -160,18 +161,17 @@ nsHyperTextAccessible::NativeRole()
   return roles::TEXT_CONTAINER; // In ATK this works
 }
 
 PRUint64
 nsHyperTextAccessible::NativeState()
 {
   PRUint64 states = nsAccessibleWrap::NativeState();
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   if (editor) {
     PRUint32 flags;
     editor->GetFlags(&flags);
     if (0 == (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
       states |= states::EDITABLE;
     }
   } else if (mContent->Tag() == nsGkAtoms::article) {
     // We want <article> to behave like a document in terms of readonly state.
@@ -705,18 +705,17 @@ nsHyperTextAccessible::HypertextOffsetsT
   *aEndNode = nsnull;
 
   NS_ENSURE_ARG_POINTER(aEndOffset);
   *aEndOffset = -1;
 
   // If the given offsets are 0 and associated editor is empty then return
   // collapsed range with editor root element as range container.
   if (aStartHTOffset == 0 && aEndHTOffset == 0) {
-    nsCOMPtr<nsIEditor> editor;
-    GetAssociatedEditor(getter_AddRefs(editor));
+    nsCOMPtr<nsIEditor> editor = GetEditor();
     if (editor) {
       bool isEmpty = false;
       editor->GetDocumentIsEmpty(&isEmpty);
       if (isEmpty) {
         nsCOMPtr<nsIDOMElement> editorRootElm;
         editor->GetRootElement(getter_AddRefs(editorRootElm));
 
         nsCOMPtr<nsIDOMNode> editorRoot(do_QueryInterface(editorRootElm));
@@ -1148,25 +1147,24 @@ nsHyperTextAccessible::GetTextAttributes
                               accAtOffsetIdx);
   nsresult rv = textAttrsMgr.GetAttributes(*aAttributes, &startOffset,
                                            &endOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Compute spelling attributes on text accessible only.
   nsIFrame *offsetFrame = accAtOffset->GetFrame();
   if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) {
-    nsCOMPtr<nsIDOMNode> node = accAtOffset->DOMNode();
-
     PRInt32 nodeOffset = 0;
     nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc,
                                           &nodeOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set 'misspelled' text attribute.
-    rv = GetSpellTextAttribute(node, nodeOffset, &startOffset, &endOffset,
+    rv = GetSpellTextAttribute(accAtOffset->GetNode(), nodeOffset,
+                               &startOffset, &endOffset,
                                aAttributes ? *aAttributes : nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
   return NS_OK;
 }
@@ -1450,118 +1448,121 @@ NS_IMETHODIMP nsHyperTextAccessible::Set
     return InsertText(aText, 0);
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::InsertText(const nsAString &aText, PRInt32 aPosition)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
 
   nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(editor));
   NS_ENSURE_STATE(peditor);
 
   nsresult rv = SetSelectionRange(aPosition, aPosition);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return peditor->InsertText(aText);
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::CopyText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Copy();
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::CutText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Cut();
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->DeleteSelection(nsIEditor::eNone);
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::PasteText(PRInt32 aPosition)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aPosition, aPosition);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Paste(nsIClipboard::kGlobalClipboard);
 }
 
-NS_IMETHODIMP
-nsHyperTextAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsHyperTextAccessible::GetEditor() const
 {
-  NS_ENSURE_ARG_POINTER(aEditor);
-  *aEditor = nsnull;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
     // If we're inside an editable container, then return that container's editor
-    nsCOMPtr<nsIAccessible> ancestor, current = this;
-    while (NS_SUCCEEDED(current->GetParent(getter_AddRefs(ancestor))) && ancestor) {
-      nsRefPtr<nsHyperTextAccessible> ancestorTextAccessible;
-      ancestor->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
-                               getter_AddRefs(ancestorTextAccessible));
-      if (ancestorTextAccessible) {
+    nsAccessible* ancestor = Parent();
+    while (ancestor) {
+      nsHyperTextAccessible* hyperText = ancestor->AsHyperText();
+      if (hyperText) {
         // Recursion will stop at container doc because it has its own impl
-        // of GetAssociatedEditor()
-        return ancestorTextAccessible->GetAssociatedEditor(aEditor);
+        // of GetEditor()
+        return hyperText->GetEditor();
       }
-      current = ancestor;
+
+      ancestor = ancestor->Parent();
     }
-    return NS_OK;
+
+    return nsnull;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
     nsCoreUtils::GetDocShellTreeItemFor(mContent);
   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(docShellTreeItem));
   if (!editingSession)
-    return NS_OK; // No editing session interface
-
-  NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE);
-  nsIDocument* docNode = mDoc->GetDocumentNode();
-  NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE);
+    return nsnull; // No editing session interface
 
   nsCOMPtr<nsIEditor> editor;
-  return editingSession->GetEditorForWindow(docNode->GetWindow(), aEditor);
+  nsIDocument* docNode = mDoc->GetDocumentNode();
+  editingSession->GetEditorForWindow(docNode->GetWindow(),
+                                     getter_AddRefs(editor));
+  return editor.forget();
 }
 
 /**
   * =================== Caret & Selection ======================
   */
 
 nsresult
 nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
@@ -1765,18 +1766,17 @@ nsHyperTextAccessible::GetSelectionDOMRa
     return;
 
   nsISelection* domSel = frameSelection->GetSelection(aType);
   if (!domSel)
     return;
 
   nsCOMPtr<nsINode> startNode = GetNode();
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   if (editor) {
     nsCOMPtr<nsIDOMElement> editorRoot;
     editor->GetRootElement(getter_AddRefs(editorRoot));
     startNode = do_QueryInterface(editorRoot);
   }
 
   if (!startNode)
     return;
@@ -1784,23 +1784,21 @@ nsHyperTextAccessible::GetSelectionDOMRa
   PRUint32 childCount = startNode->GetChildCount();
   nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(domSel));
   nsresult rv = privSel->
     GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
   NS_ENSURE_SUCCESS(rv,);
 
   // Remove collapsed ranges
   PRUint32 numRanges = aRanges->Length();
-  for (PRUint32 count = 0; count < numRanges; count ++) {
-    bool isCollapsed = false;
-    (*aRanges)[count]->GetCollapsed(&isCollapsed);
-    if (isCollapsed) {
-      aRanges->RemoveElementAt(count);
+  for (PRUint32 idx = 0; idx < numRanges; idx ++) {
+    if ((*aRanges)[idx]->Collapsed()) {
+      aRanges->RemoveElementAt(idx);
       --numRanges;
-      --count;
+      --idx;
     }
   }
 }
 
 /*
  * Gets the number of selected regions.
  */
 NS_IMETHODIMP
@@ -1832,39 +1830,29 @@ nsHyperTextAccessible::GetSelectionBound
   GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
     return NS_ERROR_INVALID_ARG;
 
   nsRange* range = ranges[aSelectionNum];
 
-  // Get start point
-  nsCOMPtr<nsIDOMNode> startDOMNode;
-  range->GetStartContainer(getter_AddRefs(startDOMNode));
-  nsCOMPtr<nsINode> startNode(do_QueryInterface(startDOMNode));
-  PRInt32 startOffset = 0;
-  range->GetStartOffset(&startOffset);
+  // Get start and end points.
+  nsINode* startNode = range->GetStartParent();
+  nsINode* endNode = range->GetEndParent();
+  PRInt32 startOffset = range->StartOffset(), endOffset = range->EndOffset();
 
-  // Get end point
-  nsCOMPtr<nsIDOMNode> endDOMNode;
-  range->GetEndContainer(getter_AddRefs(endDOMNode));
-  nsCOMPtr<nsINode> endNode(do_QueryInterface(endDOMNode));
-  PRInt32 endOffset = 0;
-  range->GetEndOffset(&endOffset);
-
-  PRInt16 rangeCompareResult = 0;
-  nsresult rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range,
-                                             &rangeCompareResult);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (rangeCompareResult < 0) {
-    // Make sure start is before end, by swapping offsets
-    // This occurs when the user selects backwards in the text
-    startNode.swap(endNode);
+  // Make sure start is before end, by swapping DOM points.  This occurs when
+  // the user selects backwards in the text.
+  PRInt32 rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
+                                                       startNode, startOffset);
+  if (rangeCompare < 0) {
+    nsINode* tempNode = startNode;
+    startNode = endNode;
+    endNode = tempNode;
     PRInt32 tempOffset = startOffset;
     startOffset = endOffset;
     endOffset = tempOffset;
   }
 
   nsAccessible *startAccessible =
     DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
   if (!startAccessible) {
@@ -2319,78 +2307,64 @@ nsHyperTextAccessible::GetDOMPointByFram
 
 // nsHyperTextAccessible
 nsresult
 nsHyperTextAccessible::RangeBoundToHypertextOffset(nsRange *aRange,
                                                    bool aIsStartBound,
                                                    bool aIsStartHTOffset,
                                                    PRInt32 *aHTOffset)
 {
-  nsCOMPtr<nsIDOMNode> DOMNode;
+  nsINode* node = nsnull;
   PRInt32 nodeOffset = 0;
 
-  nsresult rv;
   if (aIsStartBound) {
-    rv = aRange->GetStartContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetStartOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetStartParent();
+    nodeOffset = aRange->StartOffset();
   } else {
-    rv = aRange->GetEndContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetEndOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetEndParent();
+    nodeOffset = aRange->EndOffset();
   }
 
-  nsCOMPtr<nsINode> node(do_QueryInterface(DOMNode));
   nsAccessible *startAcc =
     DOMPointToHypertextOffset(node, nodeOffset, aHTOffset);
 
   if (aIsStartHTOffset && !startAcc)
     *aHTOffset = 0;
 
   return NS_OK;
 }
 
 // nsHyperTextAccessible
 nsresult
-nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
+nsHyperTextAccessible::GetSpellTextAttribute(nsINode* aNode,
                                              PRInt32 aNodeOffset,
                                              PRInt32 *aHTStartOffset,
                                              PRInt32 *aHTEndOffset,
                                              nsIPersistentProperties *aAttributes)
 {
   nsTArray<nsRange*> ranges;
   GetSelectionDOMRanges(nsISelectionController::SELECTION_SPELLCHECK, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (!rangeCount)
     return NS_OK;
 
+  nsCOMPtr<nsIDOMNode> DOMNode = do_QueryInterface(aNode);
   for (PRUint32 index = 0; index < rangeCount; index++) {
     nsRange* range = ranges[index];
 
     PRInt16 result;
-    nsresult rv = range->ComparePoint(aNode, aNodeOffset, &result);
+    nsresult rv = range->ComparePoint(DOMNode, aNodeOffset, &result);
     NS_ENSURE_SUCCESS(rv, rv);
     // ComparePoint checks boundary points, but we need to check that
     // text at aNodeOffset is inside the range.
     // See also bug 460690.
     if (result == 0) {
-      nsCOMPtr<nsIDOMNode> end;
-      rv = range->GetEndContainer(getter_AddRefs(end));
-      NS_ENSURE_SUCCESS(rv, rv);
-      PRInt32 endOffset;
-      rv = range->GetEndOffset(&endOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (aNode == end && aNodeOffset == endOffset) {
+      if (aNode == range->GetEndParent() && aNodeOffset == range->EndOffset())
         result = 1;
-      }
     }
 
     if (result == 1) { // range is before point
       PRInt32 startHTOffset = 0;
       nsresult rv = RangeBoundToHypertextOffset(range, false, true,
                                                 &startHTOffset);
       NS_ENSURE_SUCCESS(rv, rv);
 
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -259,16 +259,24 @@ public:
    *
    * @param  aOffset  [in] the given text offset
    */
   nsAccessible* GetChildAtOffset(PRUint32 aOffset)
   {
     return GetChildAt(GetChildIndexAtOffset(aOffset));
   }
 
+  //////////////////////////////////////////////////////////////////////////////
+  // EditableTextAccessible
+
+  /**
+   * Return the editor associated with the accessible.
+   */
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
+
 protected:
   // nsHyperTextAccessible
 
   /**
    * Transform magic offset into text offset.
    */
   inline PRInt32 ConvertMagicOffset(PRInt32 aOffset)
   {
@@ -404,17 +412,17 @@ protected:
    *
    * @param aIncludeDefAttrs  [in] points whether text attributes having default
    *                          values of attributes should be included
    * @param aSourceNode       [in] the node we start to traverse from
    * @param aStartOffset      [in, out] the start offset
    * @param aEndOffset        [in, out] the end offset
    * @param aAttributes       [out, optional] result attributes
    */
-  nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset,
+  nsresult GetSpellTextAttribute(nsINode* aNode, PRInt32 aNodeOffset,
                                  PRInt32 *aStartOffset,
                                  PRInt32 *aEndOffset,
                                  nsIPersistentProperties *aAttributes);
 
 private:
   /**
    * End text offsets array.
    */
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -268,34 +268,36 @@ nsXFormsEditableAccessible::NativeState(
     bool isRelevant = false;
     rv = sXFormsService->IsRelevant(DOMNode, &isRelevant);
     NS_ENSURE_SUCCESS(rv, state);
     if (isRelevant) {
       state |= states::EDITABLE | states::SELECTABLE_TEXT;
     }
   }
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_TRUE(editor, state);
   PRUint32 flags;
   editor->GetFlags(&flags);
   if (flags & nsIPlaintextEditor::eEditorSingleLineMask)
     state |= states::SINGLE_LINE;
   else
     state |= states::MULTI_LINE;
 
   return state;
 }
 
-NS_IMETHODIMP
-nsXFormsEditableAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsXFormsEditableAccessible::GetEditor() const
 {
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
-  return sXFormsService->GetEditor(DOMNode, aEditor);
+
+  nsCOMPtr<nsIEditor> editor;
+  sXFormsService->GetEditor(DOMNode, getter_AddRefs(editor));
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXFormsSelectableAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXFormsSelectableAccessible::
   nsXFormsSelectableAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
--- a/accessible/src/xforms/nsXFormsAccessible.h
+++ b/accessible/src/xforms/nsXFormsAccessible.h
@@ -139,18 +139,18 @@ public:
  * The class is base for accessible objects for XForms elements that have
  * editable area.
  */
 class nsXFormsEditableAccessible : public nsXFormsAccessible
 {
 public:
   nsXFormsEditableAccessible(nsIContent* aContent, nsDocAccessible* aDoc);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual PRUint64 NativeState();
 };
 
 
 /**
  * The class is base for accessible objects for XForms select and XForms
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -845,24 +845,27 @@ NS_IMETHODIMP nsXULTextFieldAccessible::
 }
 
 bool
 nsXULTextFieldAccessible::CanHaveAnonChildren()
 {
   return false;
 }
 
-NS_IMETHODIMP nsXULTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsXULTextFieldAccessible::GetEditor() const
 {
-  *aEditor = nsnull;
-
   nsCOMPtr<nsIContent> inputField = GetInputField();
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(inputField));
-  NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
-  return editableElt->GetEditor(aEditor);
+  if (!editableElt)
+    return nsnull;
+
+  nsCOMPtr<nsIEditor> editor;
+  editableElt->GetEditor(getter_AddRefs(editor));
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTextFieldAccessible: nsAccessible protected
 
 void
 nsXULTextFieldAccessible::CacheChildren()
 {
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -255,18 +255,18 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
   virtual bool CanHaveAnonChildren();
 
   // ActionAccessible
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -608,14 +608,14 @@ function getNodePrettyName(aNode)
   } catch (e) {
     return "' no node info '";
   }
 }
 
 function getObjAddress(aObj)
 {
   var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
-  var match = exp.exec(aObj.valueOf());
+  var match = exp.exec(aObj.toString());
   if (match)
     return match[1];
 
-  return aObj.valueOf();
+  return aObj.toString();
 }
--- a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
+++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
@@ -10,51 +10,56 @@
   <script type="application/javascript" 
           src="../common.js"></script>
   <script type="application/javascript" 
           src="../role.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
-        var accessible = getApplicationAccessible();
-        if (!accessible) {
-          SimpleTest.finish();
-          return;
-        }
+      var accessible = getApplicationAccessible();
+      if (!accessible) {
+        SimpleTest.finish();
+        return;
+      }
+
+      var bundleServ =
+        Components.classes["@mozilla.org/intl/stringbundle;1"].
+        getService(Components.interfaces.nsIStringBundleService);
+      var brandBundle =
+        bundleServ.createBundle("chrome://branding/locale/brand.properties");
 
-        // nsIAccessible::name
-        var bundleServ = Components.classes["@mozilla.org/intl/stringbundle;1"]
-                         .getService(Components.interfaces.nsIStringBundleService);
-        var bundle = bundleServ.createBundle("chrome://branding/locale/brand.properties");
+      var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
+        getService(Components.interfaces.nsIXULAppInfo);
 
-        var applicationName = "";
-
+      // nsIAccessible::name
+      var applicationName = "";
+      if (LINUX || SOLARIS) {
+        applicationName = appInfo.name;
+      } else {
         try {
-            applicationName = bundle.GetStringFromName("brandShortName");
-        }  catch(e) {
+          applicationName = brandBundle.GetStringFromName("brandShortName");
+        } catch(e) {
         }
 
         if (applicationName == "")
-            applicationName = "Gecko based application";
-
-        is (accessible.name, applicationName, "wrong application accessible name");
-
-        // nsIAccessibleApplication
-        var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
-          getService(Components.interfaces.nsIXULAppInfo);
+          applicationName = "Gecko based application";
+      }
+      is (accessible.name, applicationName, "wrong application accessible name");
 
-        is(accessible.appName, appInfo.name, "Wrong application name");
-        is(accessible.appVersion, appInfo.version, "Wrong application version");
-        is(accessible.platformName, "Gecko", "Wrong platform name");
-        is(accessible.platformVersion, appInfo.platformVersion,
-           "Wrong platform version");
+      // nsIAccessibleApplication
+      is(accessible.appName, appInfo.name, "Wrong application name");
+      is(accessible.appVersion, appInfo.version, "Wrong application version");
+      is(accessible.platformName, "Gecko", "Wrong platform name");
+      is(accessible.platformVersion, appInfo.platformVersion,
+         "Wrong platform version");
 
-        SimpleTest.finish();
+      SimpleTest.finish();
     }
+
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
   </head>
   <body>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=456121"
     title="nsApplicationAccessible::GetName does not return a default value when brand.properties does not exist">
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -299,22 +299,30 @@ function eventQueue(aEventType)
         SimpleTest.finish();
 
       return;
     }
 
     // Start processing of next invoker.
     invoker = this.getNextInvoker();
 
+    this.setEventHandler(invoker);
+
     if (gLogger.isEnabled()) {
       gLogger.logToConsole("Event queue: \n  invoke: " + invoker.getID());
       gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true);
     }
 
-    this.setEventHandler(invoker);
+    var infoText = "Invoke the '" + invoker.getID() + "' test { ";
+    for (var idx = 0; idx < this.mEventSeq.length; idx++) {
+      infoText += this.isEventUnexpected(idx) ? "un" : "";
+      infoText += "expected '" + this.getEventTypeAsString(idx) + "' event; ";
+    }
+    infoText += " }";
+    info(infoText);
 
     if (invoker.invoke() == INVOKER_ACTION_FAILED) {
       // Invoker failed to prepare action, fail and finish tests.
       this.processNextInvoker();
       return;
     }
 
     if (this.areAllEventsUnexpected())
--- a/accessible/tests/mochitest/events/test_focus_autocomplete.xul
+++ b/accessible/tests/mochitest/events/test_focus_autocomplete.xul
@@ -379,24 +379,21 @@
     //gA11yEventDumpID = "eventdump"; // debug stuff
     //gA11yEventDumpToConsole = true; // debug stuff
 
     var gInitQueue = null;
     function initTests()
     {
       if (SEAMONKEY) {
         todo(false, "Skipping this test on SeaMonkey ftb. (Bug 718237)");
+        shutdownAutoComplete();
         SimpleTest.finish();
         return;
       }
 
-      // register 'test-a11y-search' autocomplete search
-      initAutoComplete([ "hello", "hi" ],
-                       [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
-
       gInitQueue = new eventQueue();
       gInitQueue.push(new loadFormAutoComplete("iframe"));
       gInitQueue.push(new initFormAutoCompleteBy("iframe", "hello"));
       gInitQueue.push(new initFormAutoCompleteBy("iframe", "hi"));
       gInitQueue.push(new loadHTML5ListAutoComplete("iframe2"));
       gInitQueue.onFinish = function initQueue_onFinish()
       {
         SimpleTest.executeSoon(doTests);
@@ -456,16 +453,22 @@
       {
         // unregister 'test-a11y-search' autocomplete search
         shutdownAutoComplete();
       }
       gQueue.invoke(); // Will call SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
+
+    // Register 'test-a11y-search' autocomplete search.
+    // XPFE AutoComplete needs to register early.
+    initAutoComplete([ "hello", "hi" ],
+                     [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
     addA11yLoadEvent(initTests);
   ]]>
   </script>
 
   <hbox flex="1" style="overflow: auto;">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=383759"
--- a/accessible/tests/mochitest/states/test_expandable.xul
+++ b/accessible/tests/mochitest/states/test_expandable.xul
@@ -30,20 +30,16 @@
 
   <script type="application/javascript">
   <![CDATA[
     //gA11yEventDumpToConsole = true; // debuggin
 
     var gQueue = null;
     function doTest()
     {
-      // register 'test-a11y-search' autocomplete search
-      initAutoComplete([ "hello", "hi" ],
-                       [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
-
       gQueue = new eventQueue();
 
       gQueue.push(new openCombobox("menulist"));
       gQueue.push(new closeCombobox("menulist"));
 
       todo(false, "Autocompletes don't fire expanded state change events when popup open. See bug 688480!");
       //gQueue.push(new openCombobox("autocomplete"));
       //gQueue.push(new closeCombobox("autocomplete"));
@@ -68,16 +64,22 @@
     function getBrowser()
     {
       return {
         mCurrentBrowser: { engines: new Array() }
       };
     }
 
     SimpleTest.waitForExplicitFinish();
+
+    // Register 'test-a11y-search' autocomplete search.
+    // XPFE AutoComplete needs to register early.
+    initAutoComplete([ "hello", "hi" ],
+                     [ "Beep beep'm beep beep yeah", "Baby you can drive my car" ]);
+
     addA11yLoadEvent(doTest);
   ]]>
   </script>
 
   <hbox style="overflow: auto;" flex="1">
     <body xmlns="http://www.w3.org/1999/xhtml">
       <a target="_blank"
          href="https://bugzilla.mozilla.org/show_bug.cgi?id=467057"
--- a/b2g/chrome/content/webapi.js
+++ b/b2g/chrome/content/webapi.js
@@ -176,67 +176,87 @@ const ContentPanning = {
         this.onTouchMove(evt);
         break;
       case 'mouseup':
         this.onTouchEnd(evt);
         break;
       case 'click':
         evt.stopPropagation();
         evt.preventDefault();
-        evt.target.removeEventListener('click', this, true);
+        
+        let target = evt.target;
+        let view = target.ownerDocument ? target.ownerDocument.defaultView
+                                        : target;
+        view.removeEventListener('click', this, true, true);
         break;
     }
   },
 
   position: new Point(0 , 0),
 
   onTouchStart: function cp_onTouchStart(evt) {
     this.dragging = true;
+    this.panning = false;
+
+    let oldTarget = this.target;
+    [this.target, this.scrollCallback] = this.getPannable(evt.target);
 
     // If there is a pan animation running (from a previous pan gesture) and
     // the user touch back the screen, stop this animation immediatly and
-    // prevent the possible click action.
+    // prevent the possible click action if the touch happens on the same
+    // target.
+    this.preventNextClick = false;
     if (KineticPanning.active) {
       KineticPanning.stop();
-      this.preventNextClick = true;
+
+      if (oldTarget && oldTarget == this.target)
+        this.preventNextClick = true;
     }
 
-    this.scrollCallback = this.getPannable(evt.originalTarget);
+
     this.position.set(evt.screenX, evt.screenY);
     KineticPanning.record(new Point(0, 0), evt.timeStamp);
   },
 
   onTouchEnd: function cp_onTouchEnd(evt) {
     if (!this.dragging)
       return;
     this.dragging = false;
 
     this.onTouchMove(evt);
 
-    let pan = KineticPanning.isPan();
     let click = evt.detail;
-    if (click && (pan || this.preventNextClick))
-      evt.target.addEventListener('click', this, true);
+    if (this.target && click && (this.panning || this.preventNextClick)) {
+      let target = this.target;
+      let view = target.ownerDocument ? target.ownerDocument.defaultView
+                                      : target;
+      view.addEventListener('click', this, true, true);
+    }
 
-    this.preventNextClick = false;
-
-    if (pan)
+    if (this.panning)
       KineticPanning.start(this);
   },
 
   onTouchMove: function cp_onTouchMove(evt) {
     if (!this.dragging || !this.scrollCallback)
       return;
 
     let current = this.position;
     let delta = new Point(evt.screenX - current.x, evt.screenY - current.y);
     current.set(evt.screenX, evt.screenY);
 
     KineticPanning.record(delta, evt.timeStamp);
     this.scrollCallback(delta.scale(-1));
+
+    // If a pan action happens, cancel the active state of the
+    // current target.
+    if (!this.panning && KineticPanning.isPan()) {
+      this.panning = true;
+      this._resetActive();
+    }
   },
 
 
   onKineticBegin: function cp_onKineticBegin(evt) {
   },
 
   onKineticPan: function cp_onKineticPan(delta) {
     return !this.scrollCallback(delta);
@@ -244,39 +264,39 @@ const ContentPanning = {
 
   onKineticEnd: function cp_onKineticEnd() {
     if (!this.dragging)
       this.scrollCallback = null;
   },
 
   getPannable: function cp_getPannable(node) {
     if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML')
-      return null;
+      return [null, null];
 
     let content = node.ownerDocument.defaultView;
     while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) {
       let style = content.getComputedStyle(node, null);
 
       let overflow = [style.getPropertyValue('overflow'),
                       style.getPropertyValue('overflow-x'),
                       style.getPropertyValue('overflow-y')];
 
       let rect = node.getBoundingClientRect();
       let isAuto = (overflow.indexOf('auto') != -1 &&
                    (rect.height < node.scrollHeight ||
                     rect.width < node.scrollWidth));
 
       let isScroll = (overflow.indexOf('scroll') != -1);
       if (isScroll || isAuto)
-        return this._generateCallback(node);
+        return [node, this._generateCallback(node)];
 
       node = node.parentNode;
     }
 
-    return this._generateCallback(content);
+    return [content, this._generateCallback(content)];
   },
 
   _generateCallback: function cp_generateCallback(content) {
     function scroll(delta) {
       if (content instanceof Ci.nsIDOMHTMLElement) {
         let oldX = content.scrollLeft, oldY = content.scrollTop;
         content.scrollLeft += delta.x;
         content.scrollTop += delta.y;
@@ -285,16 +305,29 @@ const ContentPanning = {
       } else {
         let oldX = content.scrollX, oldY = content.scrollY;
         content.scrollBy(delta.x, delta.y);
         let newX = content.scrollX, newY = content.scrollY;
         return (newX != oldX) || (newY != oldY);
       }
     }
     return scroll;
+  },
+
+  get _domUtils() {
+    delete this._domUtils;
+    return this._domUtils = Cc['@mozilla.org/inspector/dom-utils;1']
+                              .getService(Ci.inIDOMUtils);
+  },
+
+  _resetActive: function cp_resetActive() {
+    let root = this.target.ownerDocument || this.target.document;
+
+    const kStateActive = 0x00000001;
+    this._domUtils.setContentState(root.documentElement, kStateActive);
   }
 };
 
 ContentPanning.init();
 
 
 // Min/max velocity of kinetic panning. This is in pixels/millisecond.
 const kMinVelocity = 0.4;
@@ -365,41 +398,43 @@ const KineticPanning = {
     this.target.onKineticBegin();
   },
 
   stop: function kp_stop() {
     if (!this.target)
       return;
 
     this.momentums = [];
+    this.distance.set(0, 0);
 
     this.target.onKineticEnd();
     this.target = null;
   },
 
   momentums: [],
   record: function kp_record(delta, timestamp) {
     this.momentums.push({ 'time': timestamp, 'dx' : delta.x, 'dy' : delta.y });
+    this.distance.add(delta.x, delta.y);
   },
 
-  isPan: function cp_isPan() {
+  get threshold() {
     let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor)
                      .getInterface(Ci.nsIDOMWindowUtils)
                      .displayDPI;
 
     let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi;
 
-    let deltaX = 0;
-    let deltaY = 0;
-    let start = this.momentums[0].time;
-    return this.momentums.slice(1).some(function(momentum) {
-      deltaX += momentum.dx;
-      deltaY += momentum.dy;
-      return (Math.abs(deltaX) > threshold) || (Math.abs(deltaY) > threshold);
-    });
+    delete this.threshold;
+    return this.threshold = threshold;
+  },
+
+  distance: new Point(0, 0),
+  isPan: function cp_isPan() {
+    return (Math.abs(this.distance.x) > this.threshold ||
+            Math.abs(this.distance.y) > this.threshold);
   },
 
   _startAnimation: function kp_startAnimation() {
     let c = kExponentialC;
     function getNextPosition(position, v, a, t) {
       // Important traits for this function:
       //   p(t=0) is 0
       //   p'(t=0) is v0
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1558,59 +1558,16 @@ function delayedStartup(isLoadingBlank, 
   gBrowser.tabContainer.updateVisibility();
 
   gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
 
   var homeButton = document.getElementById("home-button");
   gHomeButton.updateTooltip(homeButton);
   gHomeButton.updatePersonalToolbarStyle(homeButton);
 
-#ifdef HAVE_SHELL_SERVICE
-  // Perform default browser checking (after window opens).
-  var shell = getShellService();
-  if (shell) {
-#ifdef DEBUG
-    var shouldCheck = false;
-#else
-    var shouldCheck = shell.shouldCheckDefaultBrowser;
-#endif
-    var willRecoverSession = false;
-    try {
-      var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
-               getService(Ci.nsISessionStartup);
-      willRecoverSession =
-        (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
-    }
-    catch (ex) { /* never mind; suppose SessionStore is broken */ }
-    if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
-      // Delay the set-default-browser prompt so it doesn't block
-      // initialisation of the session store service.
-      setTimeout(function () {
-        var brandBundle = document.getElementById("bundle_brand");
-        var shellBundle = document.getElementById("bundle_shell");
-
-        var brandShortName = brandBundle.getString("brandShortName");
-        var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
-        var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
-                                                           [brandShortName]);
-        var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
-                                                           [brandShortName]);
-        var checkEveryTime = { value: shouldCheck };
-        var ps = Services.prompt;
-        var rv = ps.confirmEx(window, promptTitle, promptMessage,
-                              ps.STD_YES_NO_BUTTONS,
-                              null, null, null, checkboxLabel, checkEveryTime);
-        if (rv == 0)
-          shell.setDefaultBrowser(true, false);
-        shell.shouldCheckDefaultBrowser = checkEveryTime.value;
-      }, 0);
-    }
-  }
-#endif
-
   // BiDi UI
   gBidiUI = isBidiEnabled();
   if (gBidiUI) {
     document.getElementById("documentDirection-separator").hidden = false;
     document.getElementById("documentDirection-swap").hidden = false;
     document.getElementById("textfieldDirection-separator").hidden = false;
     document.getElementById("textfieldDirection-swap").hidden = false;
   }
@@ -3984,20 +3941,32 @@ var FullScreen = {
         this.mouseoverToggle(true);
       // This is needed if they use the context menu to quit fullscreen
       this._isPopupOpen = false;
 
       this.cleanup();
     }
   },
 
-  exitDomFullScreen : function(e) {
+  exitDomFullScreen : function() {
     document.mozCancelFullScreen();
   },
 
+  handleEvent: function (event) {
+    switch (event.type) {
+      case "deactivate":
+        // We must call exitDomFullScreen asynchronously, since "deactivate" is
+        // dispatched in the middle of the focus manager's window lowering code,
+        // and the focus manager gets confused if we exit fullscreen mode in the
+        // middle of window lowering. See bug 729872.
+        setTimeout(this.exitDomFullScreen.bind(this), 0);
+        break;
+    }
+  },
+
   enterDomFullScreen : function(event) {
     if (!document.mozFullScreen) {
       return;
     }
 
     // We receive "mozfullscreenchange" events for each subdocument which
     // is an ancestor of the document containing the element which requested
     // full-screen. Only add listeners and show warning etc when the event we
@@ -4034,17 +4003,17 @@ var FullScreen = {
 
     // Exit DOM full-screen mode upon open, close, or change tab.
     gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
     gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
     gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
 
     // Exit DOM full-screen mode when the browser window loses focus (ALT+TAB, etc).
     if (gPrefService.getBoolPref("full-screen-api.exit-on-deactivate")) {
-      window.addEventListener("deactivate", this.exitDomFullScreen, true);
+      window.addEventListener("deactivate", this);
     }
 
     // Cancel any "hide the toolbar" animation which is in progress, and make
     // the toolbar hide immediately.
     this._cancelAnimation();
     this.mouseoverToggle(false);
 
     // If there's a full-screen toggler, remove its listeners, so that mouseover
@@ -4069,17 +4038,17 @@ var FullScreen = {
       if (fullScrToggler) {
         fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
         fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
       }
       this.cancelWarning();
       gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
       gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
       gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
-      window.removeEventListener("deactivate", this.exitDomFullScreen, true);
+      window.removeEventListener("deactivate", this);
     }
   },
 
   observe: function(aSubject, aTopic, aData)
   {
     if (aData == "browser.fullscreen.autohide") {
       if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
         gBrowser.mPanelContainer.addEventListener("mousemove",
@@ -5357,29 +5326,33 @@ function setToolbarVisibility(toolbar, i
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
 }
 
 var TabsOnTop = {
   init: function TabsOnTop_init() {
+    this._initialized = true;
     this.syncUI();
     Services.prefs.addObserver(this._prefName, this, false);
   },
 
   uninit: function TabsOnTop_uninit() {
     Services.prefs.removeObserver(this._prefName, this);
   },
 
   toggle: function () {
     this.enabled = !Services.prefs.getBoolPref(this._prefName);
   },
 
   syncUI: function () {
+    if (!this._initialized)
+      return;
+
     let userEnabled = Services.prefs.getBoolPref(this._prefName);
     let enabled = userEnabled && gBrowser.tabContainer.visible;
 
     document.getElementById("cmd_ToggleTabsOnTop")
             .setAttribute("checked", userEnabled);
 
     document.documentElement.setAttribute("tabsontop", enabled);
     document.getElementById("navigator-toolbox").setAttribute("tabsontop", enabled);
@@ -6116,113 +6089,124 @@ function charsetLoadListener(event) {
     if (!gCharsetMenu)
       gCharsetMenu = Cc['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService(Ci.nsICurrentCharsetListener);
     gCharsetMenu.SetCurrentCharset(charset);
     gPrevCharset = gLastBrowserCharset;
     gLastBrowserCharset = charset;
   }
 }
 
-/* Begin Page Style Functions */
-function getAllStyleSheets(frameset) {
-  var styleSheetsArray = Array.slice(frameset.document.styleSheets);
-  for (let i = 0; i < frameset.frames.length; i++) {
-    let frameSheets = getAllStyleSheets(frameset.frames[i]);
-    styleSheetsArray = styleSheetsArray.concat(frameSheets);
-  }
-  return styleSheetsArray;
-}
-
-function stylesheetFillPopup(menuPopup) {
-  var noStyle = menuPopup.firstChild;
-  var persistentOnly = noStyle.nextSibling;
-  var sep = persistentOnly.nextSibling;
-  while (sep.nextSibling)
-    menuPopup.removeChild(sep.nextSibling);
-
-  var styleSheets = getAllStyleSheets(window.content);
-  var currentStyleSheets = {};
-  var styleDisabled = getMarkupDocumentViewer().authorStyleDisabled;
-  var haveAltSheets = false;
-  var altStyleSelected = false;
-
-  for (let i = 0; i < styleSheets.length; ++i) {
-    let currentStyleSheet = styleSheets[i];
-
-    if (!currentStyleSheet.title)
-      continue;
-
-    // Skip any stylesheets whose media attribute doesn't match.
-    if (currentStyleSheet.media.length > 0) {
-      let mediaQueryList = currentStyleSheet.media.mediaText;
-      if (!window.content.matchMedia(mediaQueryList).matches)
+
+var gPageStyleMenu = {
+
+  getAllStyleSheets: function (frameset) {
+    var styleSheetsArray = Array.slice(frameset.document.styleSheets);
+    for (let i = 0; i < frameset.frames.length; i++) {
+      let frameSheets = this.getAllStyleSheets(frameset.frames[i]);
+      styleSheetsArray = styleSheetsArray.concat(frameSheets);
+    }
+    return styleSheetsArray;
+  },
+
+  stylesheetFillPopup: function (menuPopup) {
+    var noStyle = menuPopup.firstChild;
+    var persistentOnly = noStyle.nextSibling;
+    var sep = persistentOnly.nextSibling;
+    while (sep.nextSibling)
+      menuPopup.removeChild(sep.nextSibling);
+
+    var styleSheets = this.getAllStyleSheets(window.content);
+    var currentStyleSheets = {};
+    var styleDisabled = getMarkupDocumentViewer().authorStyleDisabled;
+    var haveAltSheets = false;
+    var altStyleSelected = false;
+
+    for (let i = 0; i < styleSheets.length; ++i) {
+      let currentStyleSheet = styleSheets[i];
+
+      if (!currentStyleSheet.title)
         continue;
-    }
-
-    if (!currentStyleSheet.disabled)
-      altStyleSelected = true;
-
-    haveAltSheets = true;
-
-    let lastWithSameTitle = null;
-    if (currentStyleSheet.title in currentStyleSheets)
-      lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];
-
-    if (!lastWithSameTitle) {
-      let menuItem = document.createElement("menuitem");
-      menuItem.setAttribute("type", "radio");
-      menuItem.setAttribute("label", currentStyleSheet.title);
-      menuItem.setAttribute("data", currentStyleSheet.title);
-      menuItem.setAttribute("checked", !currentStyleSheet.disabled && !styleDisabled);
-      menuPopup.appendChild(menuItem);
-      currentStyleSheets[currentStyleSheet.title] = menuItem;
-    } else if (currentStyleSheet.disabled) {
-      lastWithSameTitle.removeAttribute("checked");
-    }
-  }
-
-  noStyle.setAttribute("checked", styleDisabled);
-  persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
-  persistentOnly.hidden = (window.content.document.preferredStyleSheetSet) ? haveAltSheets : false;
-  sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
-  return true;
-}
-
-function stylesheetInFrame(frame, title) {
-  return Array.some(frame.document.styleSheets,
-                    function (stylesheet) stylesheet.title == title);
-}
-
-function stylesheetSwitchFrame(frame, title) {
-  var docStyleSheets = frame.document.styleSheets;
-
-  for (let i = 0; i < docStyleSheets.length; ++i) {
-    let docStyleSheet = docStyleSheets[i];
-
-    if (title == "_nostyle")
-      docStyleSheet.disabled = true;
-    else if (docStyleSheet.title)
-      docStyleSheet.disabled = (docStyleSheet.title != title);
-    else if (docStyleSheet.disabled)
-      docStyleSheet.disabled = false;
-  }
-}
-
-function stylesheetSwitchAll(frameset, title) {
-  if (!title || title == "_nostyle" || stylesheetInFrame(frameset, title))
-    stylesheetSwitchFrame(frameset, title);
-
-  for (let i = 0; i < frameset.frames.length; i++)
-    stylesheetSwitchAll(frameset.frames[i], title);
-}
-
-function setStyleDisabled(disabled) {
-  getMarkupDocumentViewer().authorStyleDisabled = disabled;
-}
-/* End of the Page Style functions */
+
+      // Skip any stylesheets whose media attribute doesn't match.
+      if (currentStyleSheet.media.length > 0) {
+        let mediaQueryList = currentStyleSheet.media.mediaText;
+        if (!window.content.matchMedia(mediaQueryList).matches)
+          continue;
+      }
+
+      if (!currentStyleSheet.disabled)
+        altStyleSelected = true;
+
+      haveAltSheets = true;
+
+      let lastWithSameTitle = null;
+      if (currentStyleSheet.title in currentStyleSheets)
+        lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];
+
+      if (!lastWithSameTitle) {
+        let menuItem = document.createElement("menuitem");
+        menuItem.setAttribute("type", "radio");
+        menuItem.setAttribute("label", currentStyleSheet.title);
+        menuItem.setAttribute("data", currentStyleSheet.title);
+        menuItem.setAttribute("checked", !currentStyleSheet.disabled && !styleDisabled);
+        menuPopup.appendChild(menuItem);
+        currentStyleSheets[currentStyleSheet.title] = menuItem;
+      } else if (currentStyleSheet.disabled) {
+        lastWithSameTitle.removeAttribute("checked");
+      }
+    }
+
+    noStyle.setAttribute("checked", styleDisabled);
+    persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
+    persistentOnly.hidden = (window.content.document.preferredStyleSheetSet) ? haveAltSheets : false;
+    sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
+    return true;
+  },
+
+  stylesheetInFrame: function (frame, title) {
+    return Array.some(frame.document.styleSheets,
+                      function (stylesheet) stylesheet.title == title);
+  },
+
+  stylesheetSwitchFrame: function (frame, title) {
+    var docStyleSheets = frame.document.styleSheets;
+
+    for (let i = 0; i < docStyleSheets.length; ++i) {
+      let docStyleSheet = docStyleSheets[i];
+
+      if (title == "_nostyle")
+        docStyleSheet.disabled = true;
+      else if (docStyleSheet.title)
+        docStyleSheet.disabled = (docStyleSheet.title != title);
+      else if (docStyleSheet.disabled)
+        docStyleSheet.disabled = false;
+    }
+  },
+
+  stylesheetSwitchAll: function (frameset, title) {
+    if (!title || title == "_nostyle" || this.stylesheetInFrame(frameset, title))
+      this.stylesheetSwitchFrame(frameset, title);
+
+    for (let i = 0; i < frameset.frames.length; i++)
+      this.stylesheetSwitchAll(frameset.frames[i], title);
+  },
+
+  setStyleDisabled: function (disabled) {
+    getMarkupDocumentViewer().authorStyleDisabled = disabled;
+  },
+};
+
+/* Legacy global page-style functions */
+var getAllStyleSheets     = gPageStyleMenu.getAllStyleSheets;
+var stylesheetFillPopup   = gPageStyleMenu.stylesheetFillPopup;
+var stylesheetInFrame     = gPageStyleMenu.stylesheetInFrame;
+var stylesheetSwitchFrame = gPageStyleMenu.stylesheetSwitchFrame;
+var stylesheetSwitchAll   = gPageStyleMenu.stylesheetSwitchAll;
+var setStyleDisabled      = gPageStyleMenu.setStyleDisabled;
+
 
 var BrowserOffline = {
   _inited: false,
 
   /////////////////////////////////////////////////////////////////////////////
   // BrowserOffline Public Methods
   init: function ()
   {
--- a/browser/base/content/test/browser_aboutSyncProgress.js
+++ b/browser/base/content/test/browser_aboutSyncProgress.js
@@ -1,14 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://services-sync/main.js");
 
 let gTests = [ {
   desc: "Makes sure the progress bar appears if firstSync pref is set",
   setup: function () {
     Services.prefs.setCharPref("services.sync.firstSync", "newAccount");
   },
   run: function () {
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -423,16 +423,59 @@ BrowserGlue.prototype = {
 
           browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
         })
       });
     }
 
     let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL");
     Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet);
+
+    // Perform default browser checking.
+    var shell;
+    try {
+      shell = Components.classes["@mozilla.org/browser/shell-service;1"]
+        .getService(Components.interfaces.nsIShellService);
+    } catch (e) { }
+    if (shell) {
+#ifdef DEBUG
+      var shouldCheck = false;
+#else
+      var shouldCheck = shell.shouldCheckDefaultBrowser;
+#endif
+      var willRecoverSession = false;
+      try {
+        var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
+                 getService(Ci.nsISessionStartup);
+        willRecoverSession =
+          (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
+      }
+      catch (ex) { /* never mind; suppose SessionStore is broken */ }
+      if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
+        Services.tm.mainThread.dispatch(function() {
+          var brandBundle = win.document.getElementById("bundle_brand");
+          var shellBundle = win.document.getElementById("bundle_shell");
+  
+          var brandShortName = brandBundle.getString("brandShortName");
+          var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
+          var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
+                                                             [brandShortName]);
+          var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
+                                                             [brandShortName]);
+          var checkEveryTime = { value: shouldCheck };
+          var ps = Services.prompt;
+          var rv = ps.confirmEx(win, promptTitle, promptMessage,
+                                ps.STD_YES_NO_BUTTONS,
+                                null, null, null, checkboxLabel, checkEveryTime);
+          if (rv == 0)
+            shell.setDefaultBrowser(true, false);
+          shell.shouldCheckDefaultBrowser = checkEveryTime.value;
+        }, Ci.nsIThread.DISPATCH_NORMAL);
+      }
+    }
   },
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -162,120 +162,87 @@ PlacesViewBase.prototype = {
 
   destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
     this._contextMenuShown = false;
     if (window.content)
       window.content.focus();
   },
 
   _cleanPopup: function PVB_cleanPopup(aPopup) {
-    // Remove places popup children and update markers to keep track of
-    // their indices.
-    let start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
-    let end = aPopup._endMarker != -1 ? aPopup._endMarker :
-                                        aPopup.childNodes.length;
-    let items = [];
-
-    // Automatically adjust the start and the end markers.
-    let firstNonStaticNodeFound = false;
-    for (let i = start; i < end; ++i) {
-      let item = aPopup.childNodes[i];
-      if (item.getAttribute("builder") == "end") {
-        // we need to do this for menus that have static content at the end but
-        // are initially empty, eg. the history menu, we need to know where to
-        // start inserting new items.
-        aPopup._endMarker = i;
-        break;
-      }
-
-      if (item._placesNode) {
-        items.push(item);
-        firstNonStaticNodeFound = true;
-      }
-      else {
-        // This is static content.
-        if (!firstNonStaticNodeFound) {
-          // We are at the beginning of the popup, in static content.
-          // The markers are initialized in menu.xml, in the base binding.
-          aPopup._startMarker++;
-        }
-        else {
-          // We are at the end of the popup, after places nodes
-          aPopup._endMarker = i;
-          break;
-        }
-      }
-    }
-
-    for (let i = 0; i < items.length; ++i) {
-      aPopup.removeChild(items[i]);
-      if (aPopup._endMarker != -1)
-        aPopup._endMarker--;
+    // Remove Places nodes from the popup.
+    let child = aPopup._startMarker;
+    while (child.nextSibling != aPopup._endMarker) {
+      if (child.nextSibling._placesNode)
+        aPopup.removeChild(child.nextSibling);
+      else
+        child = child.nextSibling;
     }
   },
 
   _rebuildPopup: function PVB__rebuildPopup(aPopup) {
-    this._cleanPopup(aPopup);
-
     let resultNode = aPopup._placesNode;
     if (!resultNode.containerOpen)
       return;
 
     if (resultNode._feedURI) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem) {
-        aPopup._emptyMenuItem.hidden = true;
-      }
+      this._setEmptyPopupStatus(aPopup, false);
       aPopup._built = true;
       this._populateLivemarkPopup(aPopup);
       return;
     }
 
+    this._cleanPopup(aPopup);
+
     let cc = resultNode.childCount;
     if (cc > 0) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem)
-        aPopup._emptyMenuItem.hidden = true;
+      this._setEmptyPopupStatus(aPopup, false);
 
       for (let i = 0; i < cc; ++i) {
         let child = resultNode.getChild(i);
         this._insertNewItemToPopup(child, aPopup, null);
       }
     }
     else {
-      aPopup.setAttribute("emptyplacesresult", "true");
-      // This menu is empty.  If there is no static content, add
-      // an element to show it is empty.
-      if (aPopup._startMarker == -1 && aPopup._endMarker == -1)
-        this._showEmptyMenuItem(aPopup);
+      this._setEmptyPopupStatus(aPopup, true);
     }
     aPopup._built = true;
   },
 
   _removeChild: function PVB__removeChild(aChild) {
     // If document.popupNode pointed to this child, null it out,
     // otherwise controller's command-updating may rely on the removed
     // item still being "selected".
     if (document.popupNode == aChild)
       document.popupNode = null;
 
     aChild.parentNode.removeChild(aChild);
   },
 
-  _showEmptyMenuItem: function PVB__showEmptyMenuItem(aPopup) {
-    if (aPopup._emptyMenuItem) {
-      aPopup._emptyMenuItem.hidden = false;
-      return;
+  _setEmptyPopupStatus:
+  function PVB__setEmptyPopupStatus(aPopup, aEmpty) {
+    if (!aPopup._emptyMenuitem) {
+      let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
+      aPopup._emptyMenuitem = document.createElement("menuitem");
+      aPopup._emptyMenuitem.setAttribute("label", label);
+      aPopup._emptyMenuitem.setAttribute("disabled", true);
     }
 
-    let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
-    aPopup._emptyMenuItem = document.createElement("menuitem");
-    aPopup._emptyMenuItem.setAttribute("label", label);
-    aPopup._emptyMenuItem.setAttribute("disabled", true);
-    aPopup.appendChild(aPopup._emptyMenuItem);
+    if (aEmpty) {
+      aPopup.setAttribute("emptyplacesresult", "true");
+      // Don't add the menuitem if there is static content.
+      if (!aPopup._startMarker.previousSibling &&
+          !aPopup._endMarker.nextSibling)
+        aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
+    }
+    else {
+      aPopup.removeAttribute("emptyplacesresult");
+      try {
+        aPopup.removeChild(aPopup._emptyMenuitem);
+      } catch (ex) {}
+    }
   },
 
   _createMenuItemForPlacesNode:
   function PVB__createMenuItemForPlacesNode(aPlacesNode) {
     delete aPlacesNode._DOMElement;
     let element;
     let type = aPlacesNode.type;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
@@ -303,33 +270,38 @@ PlacesViewBase.prototype = {
             element.setAttribute("hostContainer", "true");
         }
         else if (itemId != -1) {
           PlacesUtils.livemarks.getLivemark(
             { id: itemId },
             function (aStatus, aLivemark) {
               if (Components.isSuccessCode(aStatus)) {
                 element.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+                // OS X native menubar doesn't track list-style-images since
+                // it doesn't have a frame (bug 733415).  Thus enforce updating.
+                element.setAttribute("image", "");
+                element.removeAttribute("image");
+#endif
                 // Set an expando on the node, controller will use it to build
                 // its metadata.
                 aPlacesNode._feedURI = aLivemark.feedURI;
                 aPlacesNode._siteURI = aLivemark.siteURI;
               }
             }
           );
         }
 
         let popup = document.createElement("menupopup");
         popup._placesNode = PlacesUtils.asContainer(aPlacesNode);
-        if (this._nativeView) {
-          popup._startMarker = -1;
-          popup._endMarker = -1;
+
+        if (!this._nativeView) {
+          popup.setAttribute("placespopup", "true");
         }
-        else
-          popup.setAttribute("placespopup", "true");
+
 #ifdef XP_MACOSX
         // No context menu on mac.
         popup.setAttribute("context", "placesContext");
 #endif
         element.appendChild(popup);
         element.className = "menu-iconic bookmark-item";
 
         aPlacesNode._DOMElement = popup;
@@ -349,50 +321,30 @@ PlacesViewBase.prototype = {
       aPlacesNode._DOMElement = element;
 
     return element;
   },
 
   _insertNewItemToPopup:
   function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) {
     let element = this._createMenuItemForPlacesNode(aNewChild);
-
-    if (aBefore) {
-      aPopup.insertBefore(element, aBefore);
-    }
-    else {
-      // Add the new element to the menu.  If there is static content at
-      // the end of the menu, add the element before that.  Otherwise,
-      // just add to the end.
-      if (aPopup._endMarker != -1) {
-        let lastElt = aPopup.childNodes[aPopup._endMarker];
-        aPopup.insertBefore(element, lastElt);
-      }
-      else {
-        aPopup.appendChild(element);
-      }
-    }
-
-    if (aPopup._endMarker != -1)
-      aPopup._endMarker++;
-
+    let before = aBefore || aPopup._endMarker;
+    aPopup.insertBefore(element, before);
     return element;
   },
 
   _setLivemarkSiteURIMenuItem:
   function PVB__setLivemarkSiteURIMenuItem(aPopup) {
     let siteUrl = aPopup._placesNode._siteURI ? aPopup._placesNode._siteURI.spec
                                               : null;
     if (!siteUrl && aPopup._siteURIMenuitem) {
       aPopup.removeChild(aPopup._siteURIMenuitem);
       aPopup._siteURIMenuitem = null;
-      aPopup._startMarker--;
       aPopup.removeChild(aPopup._siteURIMenuseparator);
       aPopup._siteURIMenuseparator = null;
-      aPopup._startMarker--;
     }
     else if (siteUrl && !aPopup._siteURIMenuitem) {
       // Add "Open (Feed Name)" menuitem.
       aPopup._siteURIMenuitem = document.createElement("menuitem");
       aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
       aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
       aPopup._siteURIMenuitem.setAttribute("oncommand",
         "openUILink(this.getAttribute('targetURI'), event);");
@@ -402,65 +354,58 @@ PlacesViewBase.prototype = {
       // Note: stopPropagation is needed to avoid serving middle-click
       // with BT_onClick that would open all items in tabs.
       aPopup._siteURIMenuitem.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       let label =
         PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
                                          [aPopup.parentNode.getAttribute("label")])
       aPopup._siteURIMenuitem.setAttribute("label", label);
-      aPopup.insertBefore(aPopup._siteURIMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker);
 
       aPopup._siteURIMenuseparator = document.createElement("menuseparator");
-      aPopup.insertBefore(aPopup._siteURIMenuseparator,
-                         aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker);
     }
   },
 
   /**
    * Add, update or remove the livemark status menuitem.
    * @param aPopup
    *        The livemark container popup
    * @param aStatus
    *        The livemark status
    */
   _setLivemarkStatusMenuItem:
   function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
-    let itemId = aPopup._placesNode.itemId;
     let statusMenuitem = aPopup._statusMenuitem;
     let stringId = "";
     if (aStatus == Ci.mozILivemark.STATUS_LOADING)
       stringId = "bookmarksLivemarkLoading";
     else if (aStatus == Ci.mozILivemark.STATUS_FAILED)
       stringId = "bookmarksLivemarkFailed";
 
     if (stringId && !statusMenuitem) {
       // Create the status menuitem and cache it in the popup object.
       statusMenuitem = document.createElement("menuitem");
       statusMenuitem.setAttribute("livemarkStatus", stringId);
+      statusMenuitem.className = "livemarkstatus-menuitem";
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
       statusMenuitem.setAttribute("disabled", true);
-      aPopup.insertBefore(statusMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
+      aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
       aPopup._statusMenuitem = statusMenuitem;
-      aPopup._startMarker++;
     }
     else if (stringId &&
              statusMenuitem.getAttribute("livemarkStatus") != stringId) {
       // Status has changed, update the cached status menuitem.
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
     }
     else if (!stringId && statusMenuitem) {
       // The livemark has finished loading.
       aPopup.removeChild(aPopup._statusMenuitem);
       aPopup._statusMenuitem = null;
-      aPopup._startMarker--;
     }
   },
 
   toggleCutNode: function PVB_toggleCutNode(aNode, aValue) {
     let elt = aNode._DOMElement;
     if (elt) {
       // We may get the popup for menus, but we need the menu itself.
       if (elt.localName == "menupopup")
@@ -512,16 +457,22 @@ PlacesViewBase.prototype = {
       throw "aPlacesNode must have _DOMElement set";
 
     // All livemarks have a feedURI, so use it as our indicator of a livemark
     // being modified.
     if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
       let menu = elt.parentNode;
       if (!menu.hasAttribute("livemark")) {
         menu.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+        // OS X native menubar doesn't track list-style-images since
+        // it doesn't have a frame (bug 733415).  Thus enforce updating.
+        menu.setAttribute("image", "");
+        menu.removeAttribute("image");
+#endif
       }
 
       PlacesUtils.livemarks.getLivemark(
         { id: aPlacesNode.itemId },
         (function (aStatus, aLivemark) {
           if (Components.isSuccessCode(aStatus)) {
             // Set an expando on the node, controller will use it to build
             // its metadata.
@@ -575,23 +526,18 @@ PlacesViewBase.prototype = {
       elt = elt.parentNode;
 
     if (parentElt._built) {
       parentElt.removeChild(elt);
 
       // Figure out if we need to show the "<Empty>" menu-item.
       // TODO Bug 517701: This doesn't seem to handle the case of an empty
       // root.
-      if (!parentElt.hasChildNodes() ||
-          (parentElt.childNodes.length == 1 &&
-          parentElt.firstChild == parentElt._emptyMenuItem))
-        this._showEmptyMenuItem(parentElt);
-
-      if (parentElt._endMarker != -1)
-        parentElt._endMarker--;
+      if (parentElt._startMarker.nextSibling == parentElt._endMarker)
+        this._setEmptyPopupStatus(parentElt, true);
     }
   },
 
   nodeReplaced:
   function PVB_nodeReplaced(aParentPlacesNode, aOldPlacesNode, aNewPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
@@ -615,18 +561,19 @@ PlacesViewBase.prototype = {
     }
   },
 
   nodeHistoryDetailsChanged:
   function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
     if (aPlacesNode.parent && aPlacesNode.parent._feedURI) {
       // Find the node in the parent.
       let popup = aPlacesNode.parent._DOMElement;
-      for (let i = popup._startMarker; i < popup.childNodes.length; i++) {
-        let child = popup.childNodes[i];
+      for (let child = popup._startMarker.nextSibling;
+           child != popup._endMarker;
+           child = child.nextSibling) {
         if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
           if (aCount)
             child.setAttribute("visited", "true");
           else
             child.removeAttribute("visited");
           break;
         }
       }
@@ -644,21 +591,21 @@ PlacesViewBase.prototype = {
   function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
 
     if (!parentElt._built)
       return;
 
-    let index = parentElt._startMarker + 1 + aIndex;
+    let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                aIndex + 1;
     this._insertNewItemToPopup(aPlacesNode, parentElt,
                                parentElt.childNodes[index]);
-    if (parentElt._emptyMenuItem)
-      parentElt._emptyMenuItem.hidden = true;
+    this._setEmptyPopupStatus(parentElt, false);
   },
 
   nodeMoved:
   function PBV_nodeMoved(aPlacesNode,
                          aOldParentPlacesNode, aOldIndex,
                          aNewParentPlacesNode, aNewIndex) {
     // Note: the current implementation of moveItem does not actually
     // use this notification when the item in question is moved from one
@@ -679,17 +626,18 @@ PlacesViewBase.prototype = {
 
     let parentElt = aNewParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aNewParentPlacesNode node must have _DOMElement set";
 
     if (parentElt._built) {
       // Move the node.
       parentElt.removeChild(elt);
-      let index = parentElt._startMarker + 1 + aNewIndex;
+      let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                  aNewIndex + 1;
       parentElt.insertBefore(elt, parentElt.childNodes[index]);
     }
   },
 
   containerStateChanged:
   function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
     if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
         aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
@@ -816,48 +764,83 @@ PlacesViewBase.prototype = {
       hasMultipleURIs = numURINodes > 1;
     }
 
     if (!hasMultipleURIs) {
       // We don't have to show any option.
       if (aPopup._endOptOpenAllInTabs) {
         aPopup.removeChild(aPopup._endOptOpenAllInTabs);
         aPopup._endOptOpenAllInTabs = null;
-        aPopup._endMarker--;
 
         aPopup.removeChild(aPopup._endOptSeparator);
         aPopup._endOptSeparator = null;
-        aPopup._endMarker--;
       }
     }
     else if (!aPopup._endOptOpenAllInTabs) {
       // Create a separator before options.
       aPopup._endOptSeparator = document.createElement("menuseparator");
       aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
       aPopup.appendChild(aPopup._endOptSeparator);
-      aPopup._endMarker++;
 
       // Add the "Open All in Tabs" menuitem.
       aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
       aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";
       aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
         "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
                                                "PlacesUIUtils.getViewForNode(this));");
       aPopup._endOptOpenAllInTabs.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       aPopup._endOptOpenAllInTabs.setAttribute("label",
         gNavigatorBundle.getString("menuOpenAllInTabs.label"));
       aPopup.appendChild(aPopup._endOptOpenAllInTabs);
-      aPopup._endMarker++;
+    }
+  },
+
+  _ensureMarkers: function PVB__ensureMarkers(aPopup) {
+    if (aPopup._startMarker)
+      return;
+
+    // _startMarker is an hidden menuseparator that lives before places nodes.
+    aPopup._startMarker = document.createElement("menuseparator");
+    aPopup._startMarker.hidden = true;
+    aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
+
+    // _endMarker is an hidden menuseparator that lives after places nodes.
+    aPopup._endMarker = document.createElement("menuseparator");
+    aPopup._endMarker.hidden = true;
+    aPopup.appendChild(aPopup._endMarker);
+
+    // Move the markers to the right position.
+    let firstNonStaticNodeFound = false;
+    for (let i = 0; i < aPopup.childNodes.length; i++) {
+      let child = aPopup.childNodes[i];
+      // Menus that have static content at the end, but are initially empty,
+      // use a special "builder" attribute to figure out where to start
+      // inserting places nodes.
+      if (child.getAttribute("builder") == "end") {
+        aPopup.insertBefore(aPopup._endMarker, child);
+        break;
+      }
+
+      if (child._placesNode && !firstNonStaticNodeFound) {
+        firstNonStaticNodeFound = true;
+        aPopup.insertBefore(aPopup._startMarker, child);
+      }
+    }
+    if (!firstNonStaticNodeFound) {
+      aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker);
     }
   },
 
   _onPopupShowing: function PVB__onPopupShowing(aEvent) {
     // Avoid handling popupshowing of inner views.
     let popup = aEvent.originalTarget;
+
+    this._ensureMarkers(popup);
+
     if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
       if (!popup._placesNode.containerOpen)
         popup._placesNode.containerOpen = true;
       if (!popup._built)
         this._rebuildPopup(popup);
 
       this._mayAddCommandsItems(popup);
     }
@@ -1803,18 +1786,16 @@ function PlacesMenu(aPopupShowingEvent, 
   this._viewElt = this._rootElt.parentNode;   // <menu>
   this._viewElt._placesView = this;
   this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
   this._addEventListeners(window, ["unload"], false);
 
 #ifdef XP_MACOSX
   if (this._viewElt.parentNode.localName == "menubar") {
     this._nativeView = true;
-    this._rootElt._startMarker = -1;
-    this._rootElt._endMarker = -1;
   }
 #endif
 
   PlacesViewBase.call(this, aPlace);
   this._onPopupShowing(aPopupShowingEvent);
 }
 
 PlacesMenu.prototype = {
@@ -1824,18 +1805,16 @@ PlacesMenu.prototype = {
     if (aIID.equals(Ci.nsIDOMEventListener))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
   },
 
   _removeChild: function PM_removeChild(aChild) {
     PlacesViewBase.prototype._removeChild.apply(this, arguments);
-    if (this._endMarker != -1)
-      this._endMarker--;
   },
 
   uninit: function PM_uninit() {
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                                true);
     this._removeEventListeners(window, ["unload"], false);
 
     PlacesViewBase.prototype.uninit.apply(this, arguments);
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -67,38 +67,33 @@
                                                 "menupopup-drop-indicator-bar");
       </field>
 
       <field name="_scrollBox">
         document.getAnonymousElementByAttribute(this, "class",
                                                 "popup-internal-box");
       </field>
 
-      <!-- markers for start and end of valid places items -->
-      <field name="_startMarker">-1</field>
-      <field name="_endMarker">-1</field>
-
       <!-- This is the view that manage the popup -->
       <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
 
       <!-- Check if we should hide the drop indicator for the target -->
       <method name="_hideDropIndicator">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          var target = aEvent.target;
+          let target = aEvent.target;
 
-          // in some view we have _startMarker and _endMarker, we should not
-          // draw the drop indicator outside of them
-          var betweenMarkers = true;
-          if (this._startMarker != -1 &&
-              target.boxObject.y <= this.childNodes[this._startMarker].boxObject.y)
-            betweenMarkers = false;
-          if (this._endMarker != -1 &&
-              target.boxObject.y >= this.childNodes[this._endMarker].boxObject.y)
-            betweenMarkers = false;
+          // Don't draw the drop indicator outside of markers.
+          // The markers are hidden, since otherwise sometimes popups acquire
+          // scrollboxes on OS X, so we can't use them directly.
+          let firstChildTop = this._startMarker.nextSibling.boxObject.y;
+          let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
+                                this._endMarker.previousSibling.boxObject.height;
+          let betweenMarkers = target.boxObject.y >= firstChildTop ||
+                               target.boxObject.y <= lastChildBottom;
 
           // Hide the dropmarker if current node is not a Places node.
           return !(target && target._placesNode && betweenMarkers);
         ]]></body>
       </method>
 
       <!-- This function returns information about where to drop when
            dragging over this popup insertion point -->
--- a/browser/components/places/tests/unit/test_bookmarks_html.js
+++ b/browser/components/places/tests/unit/test_bookmarks_html.js
@@ -37,66 +37,66 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 // An object representing the contents of bookmarks.preplaces.html.
 let test_bookmarks = {
   menu: [
     { title: "Mozilla Firefox",
       children: [
-        { title: "Help and Tutorials", 
+        { title: "Help and Tutorials",
           url: "http://en-us.www.mozilla.com/en-US/firefox/help/",
           icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="
         },
         { title: "Customize Firefox",
           url: "http://en-us.www.mozilla.com/en-US/firefox/customize/",
           icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="
         },
         { title: "Get Involved",
           url: "http://en-us.www.mozilla.com/en-US/firefox/community/",
           icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="
         },
         { title: "About Us",
           url: "http://en-us.www.mozilla.com/en-US/about/",
           icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="
-        },
-      ],
+        }
+      ]
     },
     { title: "test",
       description: "folder test comment",
       dateAdded: 1177541020000000,
       lastModified: 1177541050000000,
       children: [
         { title: "test post keyword",
           description: "item description",
           dateAdded: 1177375336000000,
           lastModified: 1177375423000000,
           keyword: "test",
           sidebar: true,
           postData: "hidden1%3Dbar&text1%3D%25s",
-          charset: "ISO-8859-1",
-        },
+          charset: "ISO-8859-1"
+        }
       ]
-    },
+    }
   ],
   toolbar: [
     { title: "Getting Started",
       url: "http://en-us.www.mozilla.com/en-US/firefox/central/",
       icon: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg=="
     },
     { title: "Latest Headlines",
       url: "http://en-us.fxfeeds.mozilla.com/en-US/firefox/livebookmarks/",
-      feedUrl: "http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml",
+      feedUrl: "http://en-us.fxfeeds.mozilla.com/en-US/firefox/headlines.xml"
     }
   ],
   unfiled: [
     { title: "Example.tld",
-      url: "http://example.tld/",
-    },
-  ],
+      url: "http://example.tld/"
+    }
+  ]
 };
 
 // Pre-Places bookmarks.html file pointer.
 let gBookmarksFileOld;
 // Places bookmarks.html file pointer.
 let gBookmarksFileNew;
 
 let importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
@@ -298,16 +298,18 @@ add_test(function test_import_ontop()
       run_next_test();
     });
   });
 });
 
 function testImportedBookmarks()
 {
   for (let group in test_bookmarks) {
+    do_print("[testImportedBookmarks()] Checking group '" + group + "'");
+
     let root;
     switch (group) {
       case "menu":
         root = PlacesUtils.getFolderContents(PlacesUtils.bookmarksMenuFolderId).root;
         break;
       case "toolbar":
         root = PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
         break;
@@ -330,27 +332,27 @@ function testImportedBookmarksToFolder(a
   root = PlacesUtils.getFolderContents(aFolder).root;
 
   // Menu bookmarks are put directly into the folder, while other roots are
   // imported into subfolders.
   let rootFolderCount = test_bookmarks.menu.length;
 
   for (let i = 0; i < root.childCount; i++) {
     let child = root.getChild(i);
+    // This check depends on all "menu" bookmarks being listed first in the imported file :-|
     if (i < rootFolderCount) {
       checkItem(test_bookmarks.menu[i], child);
     }
     else {
       let container = child.QueryInterface(Ci.nsINavHistoryContainerResultNode);
       let group = /Toolbar/.test(container.title) ? test_bookmarks.toolbar
                                                   : test_bookmarks.unfiled;
       container.containerOpen = true;
-      print(container.title);
+      do_print("[testImportedBookmarksToFolder()] Checking container '" + container.title + "'");
       for (let t = 0; t < container.childCount; t++) {
-        print(group[t].title + " " + container.getChild(t).title);
         checkItem(group[t], container.getChild(t));
       }
       container.containerOpen = false;
     }
   }
 
   root.containerOpen = false;
 }
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -194,17 +194,17 @@ menuitem.bookmark-item {
 }
 
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
 /* Bookmark items */
-.bookmark-item:not([container])  {
+.bookmark-item {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .bookmark-item[container] {
   list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
 }
 
 .bookmark-item[container][livemark] { 
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -224,25 +224,30 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item > .toolbarbutton-icon[type="menu"] {
   -moz-margin-end: 5px;
 }
 
 .bookmark-item[container] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.query-item[container] {
-  list-style-image: url("chrome://browser/skin/places/history.png");
-}
-
-.bookmark-item[livemark] {
+.bookmark-item[container][livemark] {
   list-style-image: url("chrome://browser/skin/page-livemarks.png");
 }
 
-.bookmark-item[query] {
+.bookmark-item[container][livemark] .bookmark-item {
+  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited] {
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.bookmark-item[container][query] {
   list-style-image: url("chrome://browser/skin/places/query.png");
 }
 
 .bookmark-item[query][tagContainer] {
   list-style-image: url("chrome://browser/skin/places/tag.png");
 }
 
 .bookmark-item[query][dayContainer] {
@@ -252,56 +257,49 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item[query][hostContainer] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
 .bookmark-item[query][hostContainer][open] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.bookmark-item[livemark] .menuitem-iconic {
-  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.bookmark-item[livemark] .menuitem-iconic[visited] {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item menuitem[openInTabs],
-.bookmark-item menuitem[siteURI] {
+/* Workaround for native menubar inheritance */
+.openintabs-menuitem,
+.openlivemarksite-menuitem,
+.livemarkstatus-menuitem {
   list-style-image: none;
 }
 
+.bookmark-item[cutting] > .toolbarbutton-icon,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
+  opacity: 0.5;
+}
+
+.bookmark-item[cutting] > .toolbarbutton-text,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
+  opacity: 0.7;
+}
+
 #wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
   background: url("chrome://browser/skin/places/bookmarksToolbar.png") no-repeat center;
 }
 
 .bookmarks-toolbar-customize {
   max-width: 15em !important;
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png") !important;
 }
 
 /* ----- BOOKMARK MENUS ----- */
 
 .bookmark-item > .menu-iconic-left > .menu-iconic-icon {
   width: 16px;
   height: 16px;
 }
 
-.bookmark-item[cutting] > .toolbarbutton-icon,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
-  opacity: 0.5;
-}
-
-.bookmark-item[cutting] > .toolbarbutton-text,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
-  opacity: 0.7;
-}
-
 #bookmarksToolbarFolderMenu,
 #BMB_bookmarksToolbar {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
 }
 
 #BMB_unsortedBookmarks {
   list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.png");
 }
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -1219,17 +1219,17 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 
 #urlbar,
 .searchbar-textbox {
   -moz-appearance: none;
   margin: 1px 3px;
   padding: 0;
   background-clip: padding-box;
   border: 1px solid ThreeDShadow;
-  border-radius: 2.5px;
+  border-radius: 2px;
 }
 
 #urlbar {
   width: 7em;
   min-width: 7em;
   -moz-padding-end: 2px;
 }
 
--- a/browser/themes/winstripe/places/organizer-aero.css
+++ b/browser/themes/winstripe/places/organizer-aero.css
@@ -69,11 +69,11 @@
   }
 
   #searchFilter {
     -moz-appearance: none;
     padding: 2px;
     -moz-padding-start: 4px;
     background-clip: padding-box;
     border: 1px solid rgba(0,0,0,.32);
-    border-radius: 2.5px;
+    border-radius: 2px;
   }
 }
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -733,18 +733,21 @@ user_pref("camino.use_system_proxy_setti
         self.log.info("Failed to read image from %s", imgoutput)
 
     import base64
     encoded = base64.b64encode(image)
     self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
 
   def killAndGetStack(self, proc, utilityPath, debuggerInfo):
     """Kill the process, preferrably in a way that gets us a stack trace."""
-    if not debuggerInfo and not self.haveDumpedScreen:
-      self.dumpScreen(utilityPath)
+    if not debuggerInfo:
+      if self.haveDumpedScreen:
+        self.log.info("Not taking screenshot here: see the one that was previously logged")
+      else:
+        self.dumpScreen(utilityPath)
 
     if self.CRASHREPORTER and not debuggerInfo:
       if self.UNIXISH:
         # ABRT will get picked up by Breakpad's signal handler
         os.kill(proc.pid, signal.SIGABRT)
         return
       elif self.IS_WIN32:
         # We should have a "crashinject" program in our utility path
@@ -791,18 +794,21 @@ user_pref("camino.use_system_proxy_setti
       while line != "" and not didTimeout:
         if logger:
           logger.log(line)
         if "TEST-START" in line and "|" in line:
           self.lastTestSeen = line.split("|")[1].strip()
         if stackFixerFunction:
           line = stackFixerFunction(line)
         self.log.info(line.rstrip().decode("UTF-8", "ignore"))
-        if not debuggerInfo and not self.haveDumpedScreen and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
-          self.dumpScreen(utilityPath)
+        if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
+          if self.haveDumpedScreen:
+            self.log.info("Not taking screenshot here: see the one that was previously logged")
+          else:
+            self.dumpScreen(utilityPath)
 
         (line, didTimeout) = self.readWithTimeout(logsource, timeout)
         if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
           # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait().
           hitMaxTime = True
           self.log.info("TEST-UNEXPECTED-FAIL | %s | application ran for longer than allowed maximum time of %d seconds", self.lastTestSeen, int(maxTime))
           self.killAndGetStack(proc, utilityPath, debuggerInfo)
       if didTimeout:
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -454,17 +454,17 @@ def wrapCommand(cmd):
   return cmd
 
 class ShutdownLeakLogger(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
-  MAX_LEAK_COUNT = 130
+  MAX_LEAK_COUNT = 123
 
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -72,10 +72,10 @@ public interface Driver {
     void startCheckerboardRecording();
     float stopCheckerboardRecording();
 
     /**
      * Get a copy of the painted content region.
      * @return A 2-D array of pixels (indexed by y, then x). The pixels
      * are in ARGB-8888 format.
      */
-    int[][] getPaintedSurface();
+    PaintedSurface getPaintedSurface();
 }
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -43,16 +43,18 @@ import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.IntBuffer;
 import java.util.HashMap;
 import java.util.List;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
 
 import java.lang.Class;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
 import java.lang.Long;
 
@@ -286,17 +288,17 @@ public class FennecNativeDriver implemen
         for (View v : mSolo.getCurrentViews()) {
             if (v instanceof GLSurfaceView) {
                 return (GLSurfaceView)v;
             }
         }
         return null;
     }
 
-    public int[][] getPaintedSurface() {
+    public PaintedSurface getPaintedSurface() {
         GLSurfaceView view = getSurfaceView();
         if (view == null) {
             return null;
         }
         IntBuffer pixelBuffer;
         try {
             pixelBuffer = (IntBuffer)_getPixels.invoke(view);
         } catch (Exception e) {
@@ -304,24 +306,44 @@ public class FennecNativeDriver implemen
             return null;
         }
 
         // now we need to (1) flip the image, because GL likes to do things up-side-down,
         // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
         int w = view.getWidth();
         int h = view.getHeight();
         pixelBuffer.position(0);
-        int[][] pixels = new int[h][w];
-        for (int y = h - 1; y >= 0; y--) {
-            for (int x = 0; x < w; x++) {
-                int agbr = pixelBuffer.get();
-                pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
+        String mapFile = "/mnt/sdcard/pixels.map";
+
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+        try {
+            fos = new FileOutputStream(mapFile);
+            dos = new DataOutputStream(fos);
+
+            for (int y = h - 1; y >= 0; y--) {
+                for (int x = 0; x < w; x++) {
+                    int agbr = pixelBuffer.get();
+                    dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000));
+                }
+            }
+            return new PaintedSurface(mapFile, w, h);
+        } catch (IOException e) {
+            throw new RoboCopException("exception with pixel writer on file: " + mapFile);
+        } finally {
+            try {
+                if (dos != null && fos != null) {
+                    dos.flush();
+                    dos.close();
+                    fos.close();
+                }
+            } catch (IOException e) {
+                throw new RoboCopException("exception closing pixel writer on file: " + mapFile);
             }
         }
-        return pixels;
     }
 
     public int mHeight=0;
     public int mScrollHeight=0;
     public int mPageHeight=10;
 
     class scrollHandler implements InvocationHandler {
         public scrollHandler(){};
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -56,21 +56,25 @@ JAVAFILES = \
   Driver.java \
   Element.java \
   FennecNativeActions.java \
   FennecMochitestAssert.java \
   FennecTalosAssert.java \
   FennecNativeDriver.java \
   FennecNativeElement.java \
   RoboCopException.java \
+  PaintedSurface.java \
   $(NULL)
 
 _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in))
 
-_TEST_FILES = $(wildcard $(TESTPATH)/*.html)
+_TEST_FILES = \
+  $(wildcard $(TESTPATH)/*.html) \
+  $(wildcard $(TESTPATH)/*.sjs) \
+  $(NULL)
 
 _ROBOCOP_TOOLS = \
   $(TESTPATH)/robocop.ini \
   parse_ids.py \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml \
new file mode 100644
--- /dev/null
+++ b/build/mobile/robocop/PaintedSurface.java.in
@@ -0,0 +1,62 @@
+#filter substitution
+/* 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/. */
+
+package @ANDROID_PACKAGE_NAME@;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class PaintedSurface {
+    private String mFileName = null;
+    private int mWidth = -1;
+    private int mHeight = -1;
+    private MappedByteBuffer mPixelBuffer = null;
+
+    public PaintedSurface(String filename, int width, int height) {
+        mFileName = filename;
+        mWidth = width;
+        mHeight = height;
+        
+        try {
+            File f = new File(filename);
+            int pixelSize = (int)f.length();
+            
+            FileInputStream pixelFile = new FileInputStream(filename);
+            mPixelBuffer = pixelFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, pixelSize);
+        } catch (java.io.FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (java.io.IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public final int getPixelAt(int x, int y) {
+        if (mPixelBuffer == null) {
+            throw new RoboCopException("Trying to access PaintedSurface with no active PixelBuffer");
+        }
+
+        if (x >= mWidth || x < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid x value");
+        }
+
+        if (y >= mHeight || y < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid y value");
+        }
+
+        // The rows are reversed so row 0 is at the end and we start with the last row.
+        // This is why we do mHeight-y;
+        int index = (x + ((mHeight - y - 1) * mWidth)) * 4;
+        int b1 = mPixelBuffer.get(index) & 0xFF;
+        int b2 = mPixelBuffer.get(index + 1) & 0xFF;
+        int b3 = mPixelBuffer.get(index + 2) & 0xFF;
+        int b4 = mPixelBuffer.get(index + 3) & 0xFF;
+        int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0);
+        return value;
+    }
+}
+
--- a/caps/src/nsNullPrincipalURI.cpp
+++ b/caps/src/nsNullPrincipalURI.cpp
@@ -62,21 +62,22 @@ nsNullPrincipalURI::nsNullPrincipalURI(c
 
 static NS_DEFINE_CID(kNullPrincipalURIImplementationCID,
                      NS_NULLPRINCIPALURI_IMPLEMENTATION_CID);
 
 NS_IMPL_THREADSAFE_ADDREF(nsNullPrincipalURI)
 NS_IMPL_THREADSAFE_RELEASE(nsNullPrincipalURI)
 
 NS_INTERFACE_MAP_BEGIN(nsNullPrincipalURI)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIURI)
   if (aIID.Equals(kNullPrincipalURIImplementationCID))
     foundInterface = static_cast<nsIURI *>(this);
   else
   NS_INTERFACE_MAP_ENTRY(nsIURI)
+  NS_INTERFACE_MAP_ENTRY(nsISizeOf)
 NS_INTERFACE_MAP_END
 
 ////////////////////////////////////////////////////////////////////////////////
 //// nsIURI
 
 NS_IMETHODIMP
 nsNullPrincipalURI::GetAsciiHost(nsACString &_host)
 {
@@ -294,8 +295,24 @@ nsNullPrincipalURI::Resolve(const nsACSt
 }
 
 NS_IMETHODIMP
 nsNullPrincipalURI::SchemeIs(const char *aScheme, bool *_schemeIs)
 {
   *_schemeIs = (0 == nsCRT::strcasecmp(mScheme.get(), aScheme));
   return NS_OK;
 }
+
+////////////////////////////////////////////////////////////////////////////////
+//// nsISizeOf
+
+size_t
+nsNullPrincipalURI::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+{
+  return mScheme.SizeOfExcludingThisIfUnshared(aMallocSizeOf) +
+         mPath.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
+}
+
+size_t
+nsNullPrincipalURI::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const {
+  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
+}
+
--- a/caps/src/nsNullPrincipalURI.h
+++ b/caps/src/nsNullPrincipalURI.h
@@ -40,30 +40,36 @@
 /**
  * This wraps nsSimpleURI so that all calls to it are done on the main thread.
  */
 
 #ifndef __nsNullPrincipalURI_h__
 #define __nsNullPrincipalURI_h__
 
 #include "nsIURI.h"
+#include "nsISizeOf.h"
 #include "nsAutoPtr.h"
 #include "nsString.h"
 
 // {51fcd543-3b52-41f7-b91b-6b54102236e6}
 #define NS_NULLPRINCIPALURI_IMPLEMENTATION_CID \
   {0x51fcd543, 0x3b52, 0x41f7, \
     {0xb9, 0x1b, 0x6b, 0x54, 0x10, 0x22, 0x36, 0xe6} }
 
 class nsNullPrincipalURI : public nsIURI
+                         , public nsISizeOf
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIURI
 
+  // nsISizeOf
+  virtual size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+  virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+
   nsNullPrincipalURI(const nsCString &aSpec);
 
 private:
   nsCString mScheme;
   nsCString mPath;
 };
 
 #endif // __nsNullPrincipalURI_h__
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -296,16 +296,20 @@ MOZ_GIO_CFLAGS = @MOZ_GIO_CFLAGS@
 MOZ_GIO_LIBS = @MOZ_GIO_LIBS@
 
 MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@
 MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@
 
 MOZ_B2G_RIL = @MOZ_B2G_RIL@
 MOZ_B2G_BT = @MOZ_B2G_BT@
 
+MOZ_ASAN = @MOZ_ASAN@
+MOZ_CFLAGS_NSS = @MOZ_CFLAGS_NSS@
+MOZ_NO_WLZDEFS = @MOZ_NO_WLZDEFS@
+
 BUILD_CTYPES = @BUILD_CTYPES@
 
 COMPILE_ENVIRONMENT = @COMPILE_ENVIRONMENT@
 CROSS_COMPILE   = @CROSS_COMPILE@
 
 WCHAR_CFLAGS	= @WCHAR_CFLAGS@
 
 OS_CPPFLAGS	= @CPPFLAGS@
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -143,16 +143,19 @@ testxpcsrcdir = $(topsrcdir)/testing/xpc
 # Execute all tests in the $(XPCSHELL_TESTS) directories.
 # See also testsuite-targets.mk 'xpcshell-tests' target for global execution.
 xpcshell-tests:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
 	  -I$(topsrcdir)/build \
 	  $(testxpcsrcdir)/runxpcshelltests.py \
 	  --symbols-path=$(DIST)/crashreporter-symbols \
 	  --build-info-json=$(DEPTH)/mozinfo.json \
+	  --tests-root-dir=$(testxpcobjdir) \
+	  --xunit-file=$(testxpcobjdir)/$(relativesrcdir)/results.xml \
+	  --xunit-suite-name=xpcshell \
 	  $(EXTRA_TEST_ARGS) \
 	  $(LIBXUL_DIST)/bin/xpcshell \
 	  $(foreach dir,$(XPCSHELL_TESTS),$(testxpcobjdir)/$(relativesrcdir)/$(dir))
 
 xpcshell-tests-remote: DM_TRANS?=adb
 xpcshell-tests-remote:
 	$(PYTHON) -u $(topsrcdir)/config/pythonpath.py \
 	  -I$(topsrcdir)/build \
--- a/configure.in
+++ b/configure.in
@@ -623,19 +623,20 @@ CXX_VERSION='N/A'
 if test "$GCC" = "yes"; then
     GNU_CC=1
     CC_VERSION=`$CC -v 2>&1 | grep 'gcc version'`
 fi
 if test "$GXX" = "yes"; then
     GNU_CXX=1
     CXX_VERSION=`$CXX -v 2>&1 | grep 'gcc version'`
 fi
-if test "`echo | $AS -v 2>&1 | grep -c GNU`" != "0"; then
+if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_AS=1
 fi
+rm -f conftest.out
 if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then
     GNU_LD=1
 fi
 if test "$GNU_CC"; then
     if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then
         GCC_USE_GNU_LD=1
     fi
 fi
@@ -1832,27 +1833,59 @@ if test -n "$CLANG_CC"; then
     _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
     CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
 fi
 if test -n "$CLANG_CXX"; then
     _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}"
 fi
 
 dnl ========================================================
+dnl = Use Address Sanitizer
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(address-sanitizer,
+[  --enable-address-sanitizer       Enable Address Sanitizer (default=no)],
+    MOZ_ASAN=1,
+    MOZ_ASAN= )
+if test -n "$MOZ_ASAN"; then
+    MOZ_LLVM_HACKS=1
+    AC_DEFINE(MOZ_ASAN)
+fi
+AC_SUBST(MOZ_ASAN)
+
+dnl ========================================================
+dnl = Enable hacks required for LLVM instrumentations
+dnl ========================================================
+MOZ_ARG_ENABLE_BOOL(llvm-hacks,
+[  --enable-llvm-hacks       Enable workarounds required for several LLVM instrumentations (default=no)],
+    MOZ_LLVM_HACKS=1,
+    MOZ_LLVM_HACKS= )
+if test -n "$MOZ_LLVM_HACKS"; then
+    MOZ_NO_WLZDEFS=1
+    MOZ_CFLAGS_NSS=1
+fi
+AC_SUBST(MOZ_NO_WLZDEFS)
+AC_SUBST(MOZ_CFLAGS_NSS)
+
+dnl ========================================================
 dnl GNU specific defaults
 dnl ========================================================
 if test "$GNU_CC"; then
     # FIXME: Let us build with strict aliasing. bug 414641.
     CFLAGS="$CFLAGS -fno-strict-aliasing"
     MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@'
     DSO_LDOPTS='-shared'
     if test "$GCC_USE_GNU_LD"; then
-        # Don't allow undefined symbols in libraries
-        DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs"
+        # Some tools like ASan use a runtime library that is only
+        # linked against executables, so we must allow undefined
+        # symbols for shared objects in some cases.
+        if test -z "$MOZ_NO_WLZDEFS"; then
+            # Don't allow undefined symbols in libraries
+            DSO_LDOPTS="$DSO_LDOPTS -Wl,-z,defs"
+        fi
     fi
     WARNINGS_AS_ERRORS='-Werror -Wno-error=uninitialized'
     DSO_CFLAGS=''
     DSO_PIC_CFLAGS='-fPIC'
     ASFLAGS="$ASFLAGS -fPIC"
     _MOZ_RTTI_FLAGS_ON=-frtti
     _MOZ_RTTI_FLAGS_OFF=-fno-rtti
 
@@ -9207,17 +9240,17 @@ if test -z "$MOZ_NATIVE_NSPR"; then
     ac_configure_args="$_SUBDIR_CONFIG_ARGS --with-dist-prefix=$MOZ_BUILD_ROOT/dist --with-mozilla"
     if test -z "$MOZ_DEBUG"; then
         ac_configure_args="$ac_configure_args --disable-debug"
     else
         ac_configure_args="$ac_configure_args --enable-debug"
     fi
     if test "$MOZ_OPTIMIZE" = "1"; then
         ac_configure_args="$ac_configure_args --enable-optimize"
-    else
+    elif test -z "$MOZ_OPTIMIZE"; then
         ac_configure_args="$ac_configure_args --disable-optimize"
     fi
     if test -n "$HAVE_64BIT_OS"; then
         ac_configure_args="$ac_configure_args --enable-64bit"
     fi
     if test -n "$USE_ARM_KUSER"; then
         ac_configure_args="$ac_configure_args --with-arm-kuser"
     fi
--- a/content/base/src/Link.cpp
+++ b/content/base/src/Link.cpp
@@ -36,16 +36,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "Link.h"
 
 #include "nsEventStates.h"
 #include "nsIURL.h"
+#include "nsISizeOf.h"
 
 #include "nsContentUtils.h"
 #include "nsEscape.h"
 #include "nsGkAtoms.h"
 #include "nsString.h"
 #include "mozAutoDocUpdate.h"
 
 #include "mozilla/Services.h"
@@ -526,10 +527,29 @@ Link::SetHrefAttribute(nsIURI *aURI)
   NS_ASSERTION(aURI, "Null URI is illegal!");
 
   nsCAutoString href;
   (void)aURI->GetSpec(href);
   (void)mElement->SetAttr(kNameSpaceID_None, nsGkAtoms::href,
                           NS_ConvertUTF8toUTF16(href), true);
 }
 
+size_t
+Link::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+{
+  size_t n = 0;
+
+  if (mCachedURI) {
+    nsCOMPtr<nsISizeOf> iface = do_QueryInterface(mCachedURI);
+    if (iface) {
+      n += iface->SizeOfIncludingThis(aMallocSizeOf);
+    }
+  }
+
+  // The following members don't need to be measured:
+  // - mElement, because it is a pointer-to-self used to avoid QIs
+  // - mHistory, because it is non-owning
+
+  return n;
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/content/base/src/Link.h
+++ b/content/base/src/Link.h
@@ -126,16 +126,19 @@ public:
   /**
    * Checks if DNS Prefetching is ok
    * 
    * @returns boolean
    *          Defaults to true; should be overridden for specialised cases
    */
   virtual bool HasDeferredDNSPrefetchRequest() { return true; }
 
+  virtual size_t
+    SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const;
+
 protected:
   virtual ~Link();
 
   bool HasCachedURI() const { return !!mCachedURI; }
 
 private:
   /**
    * Unregisters from History so this node no longer gets notifications about
--- a/content/base/src/nsCrossSiteListenerProxy.cpp
+++ b/content/base/src/nsCrossSiteListenerProxy.cpp
@@ -39,17 +39,16 @@
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
 #include "nsDOMError.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 #include "nsIParser.h"
 #include "nsParserCIID.h"
-#include "nsICharsetAlias.h"
 #include "nsMimeTypes.h"
 #include "nsIStreamConverterService.h"
 #include "nsStringStream.h"
 #include "nsGkAtoms.h"
 #include "nsWhitespaceTokenizer.h"
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsCharSeparatedTokenizer.h"
--- a/content/base/src/nsDOMFile.cpp
+++ b/content/base/src/nsDOMFile.cpp
@@ -38,17 +38,16 @@
 
 #include "nsDOMFile.h"
 
 #include "nsCExternalHandlerService.h"
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMError.h"
-#include "nsICharsetAlias.h"
 #include "nsICharsetDetector.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIConverterInputStream.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsIIPCSerializable.h"
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -37,17 +37,17 @@
 
 #include "nsDOMFileReader.h"
 
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMFile.h"
 #include "nsDOMError.h"
-#include "nsICharsetAlias.h"
+#include "nsCharsetAlias.h"
 #include "nsICharsetDetector.h"
 #include "nsICharsetConverterManager.h"
 #include "nsIConverterInputStream.h"
 #include "nsIFile.h"
 #include "nsIFileStreams.h"
 #include "nsIInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsIPlatformCharset.h"
@@ -490,20 +490,17 @@ nsDOMFileReader::GetAsText(const nsACStr
   if (!aCharset.IsEmpty()) {
     charsetGuess = aCharset;
   } else {
     rv = GuessCharset(aFileData, aDataLen, charsetGuess);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   nsCAutoString charset;
-  nsCOMPtr<nsICharsetAlias> alias = do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = alias->GetPreferred(charsetGuess, charset);
+  rv = nsCharsetAlias::GetPreferred(charsetGuess, charset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = ConvertStream(aFileData, aDataLen, charset.get(), aResult);
 
   return NS_OK;
 }
 
 nsresult
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -142,17 +142,17 @@
 #include "nsIScriptContext.h"
 #include "nsBindingManager.h"
 #include "nsIDOMHTMLDocument.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIRequest.h"
 #include "nsILink.h"
 #include "nsBlobProtocolHandler.h"
 
-#include "nsICharsetAlias.h"
+#include "nsCharsetAlias.h"
 #include "nsIParser.h"
 #include "nsIContentSink.h"
 
 #include "nsDateTimeFormatCID.h"
 #include "nsIDateTimeFormat.h"
 #include "nsEventDispatcher.h"
 #include "nsMutationEvent.h"
 #include "nsIDOMXPathEvaluator.h"
@@ -3007,23 +3007,20 @@ nsDocument::GetBaseTarget(nsAString &aBa
 
 void
 nsDocument::SetDocumentCharacterSet(const nsACString& aCharSetID)
 {
   if (!mCharacterSet.Equals(aCharSetID)) {
     mCharacterSet = aCharSetID;
 
 #ifdef DEBUG
-    nsCOMPtr<nsICharsetAlias> calias(do_GetService(NS_CHARSETALIAS_CONTRACTID));
-    if (calias) {
-      nsCAutoString canonicalName;
-      calias->GetPreferred(aCharSetID, canonicalName);
-      NS_ASSERTION(canonicalName.Equals(aCharSetID),
-                   "charset name must be canonical");
-    }
+    nsCAutoString canonicalName;
+    nsCharsetAlias::GetPreferred(aCharSetID, canonicalName);
+    NS_ASSERTION(canonicalName.Equals(aCharSetID),
+                 "charset name must be canonical");
 #endif
 
     PRInt32 n = mCharSetObservers.Length();
 
     for (PRInt32 i = 0; i < n; i++) {
       nsIObserver* observer = mCharSetObservers.ElementAt(i);
 
       observer->Observe(static_cast<nsIDocument *>(this), "charset",
@@ -3167,26 +3164,20 @@ nsDocument::TryChannelCharset(nsIChannel
   if(kCharsetFromChannel <= aCharsetSource) {
     return true;
   }
 
   if (aChannel) {
     nsCAutoString charsetVal;
     nsresult rv = aChannel->GetContentCharset(charsetVal);
     if (NS_SUCCEEDED(rv)) {
-      nsCOMPtr<nsICharsetAlias> calias(do_GetService(NS_CHARSETALIAS_CONTRACTID));
-      if (calias) {
-        nsCAutoString preferred;
-        rv = calias->GetPreferred(charsetVal,
-                                  preferred);
-        if(NS_SUCCEEDED(rv)) {
-          aCharset = preferred;
-          aCharsetSource = kCharsetFromChannel;
-          return true;
-        }
+      rv = nsCharsetAlias::GetPreferred(charsetVal, aCharset);
+      if(NS_SUCCEEDED(rv)) {
+        aCharsetSource = kCharsetFromChannel;
+        return true;
       }
     }
   }
   return false;
 }
 
 nsresult
 nsDocument::CreateShell(nsPresContext* aContext, nsIViewManager* aViewManager,
--- a/content/base/src/nsFrameMessageManager.cpp
+++ b/content/base/src/nsFrameMessageManager.cpp
@@ -877,19 +877,17 @@ nsFrameScriptExecutor::InitTabChildGloba
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
   const PRUint32 flags = nsIXPConnect::INIT_JS_STANDARD_CLASSES |
                          nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT;
 
   
   JS_SetContextPrivate(cx, aScope);
 
   nsresult rv =
-    xpc->InitClassesWithNewWrappedGlobal(cx, aScope,
-                                         NS_GET_IID(nsISupports),
-                                         mPrincipal, nsnull,
+    xpc->InitClassesWithNewWrappedGlobal(cx, aScope, mPrincipal,
                                          flags, getter_AddRefs(mGlobal));
   NS_ENSURE_SUCCESS(rv, false);
 
     
   JSObject* global = nsnull;
   rv = mGlobal->GetJSObject(&global);
   NS_ENSURE_SUCCESS(rv, false);
 
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -592,39 +592,20 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
 
     if (elem) {
       elem->RecompileScriptEventListeners();
     }
 
     if (aCx && wrapper) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       if (xpc) {
-        JSObject *preservedWrapper = nsnull;
-
-        // If reparenting moves us to a new compartment, preserving causes
-        // problems. In that case, we release ourselves and re-preserve after
-        // reparenting so we're sure to have the right JS object preserved.
-        // We use a JSObject stack copy of the wrapper to protect it from GC
-        // under ReparentWrappedNativeIfFound.
-        if (aNode->PreservingWrapper()) {
-          preservedWrapper = wrapper;
-          nsContentUtils::ReleaseWrapper(aNode, aNode);
-          NS_ASSERTION(aNode->GetWrapper(),
-                       "ReleaseWrapper cleared our wrapper, this code needs to "
-                       "be changed to deal with that!");
-        }
-
         nsCOMPtr<nsIXPConnectJSObjectHolder> oldWrapper;
         rv = xpc->ReparentWrappedNativeIfFound(aCx, wrapper, aNewScope, aNode,
                                                getter_AddRefs(oldWrapper));
 
-        if (preservedWrapper) {
-          nsContentUtils::PreserveWrapper(aNode, aNode);
-        }
-
         if (NS_FAILED(rv)) {
           aNode->mNodeInfo.swap(nodeInfo);
 
           return rv;
         }
       }
     }
   }
--- a/content/base/src/nsWebSocket.cpp
+++ b/content/base/src/nsWebSocket.cpp
@@ -597,17 +597,17 @@ nsWebSocket::Initialize(nsISupports* aOw
   nsTArray<nsString> protocolArray;
 
   if (aArgc == 2) {
     JSObject *jsobj;
 
     if (JSVAL_IS_OBJECT(aArgv[1]) &&
         (jsobj = JSVAL_TO_OBJECT(aArgv[1])) &&
         JS_IsArrayObject(aContext, jsobj)) {
-      jsuint len;
+      uint32_t len;
       JS_GetArrayLength(aContext, jsobj, &len);
       
       for (PRUint32 index = 0; index < len; ++index) {
         jsval value;
 
         if (!JS_GetElement(aContext, jsobj, index, &value))
           return NS_ERROR_DOM_SYNTAX_ERR;
 
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -56,17 +56,17 @@
 #include "nsISupportsPrimitives.h"
 #include "nsGUIEvent.h"
 #include "nsIPrivateDOMEvent.h"
 #include "prprf.h"
 #include "nsIDOMEventListener.h"
 #include "nsIJSContextStack.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsWeakPtr.h"
-#include "nsICharsetAlias.h"
+#include "nsCharsetAlias.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsDOMClassInfoID.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMWindow.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsIVariant.h"
 #include "nsVariant.h"
@@ -807,21 +807,17 @@ nsXMLHttpRequest::DetectCharset()
   if (!channel) {
     channel = mChannel;
   }
 
   nsCAutoString charsetVal;
   nsresult rv = channel ? channel->GetContentCharset(charsetVal) :
                 NS_ERROR_FAILURE;
   if (NS_SUCCEEDED(rv)) {
-    nsCOMPtr<nsICharsetAlias> calias =
-      do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv);
-    if (NS_SUCCEEDED(rv) && calias) {
-      rv = calias->GetPreferred(charsetVal, mResponseCharset);
-    }
+    rv = nsCharsetAlias::GetPreferred(charsetVal, mResponseCharset);
   }
 
   if (NS_FAILED(rv) || mResponseCharset.IsEmpty()) {
     // MS documentation states UTF-8 is default for responseText
     mResponseCharset.AssignLiteral("UTF-8");
   }
 
   if (mResponseType == XML_HTTP_RESPONSE_TYPE_JSON &&
--- a/content/canvas/src/CanvasUtils.cpp
+++ b/content/canvas/src/CanvasUtils.cpp
@@ -117,17 +117,17 @@ CoerceDouble(jsval v, double* d)
 }
 
 template<size_t N>
 static bool
 JSValToMatrixElts(JSContext* cx, const jsval& val,
                   double* (&elts)[N], nsresult* rv)
 {
     JSObject* obj;
-    jsuint length;
+    uint32_t length;
 
     if (JSVAL_IS_PRIMITIVE(val) ||
         !(obj = JSVAL_TO_OBJECT(val)) ||
         !JS_GetArrayLength(cx, obj, &length) ||
         N != length) {
         // Not an array-like thing or wrong size
         *rv = NS_ERROR_INVALID_ARG;
         return false;
--- a/content/canvas/src/CanvasUtils.h
+++ b/content/canvas/src/CanvasUtils.h
@@ -141,21 +141,21 @@ DashArrayToJSVal(FallibleTArray<T>& dash
 
 template<typename T>
 nsresult
 JSValToDashArray(JSContext* cx, const jsval& patternArray,
                  FallibleTArray<T>& dashes)
 {
     // The cap is pretty arbitrary.  16k should be enough for
     // anybody...
-    static const jsuint MAX_NUM_DASHES = 1 << 14;
+    static const uint32_t MAX_NUM_DASHES = 1 << 14;
 
     if (!JSVAL_IS_PRIMITIVE(patternArray)) {
         JSObject* obj = JSVAL_TO_OBJECT(patternArray);
-        jsuint length;
+        uint32_t length;
         if (!JS_GetArrayLength(cx, obj, &length)) {
             // Not an array-like thing
             return NS_ERROR_INVALID_ARG;
         } else if (length > MAX_NUM_DASHES) {
             // Too many dashes in the pattern
             return NS_ERROR_ILLEGAL_VALUE;
         }
 
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -749,17 +749,17 @@ protected:
     // extensions
     enum WebGLExtensionID {
         WebGL_OES_texture_float,
         WebGL_OES_standard_derivatives,
         WebGL_EXT_texture_filter_anisotropic,
         WebGL_MOZ_WEBGL_lose_context,
         WebGLExtensionID_Max
     };
-    nsCOMPtr<WebGLExtension> mEnabledExtensions[WebGLExtensionID_Max];
+    nsRefPtr<WebGLExtension> mEnabledExtensions[WebGLExtensionID_Max];
     bool IsExtensionEnabled(WebGLExtensionID ext) const {
         NS_ABORT_IF_FALSE(ext >= 0 && ext < WebGLExtensionID_Max, "bogus index!");
         return mEnabledExtensions[ext] != nsnull;
     }
     bool IsExtensionSupported(WebGLExtensionID ei);
 
     bool InitAndValidateGL();
     bool ValidateBuffers(PRInt32* maxAllowedCount, const char *info);
--- a/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
+++ b/content/canvas/test/webgl/test_webgl_conformance_test_suite.html
@@ -372,17 +372,17 @@ function start() {
       case reportType.FINISH_PAGE:
         return this.finishPage(success);
       case reportType.FINISHED_ALL_TESTS:
         this.finishedTestSuite();
         return true;
       default:
         throw 'unhandled';
         break;
-    };
+    }
   };
 
   var getURLOptions = function(obj) {
     var s = window.location.href;
     var q = s.indexOf("?");
     var e = s.indexOf("#");
     if (e < 0) {
       e = s.length;
@@ -399,48 +399,47 @@ function start() {
 
   getURLOptions(OPTIONS);
 
   function runTestSuite() {
     var reporter = new Reporter();
 
     // try to create a dummy WebGL context, just to catch context creation failures once here,
     // rather than having them result in 100's of failures (one in each test page)
-    var canvas = document.getElementById("webglcheck-default");
     var ctx;
     try {
-        ctx = canvas.getContext("experimental-webgl");
-    } catch(e) {
-        ok(false, "canvas.getContext() failed", e);
-    }
-
-    if (ctx) {
-        statusTextNode.textContent = 'Loading test lists...';
-        var iframe = document.getElementById("testframe");
-        var testHarness = new WebGLTestHarnessModule.TestHarness(
-            iframe,
-            '00_test_list.txt',
-            function(type, msg, success) {
-                return reporter.reportFunc(type, msg, success);
-            },
-            OPTIONS);
-        testHarness.setTimeoutDelay(20000); // and make it much higher when running under valgrind.
-        window.webglTestHarness = testHarness;
-    } else {
+      ctx = document.getElementById("webglcheck-default")
+                    .getContext("experimental-webgl");
+    } catch(e) {}
+    if (!ctx) {
         var errmsg = "Can't create a WebGL context";
         reporter.fullResultsNode.textContent = errmsg;
         // Workaround for SeaMonkey tinderboxes which don't support WebGL.
         if (navigator.userAgent.match(/ SeaMonkey\//))
           todo(false, errmsg + " (This is expected on SeaMonkey (tinderboxes).)");
         else
           ok(false, errmsg);
         dump("WebGL mochitest failed: " + errmsg + "\n");
         reporter.finishedTestSuite();
+        return;
     }
-  };
+
+    statusTextNode.textContent = 'Loading test lists...';
+    var iframe = document.getElementById("testframe");
+    var testHarness = new WebGLTestHarnessModule.TestHarness(
+        iframe,
+        '00_test_list.txt',
+        function(type, msg, success) {
+          return reporter.reportFunc(type, msg, success);
+        },
+        OPTIONS);
+    // Make timeout delay much higher when running under valgrind.
+    testHarness.setTimeoutDelay(20000);
+    window.webglTestHarness = testHarness;
+  }
 
   SimpleTest.requestLongerTimeout(3);
 
   var statusElem = document.getElementById("status");
   var statusTextNode = document.createTextNode('');
   statusElem.appendChild(statusTextNode);
 
   var expectedtofailElem = document.getElementById("expectedtofail");
--- a/content/html/content/src/nsFormSubmission.cpp
+++ b/content/html/content/src/nsFormSubmission.cpp
@@ -55,17 +55,17 @@
 #include "nsIDOMFile.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsStringStream.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsNetUtil.h"
 #include "nsLinebreakConverter.h"
 #include "nsICharsetConverterManager.h"
-#include "nsICharsetAlias.h"
+#include "nsCharsetAlias.h"
 #include "nsEscape.h"
 #include "nsUnicharUtils.h"
 #include "nsIMultiplexInputStream.h"
 #include "nsIMIMEInputStream.h"
 #include "nsIMIMEService.h"
 #include "nsIConsoleService.h"
 #include "nsIScriptError.h"
 #include "nsIStringBundle.h"
@@ -777,46 +777,38 @@ nsEncodingFormSubmission::EncodeVal(cons
 // --------------------------------------------------------------------------
 
 static void
 GetSubmitCharset(nsGenericHTMLElement* aForm,
                  nsACString& oCharset)
 {
   oCharset.AssignLiteral("UTF-8"); // default to utf-8
 
-  nsresult rv = NS_OK;
   nsAutoString acceptCharsetValue;
   aForm->GetAttr(kNameSpaceID_None, nsGkAtoms::acceptcharset,
                  acceptCharsetValue);
 
   PRInt32 charsetLen = acceptCharsetValue.Length();
   if (charsetLen > 0) {
     PRInt32 offset=0;
     PRInt32 spPos=0;
     // get charset from charsets one by one
-    nsCOMPtr<nsICharsetAlias> calias(do_GetService(NS_CHARSETALIAS_CONTRACTID, &rv));
-    if (NS_FAILED(rv)) {
-      return;
-    }
-    if (calias) {
-      do {
-        spPos = acceptCharsetValue.FindChar(PRUnichar(' '), offset);
-        PRInt32 cnt = ((-1==spPos)?(charsetLen-offset):(spPos-offset));
-        if (cnt > 0) {
-          nsAutoString uCharset;
-          acceptCharsetValue.Mid(uCharset, offset, cnt);
+    do {
+      spPos = acceptCharsetValue.FindChar(PRUnichar(' '), offset);
+      PRInt32 cnt = ((-1==spPos)?(charsetLen-offset):(spPos-offset));
+      if (cnt > 0) {
+        nsAutoString uCharset;
+        acceptCharsetValue.Mid(uCharset, offset, cnt);
 
-          if (NS_SUCCEEDED(calias->
-                           GetPreferred(NS_LossyConvertUTF16toASCII(uCharset),
-                                        oCharset)))
-            return;
-        }
-        offset = spPos + 1;
-      } while (spPos != -1);
-    }
+        if (NS_SUCCEEDED(nsCharsetAlias::GetPreferred(NS_LossyConvertUTF16toASCII(uCharset),
+                                                      oCharset)))
+          return;
+      }
+      offset = spPos + 1;
+    } while (spPos != -1);
   }
   // if there are no accept-charset or all the charset are not supported
   // Get the charset from document
   nsIDocument* doc = aForm->GetDocument();
   if (doc) {
     oCharset = doc->GetDocumentCharacterSet();
   }
 }
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -91,18 +91,18 @@ public:
   }
   NS_SCRIPTABLE NS_IMETHOD SetInnerHTML(const nsAString& aInnerHTML) {
     return nsGenericHTMLElement::SetInnerHTML(aInnerHTML);
   }
 
   // nsIDOMHTMLAnchorElement
   NS_DECL_NSIDOMHTMLANCHORELEMENT  
 
-  // TODO: nsHTMLAnchorElement::SizeOfAnchorElement should call
-  // Link::SizeOfExcludingThis().  See bug 682431.
+  // DOM memory reporter participant
+  NS_DECL_SIZEOF_EXCLUDING_THIS
 
   // nsILink
   NS_IMETHOD LinkAdded() { return NS_OK; }
   NS_IMETHOD LinkRemoved() { return NS_OK; }
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
@@ -515,8 +515,15 @@ nsHTMLAnchorElement::ParseAttribute(PRIn
 }
 
 nsEventStates
 nsHTMLAnchorElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
 
+size_t
+nsHTMLAnchorElement::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+{
+  return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
+         Link::SizeOfExcludingThis(aMallocSizeOf);
+}
+
--- a/content/html/content/src/nsHTMLAreaElement.cpp
+++ b/content/html/content/src/nsHTMLAreaElement.cpp
@@ -56,18 +56,18 @@ class nsHTMLAreaElement : public nsGener
 {
 public:
   nsHTMLAreaElement(already_AddRefed<nsINodeInfo> aNodeInfo);
   virtual ~nsHTMLAreaElement();
 
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
 
-  // TODO: nsHTMLAreaElement::SizeOfAnchorElement should call
-  // Link::SizeOfExcludingThis().  See bug 682431.
+  // DOM memory reporter participant
+  NS_DECL_SIZEOF_EXCLUDING_THIS
 
   // nsIDOMNode
   NS_FORWARD_NSIDOMNODE(nsGenericHTMLElement::)
 
   // nsIDOMElement
   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
 
   // nsIDOMHTMLElement
@@ -330,8 +330,16 @@ nsHTMLAreaElement::GetHrefURI() const
   return GetHrefURIForAnchors();
 }
 
 nsEventStates
 nsHTMLAreaElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
+
+size_t
+nsHTMLAreaElement::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+{
+  return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
+         Link::SizeOfExcludingThis(aMallocSizeOf);
+}
+
--- a/content/html/content/src/nsHTMLLinkElement.cpp
+++ b/content/html/content/src/nsHTMLLinkElement.cpp
@@ -79,18 +79,18 @@ public:
   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLElement::)
 
   // nsIDOMHTMLElement
   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
 
   // nsIDOMHTMLLinkElement
   NS_DECL_NSIDOMHTMLLINKELEMENT
 
-  // TODO: nsHTMLLinkElement::SizeOfAnchorElement should call
-  // Link::SizeOfExcludingThis().  See bug 682431.
+  // DOM memory reporter participant
+  NS_DECL_SIZEOF_EXCLUDING_THIS
 
   // nsILink
   NS_IMETHOD    LinkAdded();
   NS_IMETHOD    LinkRemoved();
 
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               bool aCompileEventHandlers);
@@ -453,8 +453,16 @@ nsHTMLLinkElement::GetStyleSheetInfo(nsA
   return;
 }
 
 nsEventStates
 nsHTMLLinkElement::IntrinsicState() const
 {
   return Link::LinkState() | nsGenericHTMLElement::IntrinsicState();
 }
+
+size_t
+nsHTMLLinkElement::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
+{
+  return nsGenericHTMLElement::SizeOfExcludingThis(aMallocSizeOf) +
+         Link::SizeOfExcludingThis(aMallocSizeOf);
+}
+
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -35,18 +35,16 @@
  * 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 "nsICharsetAlias.h"
-
 #include "nsCOMPtr.h"
 #include "nsXPIDLString.h"
 #include "nsPrintfCString.h"
 #include "nsReadableUtils.h"
 #include "nsUnicharUtils.h"
 #include "nsHTMLDocument.h"
 #include "nsIHTMLContentSink.h"
 #include "nsIXMLContentSink.h"
@@ -97,17 +95,16 @@
 #include "nsGenericHTMLElement.h"
 #include "mozilla/css/Loader.h"
 #include "nsIHttpChannel.h"
 #include "nsIFile.h"
 #include "nsEventListenerManager.h"
 #include "nsFrameSelection.h"
 #include "nsISelectionPrivate.h"//for toStringwithformat code
 
-#include "nsICharsetAlias.h"
 #include "nsContentUtils.h"
 #include "nsJSUtils.h"
 #include "nsIDocumentEncoder.h" //for outputting selection
 #include "nsICachingChannel.h"
 #include "nsIJSContextStack.h"
 #include "nsIContentViewer.h"
 #include "nsIWyciwygChannel.h"
 #include "nsIScriptElement.h"
--- a/content/media/nsBuiltinDecoderStateMachine.cpp
+++ b/content/media/nsBuiltinDecoderStateMachine.cpp
@@ -2014,16 +2014,23 @@ void nsBuiltinDecoderStateMachine::Advan
                           UsecsToDuration(currentFrame->mTime - mStartTime);
     NS_ASSERTION(currentFrame->mTime >= mStartTime, "Should have positive frame time");
     {
       ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
       // If we have video, we want to increment the clock in steps of the frame
       // duration.
       RenderVideoFrame(currentFrame, presTime);
     }
+    // If we're no longer playing after dropping and reacquiring the lock,
+    // playback must've been stopped on the decode thread (by a seek, for
+    // example).  In that case, the current frame is probably out of date.
+    if (!IsPlaying()) {
+      ScheduleStateMachine();
+      return;
+    }
     mDecoder->GetFrameStatistics().NotifyPresentedFrame();
     PRInt64 now = DurationToUsecs(TimeStamp::Now() - mPlayStartTime) + mPlayDuration;
     remainingTime = currentFrame->mEndTime - mStartTime - now;
     currentFrame = nsnull;
   }
 
   // Cap the current time to the larger of the audio and video end time.
   // This ensures that if we're running off the system clock, we don't
--- a/content/media/nsMediaCache.cpp
+++ b/content/media/nsMediaCache.cpp
@@ -37,16 +37,17 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "mozilla/ReentrantMonitor.h"
 #include "mozilla/XPCOM.h"
 
 #include "nsMediaCache.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
+#include "nsXULAppAPI.h"
 #include "nsNetUtil.h"
 #include "prio.h"
 #include "nsThreadUtils.h"
 #include "MediaResource.h"
 #include "nsMathUtils.h"
 #include "prlog.h"
 #include "nsIPrivateBrowsingService.h"
 #include "mozilla/Preferences.h"
@@ -540,18 +541,25 @@ nsMediaCacheStream::BlockList::NotifyBlo
 }
 
 nsresult
 nsMediaCache::Init()
 {
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
   NS_ASSERTION(!mFD, "Cache file already open?");
 
+  // In single process Gecko, store the media cache in the profile directory
+  // so that multiple users can use separate media caches concurrently.
+  // In multi-process Gecko, there is no profile dir, so just store it in the
+  // system temp directory instead.
+  nsresult rv;
   nsCOMPtr<nsIFile> tmp;
-  nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(tmp));
+  const char* dir = (XRE_GetProcessType() == GeckoProcessType_Content) ?
+    NS_OS_TEMP_DIR : NS_APP_USER_PROFILE_LOCAL_50_DIR;
+  rv = NS_GetSpecialDirectory(dir, getter_AddRefs(tmp));
   NS_ENSURE_SUCCESS(rv,rv);
 
   nsCOMPtr<nsILocalFile> tmpFile = do_QueryInterface(tmp);
   NS_ENSURE_TRUE(tmpFile != nsnull, NS_ERROR_FAILURE);
 
   // We put the media cache file in
   // ${TempDir}/mozilla-media-cache/media_cache
   rv = tmpFile->AppendNative(nsDependentCString("mozilla-media-cache"));
--- a/content/svg/content/src/nsSVGUseElement.cpp
+++ b/content/svg/content/src/nsSVGUseElement.cpp
@@ -423,19 +423,17 @@ nsSVGUseElement::SyncWidthOrHeight(nsIAt
     if (mLengthAttributes[index].IsExplicitlySet()) {
       target->SetLength(aName, mLengthAttributes[index]);
       return;
     }
     if (svg) {
       // Our width/height attribute is now no longer explicitly set, so we
       // need to revert the clone's width/height to the width/height of the
       // content that's being cloned.
-      nsSVGSVGElement* svgElement =
-        static_cast<nsSVGSVGElement*>(mSource.get());
-      svgElement->SyncWidthOrHeight(aName, target);
+      TriggerReclone();
       return;
     }
     // Our width/height attribute is now no longer explicitly set, so we
     // need to set the value to 100%
     nsSVGLength2 length;
     length.Init(nsSVGUtils::XY, 0xff,
                 100, nsIDOMSVGLength::SVG_LENGTHTYPE_PERCENTAGE);
     target->SetLength(aName, length);
--- a/content/xbl/src/nsBindingManager.cpp
+++ b/content/xbl/src/nsBindingManager.cpp
@@ -36,17 +36,16 @@
  * 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 "nsCOMPtr.h"
 #include "nsIXBLService.h"
 #include "nsIInputStream.h"
-#include "nsDoubleHashtable.h"
 #include "nsIURI.h"
 #include "nsIURL.h"
 #include "nsIChannel.h"
 #include "nsXPIDLString.h"
 #include "nsIParser.h"
 #include "nsParserCIID.h"
 #include "nsNetUtil.h"
 #include "plstr.h"
--- a/content/xml/document/src/nsXMLDocument.cpp
+++ b/content/xml/document/src/nsXMLDocument.cpp
@@ -54,18 +54,16 @@
 #include "nsIDOMWindow.h"
 #include "nsIDOMDocumentType.h"
 #include "nsINameSpaceManager.h"
 #include "nsCOMPtr.h"
 #include "nsXPIDLString.h"
 #include "nsIHttpChannel.h"
 #include "nsIURI.h"
 #include "nsIServiceManager.h"
-#include "nsICharsetAlias.h"
-#include "nsICharsetAlias.h"
 #include "nsNetUtil.h"
 #include "nsDOMError.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIPrincipal.h"
 #include "nsLayoutCID.h"
 #include "nsDOMAttribute.h"
 #include "nsGUIEvent.h"
 #include "nsCExternalHandlerService.h"
--- a/content/xslt/src/xml/txDOM.h
+++ b/content/xslt/src/xml/txDOM.h
@@ -50,17 +50,18 @@
 #define MITRE_DOM
 
 #ifdef __BORLANDC__
 #include <stdlib.h>
 #endif
 
 #include "txList.h"
 #include "nsIAtom.h"
-#include "nsDoubleHashtable.h"
+#include "nsTHashtable.h"
+#include "nsBaseHashtable.h"
 #include "nsString.h"
 #include "txCore.h"
 #include "nsAutoPtr.h"
 
 #define kTxNsNodeIndexOffset 0x00000000;
 #define kTxAttrIndexOffset 0x40000000;
 #define kTxChildIndexOffset 0x80000000;
 
@@ -224,34 +225,17 @@ class NodeDefinition : public Node
     // Helperfunction for compareDocumentOrder
     OrderInfo* getOrderInfo();
 };
 
 //
 //Definition and Implementation of a Document.
 //
 
-/**
- * nsDoubleHashtable definitions for IDs
- *
- * It may be possible to share the key value with the element,
- * but that may leave entries without keys, as the entries
- * are constructed from the key value and the setting of mElement
- * happens late. As pldhash.h ain't clear on this, we store the
- * key by inheriting from PLDHashStringEntry.
- */
-class txIDEntry : public PLDHashStringEntry
-{
-public:
-    txIDEntry(const void* aKey) : PLDHashStringEntry(aKey), mElement(nsnull)
-    {
-    }
-    Element* mElement;
-};
-DECL_DHASH_WRAPPER(txIDMap, txIDEntry, nsAString&)
+typedef nsTHashtable<nsBaseHashtableET<nsStringHashKey, Element*> > txIDMap;
 
 class Document : public NodeDefinition
 {
   public:
     Document();
 
     Element* getDocumentElement();
 
--- a/content/xslt/src/xslt/txKey.h
+++ b/content/xslt/src/xslt/txKey.h
@@ -34,17 +34,17 @@
  * 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 txKey_h__
 #define txKey_h__
 
-#include "nsDoubleHashtable.h"
+#include "nsTHashtable.h"
 #include "txNodeSet.h"
 #include "txList.h"
 #include "txXSLTPatterns.h"
 #include "txXMLUtils.h"
 
 class txPattern;
 class Expr;
 class txExecutionState;
@@ -63,31 +63,41 @@ public:
 
     txExpandedName mKeyName;
     nsString mKeyValue;
     PRInt32 mRootIdentifier;
 };
 
 struct txKeyValueHashEntry : public PLDHashEntryHdr
 {
-    txKeyValueHashEntry(const void* aKey)
-        : mKey(*static_cast<const txKeyValueHashKey*>(aKey)),
-          mNodeSet(new txNodeSet(nsnull))
-    {
-    }
+public:
+    typedef const txKeyValueHashKey& KeyType;
+    typedef const txKeyValueHashKey* KeyTypePointer;
+
+    txKeyValueHashEntry(KeyTypePointer aKey)
+        : mKey(*aKey),
+          mNodeSet(new txNodeSet(nsnull)) { }
 
-    // @see nsDoubleHashtable.h
-    bool MatchEntry(const void* aKey) const;
-    static PLDHashNumber HashKey(const void* aKey);
+    txKeyValueHashEntry(const txKeyValueHashEntry& entry)
+        : mKey(entry.mKey),
+          mNodeSet(entry.mNodeSet) { }
+
+    bool KeyEquals(KeyTypePointer aKey) const;
+
+    static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+    static PLDHashNumber HashKey(KeyTypePointer aKey);
+
+    enum { ALLOW_MEMMOVE = true };
     
     txKeyValueHashKey mKey;
     nsRefPtr<txNodeSet> mNodeSet;
 };
 
-DECL_DHASH_WRAPPER(txKeyValueHash, txKeyValueHashEntry, txKeyValueHashKey&)
+typedef nsTHashtable<txKeyValueHashEntry> txKeyValueHash;
 
 class txIndexedKeyHashKey
 {
 public:
     txIndexedKeyHashKey(txExpandedName aKeyName,
                         PRInt32 aRootIdentifier)
         : mKeyName(aKeyName),
           mRootIdentifier(aRootIdentifier)
@@ -95,32 +105,41 @@ public:
     }
 
     txExpandedName mKeyName;
     PRInt32 mRootIdentifier;
 };
 
 struct txIndexedKeyHashEntry : public PLDHashEntryHdr
 {
-    txIndexedKeyHashEntry(const void* aKey)
-        : mKey(*static_cast<const txIndexedKeyHashKey*>(aKey)),
-          mIndexed(false)
-    {
-    }
+public:
+    typedef const txIndexedKeyHashKey& KeyType;
+    typedef const txIndexedKeyHashKey* KeyTypePointer;
+
+    txIndexedKeyHashEntry(KeyTypePointer aKey)
+        : mKey(*aKey),
+          mIndexed(false) { }
 
-    // @see nsDoubleHashtable.h
-    bool MatchEntry(const void* aKey) const;
-    static PLDHashNumber HashKey(const void* aKey);
+    txIndexedKeyHashEntry(const txIndexedKeyHashEntry& entry)
+        : mKey(entry.mKey),
+          mIndexed(entry.mIndexed) { }
+
+    bool KeyEquals(KeyTypePointer aKey) const;
+
+    static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+
+    static PLDHashNumber HashKey(KeyTypePointer aKey);
+
+    enum { ALLOW_MEMMOVE = true };
 
     txIndexedKeyHashKey mKey;
     bool mIndexed;
 };
 
-DECL_DHASH_WRAPPER(txIndexedKeyHash, txIndexedKeyHashEntry,
-                   txIndexedKeyHashKey&)
+typedef nsTHashtable<txIndexedKeyHashEntry> txIndexedKeyHash;
 
 /**
  * Class holding all <xsl:key>s of a particular expanded name in the
  * stylesheet.
  */
 class txXSLKey {
     
 public:
--- a/content/xslt/src/xslt/txKeyFunctionCall.cpp
+++ b/content/xslt/src/xslt/txKeyFunctionCall.cpp
@@ -147,78 +147,60 @@ txKeyFunctionCall::getNameAtom(nsIAtom**
     return NS_OK;
 }
 #endif
 
 /**
  * Hash functions
  */
 
-DHASH_WRAPPER(txKeyValueHash, txKeyValueHashEntry, txKeyValueHashKey&)
-DHASH_WRAPPER(txIndexedKeyHash, txIndexedKeyHashEntry, txIndexedKeyHashKey&)
-
 bool
-txKeyValueHashEntry::MatchEntry(const void* aKey) const
+txKeyValueHashEntry::KeyEquals(KeyTypePointer aKey) const
 {
-    const txKeyValueHashKey* key =
-        static_cast<const txKeyValueHashKey*>(aKey);
-
-    return mKey.mKeyName == key->mKeyName &&
-           mKey.mRootIdentifier == key->mRootIdentifier &&
-           mKey.mKeyValue.Equals(key->mKeyValue);
+    return mKey.mKeyName == aKey->mKeyName &&
+           mKey.mRootIdentifier == aKey->mRootIdentifier &&
+           mKey.mKeyValue.Equals(aKey->mKeyValue);
 }
 
 PLDHashNumber
-txKeyValueHashEntry::HashKey(const void* aKey)
+txKeyValueHashEntry::HashKey(KeyTypePointer aKey)
 {
-    const txKeyValueHashKey* key =
-        static_cast<const txKeyValueHashKey*>(aKey);
-
-    return key->mKeyName.mNamespaceID ^
-           NS_PTR_TO_INT32(key->mKeyName.mLocalName.get()) ^
-           key->mRootIdentifier ^
-           HashString(key->mKeyValue);
+    return aKey->mKeyName.mNamespaceID ^
+           NS_PTR_TO_INT32(aKey->mKeyName.mLocalName.get()) ^
+           aKey->mRootIdentifier ^
+           HashString(aKey->mKeyValue);
 }
 
 bool
-txIndexedKeyHashEntry::MatchEntry(const void* aKey) const
+txIndexedKeyHashEntry::KeyEquals(KeyTypePointer aKey) const
 {
-    const txIndexedKeyHashKey* key =
-        static_cast<const txIndexedKeyHashKey*>(aKey);
-
-    return mKey.mKeyName == key->mKeyName &&
-           mKey.mRootIdentifier == key->mRootIdentifier;
+    return mKey.mKeyName == aKey->mKeyName &&
+           mKey.mRootIdentifier == aKey->mRootIdentifier;
 }
 
 PLDHashNumber
-txIndexedKeyHashEntry::HashKey(const void* aKey)
+txIndexedKeyHashEntry::HashKey(KeyTypePointer aKey)
 {
-    const txIndexedKeyHashKey* key =
-        static_cast<const txIndexedKeyHashKey*>(aKey);
-
-    return key->mKeyName.mNamespaceID ^
-           NS_PTR_TO_INT32(key->mKeyName.mLocalName.get()) ^
-           key->mRootIdentifier;
+    return aKey->mKeyName.mNamespaceID ^
+           NS_PTR_TO_INT32(aKey->mKeyName.mLocalName.get()) ^
+           aKey->mRootIdentifier;
 }
 
 /*
  * Class managing XSLT-keys
  */
 
 nsresult
 txKeyHash::getKeyNodes(const txExpandedName& aKeyName,
                        const txXPathNode& aRoot,
                        const nsAString& aKeyValue,
                        bool aIndexIfNotFound,
                        txExecutionState& aEs,
                        txNodeSet** aResult)
 {
-    NS_ENSURE_TRUE(mKeyValues.mHashTable.ops && mIndexedKeys.mHashTable.ops,
-                   NS_ERROR_OUT_OF_MEMORY);
-
     *aResult = nsnull;
 
     PRInt32 identifier = txXPathNodeUtils::getUniqueIdentifier(aRoot);
 
     txKeyValueHashKey valueKey(aKeyName, identifier, aKeyValue);
     txKeyValueHashEntry* valueEntry = mKeyValues.GetEntry(valueKey);
     if (valueEntry) {
         *aResult = valueEntry->mNodeSet;
@@ -236,17 +218,17 @@ txKeyHash::getKeyNodes(const txExpandedN
         // indexed, so don't bother investigating.
         *aResult = mEmptyNodeSet;
         NS_ADDREF(*aResult);
 
         return NS_OK;
     }
 
     txIndexedKeyHashKey indexKey(aKeyName, identifier);
-    txIndexedKeyHashEntry* indexEntry = mIndexedKeys.AddEntry(indexKey);
+    txIndexedKeyHashEntry* indexEntry = mIndexedKeys.PutEntry(indexKey);
     NS_ENSURE_TRUE(indexEntry, NS_ERROR_OUT_OF_MEMORY);
 
     if (indexEntry->mIndexed) {
         // The key was indexed and apparently didn't contain this value so
         // return the empty nodeset.
         *aResult = mEmptyNodeSet;
         NS_ADDREF(*aResult);
 
@@ -407,32 +389,32 @@ nsresult txXSLKey::testNode(const txXPat
                                             (static_cast<txAExprResult*>
                                                         (exprResult));
                 PRInt32 i;
                 for (i = 0; i < res->size(); ++i) {
                     val.Truncate();
                     txXPathNodeUtils::appendNodeValue(res->get(i), val);
 
                     aKey.mKeyValue.Assign(val);
-                    txKeyValueHashEntry* entry = aKeyValueHash.AddEntry(aKey);
+                    txKeyValueHashEntry* entry = aKeyValueHash.PutEntry(aKey);
                     NS_ENSURE_TRUE(entry && entry->mNodeSet,
                                    NS_ERROR_OUT_OF_MEMORY);
 
                     if (entry->mNodeSet->isEmpty() ||
                         entry->mNodeSet->get(entry->mNodeSet->size() - 1) !=
                         aNode) {
                         entry->mNodeSet->append(aNode);
                     }
                 }
             }
             else {
                 exprResult->stringValue(val);
 
                 aKey.mKeyValue.Assign(val);
-                txKeyValueHashEntry* entry = aKeyValueHash.AddEntry(aKey);
+                txKeyValueHashEntry* entry = aKeyValueHash.PutEntry(aKey);
                 NS_ENSURE_TRUE(entry && entry->mNodeSet,
                                NS_ERROR_OUT_OF_MEMORY);
 
                 if (entry->mNodeSet->isEmpty() ||
                     entry->mNodeSet->get(entry->mNodeSet->size() - 1) !=
                     aNode) {
                     entry->mNodeSet->append(aNode);
                 }
--- a/content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp
+++ b/content/xslt/src/xslt/txMozillaStylesheetCompiler.cpp
@@ -34,17 +34,17 @@
  * 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 "nsCOMArray.h"
 #include "nsIAuthPrompt.h"
-#include "nsICharsetAlias.h"
+#include "nsCharsetAlias.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
 #include "nsIExpatSink.h"
 #include "nsIChannelEventSink.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsILoadGroup.h"
 #include "nsINameSpaceManager.h"
@@ -277,52 +277,47 @@ txStylesheetSink::OnDataAvailable(nsIReq
 
     return mListener->OnDataAvailable(aRequest, aContext, aInputStream,
                                       aOffset, aCount);
 }
 
 NS_IMETHODIMP
 txStylesheetSink::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
-    nsCAutoString charset(NS_LITERAL_CSTRING("UTF-8"));
     PRInt32 charsetSource = kCharsetFromDocTypeDefault;
 
     nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
 
     // check channel's charset...
     nsCAutoString charsetVal;
-    nsresult rv = channel->GetContentCharset(charsetVal);
-    if (NS_SUCCEEDED(rv)) {
-        nsCOMPtr<nsICharsetAlias> calias =
-            do_GetService(NS_CHARSETALIAS_CONTRACTID);
+    nsCAutoString charset;
+    if (NS_SUCCEEDED(channel->GetContentCharset(charsetVal))) {
+        if (NS_SUCCEEDED(nsCharsetAlias::GetPreferred(charsetVal, charset))) {
+            charsetSource = kCharsetFromChannel;
+        }
+    }
 
-        if (calias) {
-            nsCAutoString preferred;
-            rv = calias->GetPreferred(charsetVal,
-                                      preferred);
-            if (NS_SUCCEEDED(rv)) {            
-                charset = preferred;
-                charsetSource = kCharsetFromChannel;
-             }
-        }
+    if (charset.IsEmpty()) {
+      charset.AssignLiteral("UTF-8");
     }
 
     nsCOMPtr<nsIParser> parser = do_QueryInterface(aContext);
     parser->SetDocumentCharset(charset, charsetSource);
 
     nsCAutoString contentType;
     channel->GetContentType(contentType);
 
     // Time to sniff! Note: this should go away once file channels do
     // sniffing themselves.
     nsCOMPtr<nsIURI> uri;
     channel->GetURI(getter_AddRefs(uri));
     bool sniff;
     if (NS_SUCCEEDED(uri->SchemeIs("file", &sniff)) && sniff &&
         contentType.Equals(UNKNOWN_CONTENT_TYPE)) {
+        nsresult rv;
         nsCOMPtr<nsIStreamConverterService> serv =
             do_GetService("@mozilla.org/streamConverters;1", &rv);
         if (NS_SUCCEEDED(rv)) {
             nsCOMPtr<nsIStreamListener> converter;
             rv = serv->AsyncConvertData(UNKNOWN_CONTENT_TYPE,
                                         "*/*",
                                         mListener,
                                         aContext,
--- a/content/xslt/src/xslt/txMozillaTextOutput.cpp
+++ b/content/xslt/src/xslt/txMozillaTextOutput.cpp
@@ -40,17 +40,17 @@
 #include "nsContentCID.h"
 #include "nsIContent.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsIDocumentTransformer.h"
 #include "nsNetUtil.h"
 #include "nsIParser.h"
-#include "nsICharsetAlias.h"
+#include "nsCharsetAlias.h"
 #include "nsIPrincipal.h"
 #include "txURIUtils.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
 #include "nsGkAtoms.h"
 
 using namespace mozilla::dom;
 
@@ -179,21 +179,19 @@ txMozillaTextOutput::createResultDocumen
 
     // Reset and set up document
     URIUtils::ResetWithSource(mDocument, aSourceDocument);
 
     // Set the charset
     if (!mOutputFormat.mEncoding.IsEmpty()) {
         NS_LossyConvertUTF16toASCII charset(mOutputFormat.mEncoding);
         nsCAutoString canonicalCharset;
-        nsCOMPtr<nsICharsetAlias> calias =
-            do_GetService("@mozilla.org/intl/charsetalias;1");
 
-        if (calias &&
-            NS_SUCCEEDED(calias->GetPreferred(charset, canonicalCharset))) {
+        if (NS_SUCCEEDED(nsCharsetAlias::GetPreferred(charset,
+                                                      canonicalCharset))) {
             mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
             mDocument->SetDocumentCharacterSet(canonicalCharset);
         }
     }
 
     // Notify the contentsink that the document is created
     nsCOMPtr<nsITransformObserver> observer = do_QueryReferent(mObserver);
     if (observer) {
--- a/content/xslt/src/xslt/txMozillaXMLOutput.cpp
+++ b/content/xslt/src/xslt/txMozillaXMLOutput.cpp
@@ -59,17 +59,17 @@
 #include "nsCSSStyleSheet.h"
 #include "txStringUtils.h"
 #include "txURIUtils.h"
 #include "nsIHTMLDocument.h"
 #include "nsIStyleSheetLinkingElement.h"
 #include "nsIDocumentTransformer.h"
 #include "mozilla/css/Loader.h"
 #include "mozilla/dom/Element.h"
-#include "nsICharsetAlias.h"
+#include "nsCharsetAlias.h"
 #include "nsIHTMLContentSink.h"
 #include "nsContentUtils.h"
 #include "txXMLUtils.h"
 #include "nsContentSink.h"
 #include "nsINode.h"
 #include "nsContentCreatorFunctions.h"
 #include "txError.h"
 
@@ -852,21 +852,17 @@ txMozillaXMLOutput::createResultDocument
 
     // Reset and set up the document
     URIUtils::ResetWithSource(mDocument, aSourceDocument);
 
     // Set the charset
     if (!mOutputFormat.mEncoding.IsEmpty()) {
         NS_LossyConvertUTF16toASCII charset(mOutputFormat.mEncoding);
         nsCAutoString canonicalCharset;
-        nsCOMPtr<nsICharsetAlias> calias =
-            do_GetService("@mozilla.org/intl/charsetalias;1");
-
-        if (calias &&
-            NS_SUCCEEDED(calias->GetPreferred(charset, canonicalCharset))) {
+        if (NS_SUCCEEDED(nsCharsetAlias::GetPreferred(charset, canonicalCharset))) {
             mDocument->SetDocumentCharacterSetSource(kCharsetFromOtherComponent);
             mDocument->SetDocumentCharacterSet(canonicalCharset);
         }
     }
 
     // Set the mime-type
     if (!mOutputFormat.mMediaType.IsEmpty()) {
         mDocument->SetContentType(mOutputFormat.mMediaType);
new file mode 100644
--- /dev/null
+++ b/dom/base/DOMRequestHelper.jsm
@@ -0,0 +1,80 @@
+/* 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/. */
+
+/**
+  * helper object for APIs that deal with DOMRequest and need to release them properly
+  * when the window goes out of scope
+  */
+const Cu = Components.utils; 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+let EXPORTED_SYMBOLS = ["DOMRequestIpcHelper"];
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyGetter(Services, "rs", function() {
+  return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
+});
+
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+  return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
+function DOMRequestIpcHelper() {
+}
+
+DOMRequestIpcHelper.prototype = {
+  getRequestId: function(aRequest) {
+    let id = "id" + this._getRandomId();
+    this._requests[id] = aRequest;
+    return id;
+  },
+
+  getRequest: function(aId) {
+    if (this._requests[aId])
+      return this._requests[aId];
+  },
+
+  removeRequest: function(aId) {
+    if (this._requests[aId])
+      delete this._requests[aId];
+  },
+
+  _getRandomId: function() {
+    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
+  },
+
+  observe: function(aSubject, aTopic, aData) {
+    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
+    if (wId == this.innerWindowID) {
+      Services.obs.removeObserver(this, "inner-window-destroyed");
+      this._requests = [];
+      this._window = null;
+      this._messages.forEach((function(msgName) {
+        cpmm.removeMessageListener(msgName, this);
+      }).bind(this));
+      if(this.uninit)
+        this.uninit();
+    }
+  },
+
+  initHelper: function(aWindow, aMessages) {
+    this._messages = aMessages;
+    this._requests = [];
+    this._window = aWindow;
+    let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
+    this.innerWindowID = util.currentInnerWindowID;
+    this._id = this._getRandomId();
+    Services.obs.addObserver(this, "inner-window-destroyed", false);
+    this._messages.forEach((function(msgName) {
+      cpmm.addMessageListener(msgName, this);
+    }).bind(this));
+  },
+
+  createRequest: function() {
+    return Services.rs.createRequest(this._window);
+  }
+}
--- a/dom/base/Makefile.in
+++ b/dom/base/Makefile.in
@@ -64,16 +64,19 @@ EXTRA_COMPONENTS = \
 	        Webapps.js \
 	        Webapps.manifest \
 		$(NULL)
 
 EXTRA_JS_MODULES += Webapps.jsm \
 		$(NULL)
 endif
 
+EXTRA_JS_MODULES += DOMRequestHelper.jsm \
+		$(NULL)
+
 XPIDLSRCS = \
   nsIDOMDOMError.idl \
   nsIDOMDOMRequest.idl \
   nsIEntropyCollector.idl \
   nsIScriptChannel.idl \
   $(NULL)
 
 EXPORTS = \
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -65,16 +65,18 @@
 #include "nsIJSContextStack.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsContentUtils.h"
 #include "nsUnicharUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Telemetry.h"
 #include "BatteryManager.h"
 #include "PowerManager.h"
+#include "nsIDOMWakeLock.h"
+#include "nsIPowerManagerService.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
@@ -167,17 +169,20 @@ Navigator::Invalidate()
     mNotification = nsnull;
   }
 
   if (mBatteryManager) {
     mBatteryManager->Shutdown();
     mBatteryManager = nsnull;
   }
 
-  mPowerManager = nsnull;
+  if (mPowerManager) {
+    mPowerManager->Shutdown();
+    mPowerManager = nsnull;
+  }
 
   if (mSmsManager) {
     mSmsManager->Shutdown();
     mSmsManager = nsnull;
   }
 
 #ifdef MOZ_B2G_RIL
   if (mTelephony) {
@@ -953,25 +958,48 @@ Navigator::GetMozBattery(nsIDOMMozBatter
   NS_ADDREF(*aBattery = mBatteryManager);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 Navigator::GetMozPower(nsIDOMMozPowerManager** aPower)
 {
+  *aPower = nsnull;
+
   if (!mPowerManager) {
+    nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+    NS_ENSURE_TRUE(win, NS_OK);
+
     mPowerManager = new power::PowerManager();
+    mPowerManager->Init(win);
   }
 
-  NS_ADDREF(*aPower = mPowerManager);
+  nsCOMPtr<nsIDOMMozPowerManager> power =
+    do_QueryInterface(NS_ISUPPORTS_CAST(nsIDOMMozPowerManager*, mPowerManager));
+  power.forget(aPower);
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+Navigator::RequestWakeLock(const nsAString &aTopic, nsIDOMMozWakeLock **aWakeLock)
+{
+  *aWakeLock = nsnull;
+
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+  NS_ENSURE_TRUE(win, NS_OK);
+
+  nsCOMPtr<nsIPowerManagerService> pmService =
+    do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+  NS_ENSURE_TRUE(pmService, NS_OK);
+
+  return pmService->NewWakeLock(aTopic, win, aWakeLock);
+}
+
 //*****************************************************************************
 //    Navigator::nsIDOMNavigatorSms
 //*****************************************************************************
 
 bool
 Navigator::IsSmsAllowed() const
 {
   static const bool defaultSmsPermission = false;
--- a/dom/base/Webapps.js
+++ b/dom/base/Webapps.js
@@ -1,359 +1,407 @@
-/* ***** 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 Open Web Apps.
- *
- * 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):
- *   Fabrice Desré <fabrice@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 ***** */
+/* 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/. */
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
+
+XPCOMUtils.defineLazyGetter(Services, "rs", function() {
+  return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
+});
+
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+  return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
+function convertAppsArray(aApps, aWindow) {
+  let apps = new Array();
+  for (let i = 0; i < aApps.length; i++) {
+    let app = aApps[i];
+    apps.push(new WebappsApplication(aWindow, app.origin, app.manifest, app.manifestURL, 
+                                     app.receipts, app.installOrigin, app.installTime));
+  }
+  return apps;
+}
 
 function WebappsRegistry() {
-  this.messages = ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
-                   "Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO",
-                   "Webapps:Enumerate:Return:OK", "Webapps:Enumerate:Return:KO"];
-
-  this.mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
-
-  this.messages.forEach((function(msgName) {
-    this.mm.addMessageListener(msgName, this);
-  }).bind(this));
-
-  this._window = null;
-  this._id = this._getRandomId();
-  this._callbacks = [];
 }
 
 WebappsRegistry.prototype = {
-  _onerror: null,
-  _oninstall: null,
-  _onuninstall: null,
+  __proto__: DOMRequestIpcHelper.prototype,
 
   /** from https://developer.mozilla.org/en/OpenWebApps/The_Manifest
    * only the name property is mandatory
    */
   checkManifest: function(aManifest, aInstallOrigin) {
     // TODO : check for install_allowed_from
     if (aManifest.name == undefined)
       return false;
-    
+
     if (aManifest.installs_allowed_from) {
       ok = false;
       aManifest.installs_allowed_from.forEach(function(aOrigin) {
         if (aOrigin == "*" || aOrigin == aInstallOrigin)
           ok = true;
       });
       return ok;
     }
     return true;
   },
-  
-  getCallbackId: function(aCallback) {
-    let id = "id" + this._getRandomId();
-    this._callbacks[id] = aCallback;
-    return id;
-  },
-  
-  getCallback: function(aId) {
-    return this._callbacks[aId];
-  },
-
-  removeCallback: function(aId) {
-    if (this._callbacks[aId])
-      delete this._callbacks[aId];
-  },
-  
-  _getRandomId: function() {
-    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
-  },
-
-  _convertAppsArray: function(aApps) {
-    let apps = new Array();
-    for (let i = 0; i < aApps.length; i++) {
-      let app = aApps[i];
-      apps.push(new WebappsApplication(app.origin, app.manifest, app.receipt, app.installOrigin, app.installTime));
-    }
-    return apps;
-  },
-
-  set oninstall(aCallback) {
-    if (this.hasPrivileges)
-      this._oninstall = aCallback;
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-  
-  set onuninstall(aCallback) {
-    if (this.hasPrivileges)
-      this._onuninstall = aCallback;
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
-  },
-
-  set onerror(aCallback) {
-    this._onerror = aCallback;
-  },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
-    if (!(msg.oid == this._id || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
+    if (msg.oid != this._id)
       return
+    let req = this.getRequest(msg.requestID);
+    if (!req)
+      return;
     let app = msg.app;
-    let cb;
     switch (aMessage.name) {
       case "Webapps:Install:Return:OK":
-        if (this._oninstall)
-          this._oninstall.handleEvent(new WebappsApplication(app.origin, app.manifest, app.receipt,
+        Services.rs.fireSuccess(req, new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
                                                 app.installOrigin, app.installTime));
         break;
       case "Webapps:Install:Return:KO":
-        if (this._onerror)
-          this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.DENIED));
-        break;
-      case "Webapps:Uninstall:Return:OK":
-        if (this._onuninstall)
-          this._onuninstall.handleEvent(new WebappsApplication(msg.origin, null, null, null, 0));
+        Services.rs.fireError(req, "DENIED");
         break;
-      case "Webapps:Uninstall:Return:KO":
-        if (this._onerror)
-          this._onerror.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
-        break;
-      case "Webapps:Enumerate:Return:OK":
-        cb = this.getCallback(msg.callbackID);
-        if (cb.success) {
-          let apps = this._convertAppsArray(msg.apps);
-          cb.success.handleEvent(apps, apps.length);
+      case "Webapps:GetSelf:Return:OK":
+        if (msg.apps.length) {
+          app = msg.apps[0];
+          Services.rs.fireSuccess(req, new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
+                                                  app.installOrigin, app.installTime));
+        } else {
+          Services.rs.fireSuccess(req, null);
         }
         break;
-      case "Webapps:Enumerate:Return:KO":
-        cb = this.getCallback(msg.callbackID);
-        if (cb.error)
-          cb.error.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
+      case "Webapps:GetInstalled:Return:OK":
+        Services.rs.fireSuccess(req, convertAppsArray(msg.apps, this._window));
+        break;
+      case "Webapps:GetSelf:Return:KO":
+      case "Webapps:GetInstalled:Return:KO":
+        Services.rs.fireError(req, "ERROR");
         break;
     }
-    this.removeCallback(msg.callbackID);
-  },
-  
-  _fireError: function(aCode) {
-    if (!this._onerror)
-      return;
-    this._onerror.handleEvent(new RegistryError(aCode));
+    this.removeRequest(msg.requestID);
   },
 
   _getOrigin: function(aURL) {
     let uri = Services.io.newURI(aURL, null, null);
     return uri.prePath; 
   },
 
   // mozIDOMApplicationRegistry implementation
   
-  install: function(aURL, aReceipt) {
+  install: function(aURL, aParams) {
+    let request = this.createRequest();
+    let requestID = this.getRequestId(request);
     let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].createInstance(Ci.nsIXMLHttpRequest);
     xhr.open("GET", aURL, true);
 
     xhr.addEventListener("load", (function() {
       if (xhr.status == 200) {
         try {
           let installOrigin = this._getOrigin(this._window.location.href);
           let manifest = JSON.parse(xhr.responseText, installOrigin);
           if (!this.checkManifest(manifest, installOrigin)) {
-            this._fireError(Ci.mozIDOMApplicationRegistryError.INVALID_MANIFEST);
+            Services.rs.fireError(request, "INVALID_MANIFEST");
           } else {
-            this.mm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
-                                                          origin: this._getOrigin(aURL),
-                                                          manifest: manifest,
-                                                          receipt: aReceipt },
-                                                          from: this._window.location.href,
-                                                          oid: this._id });
+            let receipts = (aParams && aParams.receipts && Array.isArray(aParams.receipts)) ? aParams.receipts : [];
+            cpmm.sendAsyncMessage("Webapps:Install", { app: { installOrigin: installOrigin,
+                                                              origin: this._getOrigin(aURL),
+                                                              manifestURL: aURL,
+                                                              manifest: manifest,
+                                                              receipts: receipts },
+                                                              from: this._window.location.href,
+                                                              oid: this._id,
+                                                              requestID: requestID });
           }
         } catch(e) {
-          this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_PARSE_ERROR);
+          Services.rs.fireError(request, "MANIFEST_PARSE_ERROR");
         }
       }
       else {
-        this._fireError(Ci.mozIDOMApplicationRegistryError.MANIFEST_URL_ERROR);
+        Services.rs.fireError(request, "MANIFEST_URL_ERROR");
       }      
     }).bind(this), false);
 
     xhr.addEventListener("error", (function() {
-      this._fireError(Ci.mozIDOMApplicationRegistryError.NETWORK_ERROR);
+      Services.rs.fireError(request, "NETWORK_ERROR");
     }).bind(this), false);
 
     xhr.send(null);
-  },
-
-  uninstall: function(aOrigin) {
-    if (this.hasPrivileges)
-      this.mm.sendAsyncMessage("Webapps:Uninstall", { from: this._window.location.href,
-                                                      origin: aOrigin,
-                                                      oid: this._id });
-    else
-      throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
+    return request;
   },
 
-  launch: function(aOrigin) {
-    this.mm.sendAsyncMessage("Webapps:Launch", { origin: aOrigin,
-                                                 from: this._window.location.href});
-  },
-  
-  enumerate: function(aSuccess, aError) {
-    this.mm.sendAsyncMessage("Webapps:Enumerate", { from: this._window.location.href,
-                                                    origin: this._getOrigin(this._window.location.href),
-                                                    oid: this._id,
-                                                    callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
+  getSelf: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:GetSelf", { origin: this._getOrigin(this._window.location.href),
+                                               oid: this._id,
+                                               requestID: this.getRequestId(request) });
+    return request;
   },
 
-  enumerateAll: function(aSuccess, aError) {
-    if (this.hasPrivileges) {
-      this.mm.sendAsyncMessage("Webapps:EnumerateAll", { from: this._window.location.href,
-                                                    origin: this._getOrigin(this._window.location.href),
+  getInstalled: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:GetInstalled", { origin: this._getOrigin(this._window.location.href),
                                                     oid: this._id,
-                                                    callbackID:  this.getCallbackId({ success: aSuccess, error: aError }) });
-    } else {
-      if (aError)
-        aError.handleEvent(new RegistryError(Ci.mozIDOMApplicationRegistryError.PERMISSION_DENIED));
-    }
+                                                    requestID: this.getRequestId(request) });
+    return request;
   },
 
-  observe: function(aSubject, aTopic, aData) {
-    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    if (wId == this.innerWindowID) {
-      Services.obs.removeObserver(this, "inner-window-destroyed");
-      this._oninstall = null;
-      this._onuninstall = null;
-      this._onerror = null;
-      this._callbacks = [];
-      this._window = null;
-    }
+  get mgmt() {
+    if (!this._mgmt)
+      this._mgmt = new WebappsApplicationMgmt(this._window);
+    return this._mgmt;
+  },
+
+  uninit: function() {
+    this._mgmt = null;
   },
 
   // nsIDOMGlobalPropertyInitializer implementation
   init: function(aWindow) {
-    dump("DOMApplicationRegistry::init() " + aWindow + "\n");
-    this._window = aWindow;
-    this._window.appId = this._id;
-    let from = Services.io.newURI(this._window.location.href, null, null);
-    let perm = Services.perms.testExactPermission(from, "webapps-manage");
-
-    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
-    this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
+    this.initHelper(aWindow, ["Webapps:Install:Return:OK", "Webapps:Install:Return:KO",
+                              "Webapps:GetInstalled:Return:OK", "Webapps:GetInstalled:Return:KO",
+                              "Webapps:GetSelf:Return:OK", "Webapps:GetSelf:Return:KO"]);
 
     Services.obs.addObserver(this, "inner-window-destroyed", false);
     let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-    this.innerWindowID = util.currentInnerWindowID;
+    this._id = util.outerWindowID;
   },
   
   classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistry, Ci.nsIDOMGlobalPropertyInitializer]),
   
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{fff440b3-fae2-45c1-bf03-3b5a2e432270}"),
                                     contractID: "@mozilla.org/webapps;1",
                                     interfaces: [Ci.mozIDOMApplicationRegistry],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Registry"})
 }
 
-function WebappsApplication(aOrigin, aManifest, aReceipt, aInstallOrigin, aInstallTime) {
+/**
+  * mozIDOMApplication object
+  */
+function WebappsApplication(aWindow, aOrigin, aManifest, aManifestURL, aReceipts, aInstallOrigin, aInstallTime) {
   this._origin = aOrigin;
   this._manifest = aManifest;
-  this._receipt = aReceipt;
+  this._manifestURL = aManifestURL;
+  this._receipts = aReceipts;
   this._installOrigin = aInstallOrigin;
   this._installTime = aInstallTime;
+
+  this.initHelper(aWindow, ["Webapps:Uninstall:Return:OK", "Webapps:Uninstall:Return:KO"]);
 }
 
 WebappsApplication.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
   _origin: null,
   _manifest: null,
-  _receipt: null,
+  _manifestURL: null,
+  _receipts: [],
   _installOrigin: null,
   _installTime: 0,
 
   get origin() {
     return this._origin;
   },
 
   get manifest() {
     return this._manifest;
   },
 
-  get receipt() {
-    return this._receipt;
+  get manifestURL() {
+    return this._manifestURL;
+  },
+
+  get receipts() {
+    return this._receipts;
   },
 
   get installOrigin() {
     return this._installOrigin;
   },
   
   get installTime() {
     return this._installTime;
   },
 
+  launch: function(aStartPoint) {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:Launch", { origin: this._origin,
+                                              startPoint: aStartPoint,
+                                              oid: this._id,
+                                              requestID: this.getRequestId(request) });
+    return request;
+  },
+
+  uninstall: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:Uninstall", { origin: this._origin,
+                                                 oid: this._id,
+                                                 requestID: this.getRequestId(request) });
+    return request;
+  },
+
+  receiveMessage: function(aMessage) {
+    var msg = aMessage.json;
+    let req = this.getRequest(msg.requestID);
+    if (msg.oid != this._id || !req)
+      return;
+    switch (aMessage.name) {
+      case "Webapps:Uninstall:Return:OK":
+        Services.rs.fireSuccess(req, msg.origin);
+        break;
+      case "Webapps:Uninstall:Return:KO":
+        Services.rs.fireError(req, msg.origin);
+        break;
+    }
+    this.removeRequest(msg.requestID);
+  },
+
   classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
 
   QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplication]),
 
   classInfo: XPCOMUtils.generateCI({classID: Components.ID("{723ed303-7757-4fb0-b261-4f78b1f6bd22}"),
                                     contractID: "@mozilla.org/webapps/application;1",
                                     interfaces: [Ci.mozIDOMApplication],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
                                     classDescription: "Webapps Application"})
 }
 
-function RegistryError(aCode) {
-  this._code = aCode;
+/**
+  * mozIDOMApplicationMgmt object
+  */
+function WebappsApplicationMgmt(aWindow) {
+  let principal = aWindow.document.nodePrincipal;
+  let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
+
+  let perm = principal == secMan.getSystemPrincipal() ? 
+               Ci.nsIPermissionManager.ALLOW_ACTION : 
+               Services.perms.testExactPermission(principal.URI, "webapps-manage");
+
+  //only pages with perm set can use some functions
+  this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
+
+  this.initHelper(aWindow, ["Webapps:GetAll:Return:OK", "Webapps:GetAll:Return:KO",
+                            "Webapps:Install:Return:OK", "Webapps:Uninstall:Return:OK"]);
+
+  this._oninstall = null;
+  this._onuninstall = null;
 }
 
-RegistryError.prototype = {
-  _code: null,
-  
-  get code() {
-    return this._code;
+WebappsApplicationMgmt.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
+
+  uninit: function() {
+    this._oninstall = null;
+    this._onuninstall = null;
+  },
+
+  getAll: function() {
+    let request = this.createRequest();
+    cpmm.sendAsyncMessage("Webapps:GetAll", { oid: this._id,
+                                              requestID: this.getRequestId(request),
+                                              hasPrivileges: this.hasPrivileges });
+    return request;
+  },
+
+  get oninstall() {
+    return this._oninstall;
+  },
+
+  get onuninstall() {
+    this._onuninstall;
+  },
+
+  set oninstall(aCallback) {
+    if (this.hasPrivileges)
+      this._oninstall = aCallback;
+    else
+
+      throw new Components.exception("Denied", Cr.NS_ERROR_FAILURE);
+  },
+
+  set onuninstall(aCallback) {
+    if (this.hasPrivileges)
+      this._onuninstall = aCallback;
+    else
+      throw new Components.exception("Denied", Cr.NS_ERROR_FAILURE);
   },
-  
-  classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
-
-  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationRegistryError]),
 
-  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{b4937718-11a3-400b-a69f-ab442a418569}"),
-                                    contractID: "@mozilla.org/webapps/error;1",
-                                    interfaces: [Ci.mozIDOMApplicationRegistryError],
+  receiveMessage: function(aMessage) {
+    var msg = aMessage.json;
+    let req = this.getRequest(msg.requestID);
+    // We want Webapps:Install:Return:OK and Webapps:Uninstall:Return:OK to be boradcasted
+    // to all instances of mozApps.mgmt
+    if (!((msg.oid == this._id && req) 
+       || aMessage.name == "Webapps:Install:Return:OK" || aMessage.name == "Webapps:Uninstall:Return:OK"))
+      return;
+    switch (aMessage.name) {
+      case "Webapps:GetAll:Return:OK":
+        Services.rs.fireSuccess(req, convertAppsArray(msg.apps, this._window));
+        break;
+      case "Webapps:GetAll:Return:KO":
+        Services.rs.fireError(req, "DENIED");
+        break;
+      case "Webapps:Install:Return:OK":
+        if (this._oninstall) {
+          let app = msg.app;
+          let event = new WebappsApplicationEvent(new WebappsApplication(this._window, app.origin, app.manifest, app.manifestURL, app.receipts,
+                                                app.installOrigin, app.installTime));
+          this._oninstall.handleEvent(event);
+        }
+        break;
+      case "Webapps:Uninstall:Return:OK":
+        if (this._onuninstall) {
+          let event = new WebappsApplicationEvent(new WebappsApplication(this._window, msg.origin, null, null, null, null, 0));
+          this._onuninstall.handleEvent(event);
+        }
+        break;
+    }
+    this.removeRequest(msg.requestID);
+  },
+
+  classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationMgmt]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
+                                    contractID: "@mozilla.org/webapps/application-mgmt;1",
+                                    interfaces: [Ci.mozIDOMApplicationMgmt],
                                     flags: Ci.nsIClassInfo.DOM_OBJECT,
-                                    classDescription: "Webapps Registry Error"})
+                                    classDescription: "Webapps Application Mgmt"})
 }
 
-const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication, RegistryError]);
+/**
+  * mozIDOMApplicationEvent object
+  */
+function WebappsApplicationEvent(aApp) {
+  this._app = aApp;
+}
+
+WebappsApplicationEvent.prototype = {
+  get application() {
+    return this._app;
+  },
+
+  classID: Components.ID("{5bc42b2a-9acc-49d5-a336-c353c8125e48}"),
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.mozIDOMApplicationEvent]),
+
+  classInfo: XPCOMUtils.generateCI({classID: Components.ID("{8c1bca96-266f-493a-8d57-ec7a95098c15}"),
+                                    contractID: "@mozilla.org/webapps/application-event;1",
+                                    interfaces: [Ci.mozIDOMApplicationEvent],
+                                    flags: Ci.nsIClassInfo.DOM_OBJECT,
+                                    classDescription: "Webapps Application Event"})
+}
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsRegistry, WebappsApplication]);
--- a/dom/base/Webapps.jsm
+++ b/dom/base/Webapps.jsm
@@ -1,71 +1,42 @@
-/* ***** 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 Mobile Browser.
- *
- * 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):
- *   Fabrice Desré <fabrice@mozilla.com>
- *   Mark Finkle <mfinkle@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 ***** */
+/* 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/. */
 
 const Cu = Components.utils; 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 let EXPORTED_SYMBOLS = ["DOMApplicationRegistry", "DOMApplicationManifest"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 XPCOMUtils.defineLazyGetter(this, "NetUtil", function() {
   Cu.import("resource://gre/modules/NetUtil.jsm");
   return NetUtil;
 });
 
+XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
 let DOMApplicationRegistry = {
   appsFile: null,
   webapps: { },
 
   init: function() {
-    this.mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
     let messages = ["Webapps:Install", "Webapps:Uninstall",
-                    "Webapps:Enumerate", "Webapps:EnumerateAll", "Webapps:Launch"];
+                    "Webapps:GetSelf", "Webapps:GetInstalled",
+                    "Webapps:Launch", "Webapps:GetAll"];
 
     messages.forEach((function(msgName) {
-      this.mm.addMessageListener(msgName, this);
+      ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     let appsDir = FileUtils.getDir("ProfD", ["webapps"], true, true);
     this.appsFile = FileUtils.getFile("ProfD", ["webapps", "webapps.json"], true);
 
     if (!this.appsFile.exists())
       return;
 
@@ -109,39 +80,39 @@ let DOMApplicationRegistry = {
       Cu.reportError("DOMApplicationRegistry: Could not read from " + aFile.path + " : " + ex);
       if (aCallback)
         aCallback(null);
     }
   },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.json;
-    let from = Services.io.newURI(msg.from, null, null);
-    let perm = Services.perms.testExactPermission(from, "webapps-manage");
-
-    //only pages with perm set and chrome or about pages can uninstall, enumerate all set oninstall an onuninstall
-    let hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION || from.schemeIs("chrome") || from.schemeIs("about");
 
     switch (aMessage.name) {
       case "Webapps:Install":
         // always ask for UI to install
         Services.obs.notifyObservers(this, "webapps-ask-install", JSON.stringify(msg));
         break;
+      case "Webapps:GetSelf":
+        this.getSelf(msg);
+        break;
       case "Webapps:Uninstall":
-        if (hasPrivileges)
-          this.uninstall(msg);
+        this.uninstall(msg);
         break;
       case "Webapps:Launch":
         Services.obs.notifyObservers(this, "webapps-launch", JSON.stringify(msg));
         break;
-      case "Webapps:Enumerate":
-        this.enumerate(msg);
+      case "Webapps:GetInstalled":
+        this.getInstalled(msg);
         break;
-      case "Webapps:EnumerateAll":
-        this.enumerateAll(msg);
+      case "Webapps:GetAll":
+        if (msg.hasPrivileges)
+          this.getAll(msg);
+        else
+          ppmm.sendAsyncMessage("Webapps:GetAll:Return:KO", msg);
         break;
     }
   },
 
   _writeFile: function ss_writeFile(aFile, aData, aCallbak) {
     // Initialize the file output stream.
     let ostream = FileUtils.openSafeFileOutputStream(aFile);
 
@@ -157,24 +128,25 @@ let DOMApplicationRegistry = {
     });
   },
 
   // clones a app object, without the manifest
   _cloneAppObject: function(aApp) {
     let clone = {
       installOrigin: aApp.installOrigin,
       origin: aApp.origin,
-      receipt: aApp.receipt,
-      installTime: aApp.installTime
+      receipts: aApp.receipts,
+      installTime: aApp.installTime,
+      manifestURL: aApp.manifestURL
     };
     return clone;
   },
 
   denyInstall: function(aData) {
-    this.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
+    ppmm.sendAsyncMessage("Webapps:Install:Return:KO", aData);
   },
 
   confirmInstall: function(aData, aFromSync) {
     let app = aData.app;
     let id = app.syncId || this._appId(app.origin);
 
     // install an application again is considered as an update
     if (id) {
@@ -197,17 +169,17 @@ let DOMApplicationRegistry = {
 
     this.webapps[id] = this._cloneAppObject(app);
     delete this.webapps[id].manifest;
     this.webapps[id].installTime = (new Date()).getTime()
 
     
     if (!aFromSync)
       this._saveApps((function() {
-        this.mm.sendAsyncMessage("Webapps:Install:Return:OK", aData);
+        ppmm.sendAsyncMessage("Webapps:Install:Return:OK", aData);
         Services.obs.notifyObservers(this, "webapps-sync-install", id);
       }).bind(this));
   },
 
   _appId: function(aURI) {
     for (let id in this.webapps) {
       if (this.webapps[id].origin == aURI)
         return id;
@@ -239,95 +211,88 @@ let DOMApplicationRegistry = {
       if (index == aData.length - 1)
         aFinalCallback(aData);
       else
         this._readManifests(aData, aFinalCallback, index + 1);
     }).bind(this)); 
   },
 
   uninstall: function(aData) {
+    let found = false;
     for (let id in this.webapps) {
       let app = this.webapps[id];
       if (app.origin == aData.origin) {
+        found = true;
         delete this.webapps[id];
         let dir = FileUtils.getDir("ProfD", ["webapps", id], true, true);
         try {
           dir.remove(true);
         } catch (e) {
         }
         this._saveApps((function() {
-          this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
+          ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", aData);
           Services.obs.notifyObservers(this, "webapps-sync-uninstall", id);
         }).bind(this));
       }
     }
+    if (!found)
+      ppmm.sendAsyncMessage("Webapps:Uninstall:Return:KO", aData);
   },
 
-  enumerate: function(aData) {
+  getSelf: function(aData) {
     aData.apps = [];
     let tmp = [];
-    let selfId;
+    let id = this._appId(aData.origin);
 
-    let id = this._appId(aData.origin);
-    // if it's an app, add itself to the result
     if (id) {
       let app = this._cloneAppObject(this.webapps[id]);
       aData.apps.push(app);
       tmp.push({ id: id });
-      selfId = id;
     }
 
-    // check if it's a store.
-    let isStore = false;
-    for (id in this.webapps) {
-      let app = this.webapps[id];
-      if (app.installOrigin == aData.origin) {
-        isStore = true;
-        break;
-      }
-    }
+    this._readManifests(tmp, (function(aResult) {
+      for (let i = 0; i < aResult.length; i++)
+        aData.apps[i].manifest = aResult[i].manifest;
+      ppmm.sendAsyncMessage("Webapps:GetSelf:Return:OK", aData);
+    }).bind(this));
+  },
 
-    // add all the apps from this store
-    if (isStore) {
-      for (id in this.webapps) {
-        if (id == selfId)
-          continue;
-        let app = this._cloneAppObject(this.webapps[id]);
-        if (app.installOrigin == aData.origin) {
-          aData.apps.push(app);
-          tmp.push({ id: id });
-        }
+  getInstalled: function(aData) {
+    aData.apps = [];
+    let tmp = [];
+    let id = this._appId(aData.origin);
+
+    for (id in this.webapps) {
+      if (this.webapps[id].installOrigin == aData.origin) {
+        aData.apps.push(this._cloneAppObject(this.webapps[id]));
+        tmp.push({ id: id });
       }
     }
 
     this._readManifests(tmp, (function(aResult) {
       for (let i = 0; i < aResult.length; i++)
         aData.apps[i].manifest = aResult[i].manifest;
-      this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
+      ppmm.sendAsyncMessage("Webapps:GetInstalled:Return:OK", aData);
     }).bind(this));
   },
 
-  denyEnumerate: function(aData) {
-    this.mm.sendAsyncMessage("Webapps:Enumerate:Return:KO", aData);
-  },
-
-  enumerateAll: function(aData) {
+  getAll: function(aData) {
     aData.apps = [];
     let tmp = [];
 
     for (id in this.webapps) {
       let app = this._cloneAppObject(this.webapps[id]);
       aData.apps.push(app);
       tmp.push({ id: id });
     }
 
     this._readManifests(tmp, (function(aResult) {
       for (let i = 0; i < aResult.length; i++)
         aData.apps[i].manifest = aResult[i].manifest;
-      this.mm.sendAsyncMessage("Webapps:Enumerate:Return:OK", aData);
+      ppmm.sendAsyncMessage("Webapps:GetAll:Return:OK", aData);
     }).bind(this));
   },
 
   getManifestFor: function(aOrigin, aCallback) {
     if (!aCallback)
       return;
 
     let id = this._appId(aOrigin);
@@ -363,26 +328,26 @@ let DOMApplicationRegistry = {
           continue;
         let origin = this.webapps[record.id].origin;
         delete this.webapps[record.id];
         let dir = FileUtils.getDir("ProfD", ["webapps", record.id], true, true);
         try {
           dir.remove(true);
         } catch (e) {
         }
-        this.mm.sendAsyncMessage("Webapps:Uninstall:Return:OK", { origin: origin });
+        ppmm.sendAsyncMessage("Webapps:Uninstall:Return:OK", { origin: origin });
       } else {
         if (!!this.webapps[record.id]) {
           this.webapps[record.id] = record.value;
           delete this.webapps[record.id].manifest;
         }
         else {
           let data = { app: record.value };
           this.confirmInstall(data, true);
-          this.mm.sendAsyncMessage("Webapps:Install:Return:OK", data);
+          ppmm.sendAsyncMessage("Webapps:Install:Return:OK", data);
         }
       }
     }
     this._saveApps(aCallback);
   },
 
   /*
    * May be removed once sync API change
--- a/dom/base/Webapps.manifest
+++ b/dom/base/Webapps.manifest
@@ -1,10 +1,4 @@
 # Webapps.js
 component {fff440b3-fae2-45c1-bf03-3b5a2e432270} Webapps.js
 contract @mozilla.org/webapps;1 {fff440b3-fae2-45c1-bf03-3b5a2e432270}
 category JavaScript-navigator-property mozApps @mozilla.org/webapps;1
-
-component {723ed303-7757-4fb0-b261-4f78b1f6bd22} Webapps.js
-contract @mozilla.org/webapps/application;1 {723ed303-7757-4fb0-b261-4f78b1f6bd22}
-
-component {b4937718-11a3-400b-a69f-ab442a418569} Webapps.js
-contract @mozilla.org/webapps/error;1 {b4937718-11a3-400b-a69f-ab442a418569}
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -502,16 +502,17 @@ using mozilla::dom::indexedDB::IDBWrappe
 #include "nsIDOMCustomEvent.h"
 
 #include "nsWrapperCacheInlines.h"
 #include "dombindings.h"
 
 #include "nsIDOMBatteryManager.h"
 #include "BatteryManager.h"
 #include "nsIDOMPowerManager.h"
+#include "nsIDOMWakeLock.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"
@@ -543,16 +544,17 @@ static const char kDOMStringBundleURL[] 
 
 #define WINDOW_SCRIPTABLE_FLAGS                                               \
  (nsIXPCScriptable::WANT_GETPROPERTY |                                        \
   nsIXPCScriptable::WANT_PRECREATE |                                          \
   nsIXPCScriptable::WANT_FINALIZE |                                           \
   nsIXPCScriptable::WANT_ENUMERATE |                                          \
   nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE |                               \
   nsIXPCScriptable::USE_STUB_EQUALITY_HOOK |                                  \
+  nsIXPCScriptable::IS_GLOBAL_OBJECT |                                        \
   nsIXPCScriptable::WANT_OUTER_OBJECT)
 
 #define NODE_SCRIPTABLE_FLAGS                                                 \
  ((DOM_DEFAULT_SCRIPTABLE_FLAGS |                                             \
    nsIXPCScriptable::USE_STUB_EQUALITY_HOOK |                                 \
    nsIXPCScriptable::WANT_ADDPROPERTY) &                                      \
   ~nsIXPCScriptable::USE_JSSTUB_FOR_ADDPROPERTY)
 
@@ -1430,16 +1432,19 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozBatteryManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozPowerManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
+  NS_DEFINE_CLASSINFO_DATA(MozWakeLock, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
   NS_DEFINE_CLASSINFO_DATA(MozSmsManager, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozSmsMessage, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(MozSmsEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -1544,17 +1549,17 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(EventListenerInfo, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(TransitionEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(AnimationEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(ContentFrameMessageManager, nsEventTargetSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS | nsIXPCScriptable::IS_GLOBAL_OBJECT)
 
   NS_DEFINE_CLASSINFO_DATA(FormData, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DesktopNotification, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(DesktopNotificationCenter, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
@@ -1612,18 +1617,18 @@ static nsDOMClassInfoData sClassInfoData
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(TelephonyCall, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CallEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
 #ifdef MOZ_B2G_BT
-  NS_DEFINE_CLASSINFO_DATA(BluetoothAdapter, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(BluetoothAdapter, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DOMRequest, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 };
@@ -4025,16 +4030,20 @@ nsDOMClassInfo::Init()
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozBatteryManager)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozPowerManager, nsIDOMMozPowerManager)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozPowerManager)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(MozWakeLock, nsIDOMMozWakeLock)
+     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozWakeLock)
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(MozSmsManager, nsIDOMMozSmsManager)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsManager)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MozSmsMessage, nsIDOMMozSmsMessage)
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMMozSmsMessage)
   DOM_CLASSINFO_MAP_END
 
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -424,16 +424,17 @@ DOMCI_CLASS(GeoGeolocation)
 DOMCI_CLASS(GeoPosition)
 DOMCI_CLASS(GeoPositionCoords)
 DOMCI_CLASS(GeoPositionAddress)
 DOMCI_CLASS(GeoPositionError)
 
 DOMCI_CLASS(MozBatteryManager)
 
 DOMCI_CLASS(MozPowerManager)
+DOMCI_CLASS(MozWakeLock)
 
 DOMCI_CLASS(MozSmsManager)
 DOMCI_CLASS(MozSmsMessage)
 DOMCI_CLASS(MozSmsEvent)
 DOMCI_CLASS(MozSmsRequest)
 DOMCI_CLASS(MozSmsFilter)
 DOMCI_CLASS(MozSmsCursor)
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -139,17 +139,21 @@ static PRLogModuleInfo* gJSDiagnostics;
 #define NS_INTERSLICE_GC_DELAY      100 // ms
 
 // The amount of time we wait between a request to CC (after GC ran)
 // and doing the actual CC.
 #define NS_CC_DELAY                 6000 // ms
 
 #define NS_CC_SKIPPABLE_DELAY       400 // ms
 
-#define NS_CC_FORCED                (5 * 60 * PR_USEC_PER_SEC) // 5 min
+// Force a CC after this long if there's anything in the purple buffer.
+#define NS_CC_FORCED                (2 * 60 * PR_USEC_PER_SEC) // 2 min
+
+// Trigger a CC if the purple buffer exceeds this size when we check it.
+#define NS_CC_PURPLE_LIMIT          250
 
 #define JAVASCRIPT nsIProgrammingLanguage::JAVASCRIPT
 
 // if you add statics here, add them to the list in nsJSRuntime::Startup
 
 static nsITimer *sGCTimer;
 static nsITimer *sShrinkGCBuffersTimer;
 static nsITimer *sCCTimer;
@@ -2222,21 +2226,19 @@ nsJSContext::CreateNativeGlobalForInner(
   nsCOMPtr<nsIPrincipal> systemPrincipal;
   if (aIsChrome) {
     nsIScriptSecurityManager *ssm = nsContentUtils::GetSecurityManager();
     ssm->GetSystemPrincipal(getter_AddRefs(systemPrincipal));
   }
 
   nsRefPtr<nsIXPConnectJSObjectHolder> jsholder;
   nsresult rv = xpc->
-          InitClassesWithNewWrappedGlobal(mContext,
-                                          aNewInner, NS_GET_IID(nsISupports),
+          InitClassesWithNewWrappedGlobal(mContext, aNewInner,
                                           aIsChrome ? systemPrincipal.get() : aPrincipal,
-                                          nsnull, flags,
-                                          getter_AddRefs(jsholder));
+                                          flags, getter_AddRefs(jsholder));
   if (NS_FAILED(rv)) {
     return rv;
   }
   jsholder->GetJSObject(aNativeGlobal);
   jsholder.forget(aHolder);
   return NS_OK;
 }
 
@@ -2319,17 +2321,17 @@ nsJSContext::SetOuterObject(JSObject* aO
 
   nsIXPConnect *xpc = nsContentUtils::XPConnect();
   nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
   nsresult rv = xpc->GetWrappedNativeOfJSObject(mContext, inner,
                                                 getter_AddRefs(wrapper));
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
 
-  wrapper->RefreshPrototype();
+  wrapper->FinishInitForWrappedGlobal();
   JS_SetPrototype(mContext, aOuterObject, JS_GetPrototype(inner));
 
   return NS_OK;
 }
 
 nsresult
 nsJSContext::InitOuterWindow()
 {
@@ -3281,56 +3283,80 @@ GCTimerFired(nsITimer *aTimer, void *aCl
 void
 ShrinkGCBuffersTimerFired(nsITimer *aTimer, void *aClosure)
 {
   NS_RELEASE(sShrinkGCBuffersTimer);
 
   nsJSContext::ShrinkGCBuffersNow();
 }
 
-// static
-void
+static bool
+ShouldTriggerCC(PRUint32 aSuspected)
+{
+  return sNeedsFullCC ||
+         aSuspected > NS_CC_PURPLE_LIMIT ||
+         sLastCCEndTime + NS_CC_FORCED < PR_Now();
+}
+
+static void
+TimerFireForgetSkippable(PRUint32 aSuspected, bool aRemoveChildless)
+{
+  PRTime startTime = PR_Now();
+  nsCycleCollector_forgetSkippable(aRemoveChildless);
+  sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
+  sCleanupSinceLastGC = true;
+  PRTime delta = PR_Now() - startTime;
+  if (sMinForgetSkippableTime > delta) {
+    sMinForgetSkippableTime = delta;
+  }
+  if (sMaxForgetSkippableTime < delta) {
+    sMaxForgetSkippableTime = delta;
+  }
+  sTotalForgetSkippableTime += delta;
+  sRemovedPurples += (aSuspected - sPreviousSuspectedCount);
+  ++sForgetSkippableBeforeCC;
+}
+
+static void
 CCTimerFired(nsITimer *aTimer, void *aClosure)
 {
-  if (sDidShutdown) {
-    return;
-  }
-  if (sCCLockedOut) {
+  if (sDidShutdown || sCCLockedOut) {
     return;
   }
   ++sCCTimerFireCount;
-  if (sCCTimerFireCount < (NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY)) {
-    PRUint32 suspected = nsCycleCollector_suspectedCount();
-    if ((sPreviousSuspectedCount + 100) > suspected) {
-      // Just few new suspected objects, return early.
-      return;
+
+  // During early timer fires, we only run forgetSkippable. During the first
+  // late timer fire, we decide if we are going to have a second and final
+  // late timer fire, where we may run the CC.
+  const PRUint32 numEarlyTimerFires = NS_CC_DELAY / NS_CC_SKIPPABLE_DELAY - 2;
+  bool isLateTimerFire = sCCTimerFireCount > numEarlyTimerFires;
+  PRUint32 suspected = nsCycleCollector_suspectedCount();
+  if (isLateTimerFire && ShouldTriggerCC(suspected)) {
+    if (sCCTimerFireCount == numEarlyTimerFires + 1) {
+      TimerFireForgetSkippable(suspected, true);
+      if (ShouldTriggerCC(nsCycleCollector_suspectedCount())) {
+        // Our efforts to avoid a CC have failed, so we return to let the
+        // timer fire once more to trigger a CC.
+        return;
+      }
+    } else {
+      // We are in the final timer fire and still meet the conditions for
+      // triggering a CC.
+      nsJSContext::CycleCollectNow();
     }
-    
-    PRTime startTime = PR_Now();
-    nsCycleCollector_forgetSkippable();
-    sPreviousSuspectedCount = nsCycleCollector_suspectedCount();
-    sCleanupSinceLastGC = true;
-    PRTime delta = PR_Now() - startTime;
-    if (sMinForgetSkippableTime > delta) {
-      sMinForgetSkippableTime = delta;
-    }
-    if (sMaxForgetSkippableTime < delta) {
-      sMaxForgetSkippableTime = delta;
-    }
-    sTotalForgetSkippableTime += delta;
-    sRemovedPurples += (suspected - sPreviousSuspectedCount);
-    ++sForgetSkippableBeforeCC;
-  } else {
+  } else if ((sPreviousSuspectedCount + 100) <= suspected) {
+    // Only do a forget skippable if there are more than a few new objects.
+    TimerFireForgetSkippable(suspected, false);
+  }
+
+  if (isLateTimerFire) {
+    // We have either just run the CC or decided we don't want to run the CC
+    // next time, so kill the timer.
     sPreviousSuspectedCount = 0;
     nsJSContext::KillCCTimer();
-    if (sNeedsFullCC ||
-        nsCycleCollector_suspectedCount() > 500 ||
-        sLastCCEndTime + NS_CC_FORCED < PR_Now()) {
-      nsJSContext::CycleCollectNow();
-    }
   }
 }
 
 // static
 bool
 nsJSContext::CleanupSinceLastGC()
 {
   return sCleanupSinceLastGC;
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -11,151 +11,160 @@
 #include "mozilla/LazyIdleThread.h"
 
 #include "BluetoothAdapter.h"
 
 #if defined(MOZ_WIDGET_GONK)
 #include <bluedroid/bluetooth.h>
 #endif
 
-#define POWERED_EVENT_NAME NS_LITERAL_STRING("powered")
-
-BEGIN_BLUETOOTH_NAMESPACE
+USING_BLUETOOTH_NAMESPACE
 
 class ToggleBtResultTask : public nsRunnable
 {
   public:
-    ToggleBtResultTask(bool result, nsRefPtr<BluetoothAdapter>& adapterPtr)
+    ToggleBtResultTask(nsRefPtr<BluetoothAdapter>& adapterPtr, bool result)
       : mResult(result)
     {
-      MOZ_ASSERT(!NS_IsMainThread()); // This should be running on the worker thread
+      MOZ_ASSERT(!NS_IsMainThread());
 
       mAdapterPtr.swap(adapterPtr);
     }
 
-    NS_IMETHOD Run() {
-      MOZ_ASSERT(NS_IsMainThread()); // This method is supposed to run on the main thread!
+    NS_IMETHOD Run() 
+    {
+      MOZ_ASSERT(NS_IsMainThread());
+
+      if (!mResult) {
+        //TODO:Bug-731361
+        NS_WARNING("BT firmware loading fails.\n");
+      }
+ 
+      //mAdapterPtr must be null before returning to prevent the background 
+      //thread from racing to release it during the destruction of this runnable.
       mAdapterPtr->FirePowered();
+      mAdapterPtr = nsnull;
 
       return NS_OK;
     }
 
   private:
+    nsRefPtr<BluetoothAdapter> mAdapterPtr;
     bool mResult;
-    nsRefPtr<BluetoothAdapter> mAdapterPtr;
 };
 
 class ToggleBtTask : public nsRunnable
 {
   public:
-    ToggleBtTask(bool onOff, BluetoothAdapter* adapterPtr)
+    ToggleBtTask(bool onOff, BluetoothAdapter* adapterPtr) 
       : mOnOff(onOff),
-      mAdapterPtr(adapterPtr)
+        mAdapterPtr(adapterPtr) 
     {
-      MOZ_ASSERT(NS_IsMainThread()); // The constructor should be running on the main thread.
+      MOZ_ASSERT(NS_IsMainThread());
     }
 
-    NS_IMETHOD Run() {
+    NS_IMETHOD Run() 
+    {
+      MOZ_ASSERT(!NS_IsMainThread());
+
       bool result;
 
-      MOZ_ASSERT(!NS_IsMainThread()); // This should be running on the worker thread.
-
       //Toggle BT here
 #if defined(MOZ_WIDGET_GONK)  
       if (mOnOff) {
         result = bt_enable();
       } else {
         result = bt_disable();
       }
-#else
+#else 
       result = true;
 #endif
 
       // Create a result thread and pass it to Main Thread, 
-      nsCOMPtr<nsIRunnable> resultRunnable = new ToggleBtResultTask(result, mAdapterPtr);
-      NS_DispatchToMainThread(resultRunnable);
+      nsCOMPtr<nsIRunnable> resultRunnable = new ToggleBtResultTask(mAdapterPtr, result);
+
+      if (NS_FAILED(NS_DispatchToMainThread(resultRunnable))) {
+        NS_WARNING("Failed to dispatch to main thread!");
+      }
 
       return NS_OK;
     }
 
   private:
     nsRefPtr<BluetoothAdapter> mAdapterPtr;
     bool mOnOff;
 };
 
-END_BLUETOOTH_NAMESPACE
-
-DOMCI_DATA(BluetoothAdapter, mozilla::dom::bluetooth::BluetoothAdapter)
-
-USING_BLUETOOTH_NAMESPACE
+DOMCI_DATA(BluetoothAdapter, BluetoothAdapter)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothAdapter)
 
-NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothAdapter,
-    nsDOMEventTargetHelper)
-NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(powered)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothAdapter, 
+                                                  nsDOMEventTargetHelper)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(powered)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
-NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothAdapter,
-    nsDOMEventTargetHelper)
-NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(powered)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothAdapter, 
+                                                nsDOMEventTargetHelper)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(powered)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
-  NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothAdapter)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothAdapter)
   NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothAdapter)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothAdapter)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
 
 NS_IMPL_ADDREF_INHERITED(BluetoothAdapter, nsDOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BluetoothAdapter, nsDOMEventTargetHelper)
 
-BluetoothAdapter::BluetoothAdapter() : mPower(false)
+BluetoothAdapter::BluetoothAdapter() 
+  : mPower(false)
 {
 }
 
 NS_IMETHODIMP
 BluetoothAdapter::GetPower(bool* aPower)
 {
-#if defined(MOZ_WIDGET_GONK)  
-  *aPower = bt_is_enabled();
-#else
   *aPower = mPower;
-#endif
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 BluetoothAdapter::SetPower(bool aPower)
 {
   if (mPower != aPower) {
     mPower = aPower;
 
-    ToggleBluetoothAsync();
+    return ToggleBluetoothAsync();
   }
 
   return NS_OK;
 }
 
-void 
+nsresult
 BluetoothAdapter::ToggleBluetoothAsync()
 {
   if (!mToggleBtThread) {
     mToggleBtThread = new LazyIdleThread(15000);
   }
 
   nsCOMPtr<nsIRunnable> r = new ToggleBtTask(mPower, this);
 
-  mToggleBtThread->Dispatch(r, 0);
+  return mToggleBtThread->Dispatch(r, NS_DISPATCH_NORMAL);
 }
 
 nsresult
 BluetoothAdapter::FirePowered()
 {
   nsRefPtr<nsDOMEvent> event = new nsDOMEvent(nsnull, nsnull);
-  nsresult rv = event->InitEvent(POWERED_EVENT_NAME, false, false);
+  nsresult rv = event->InitEvent(NS_LITERAL_STRING("powered"), false, false);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rv = event->SetTrusted(true);
   NS_ENSURE_SUCCESS(rv, rv);
 
   bool dummy;
   rv = DispatchEvent(event, &dummy);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
--- a/dom/bluetooth/BluetoothAdapter.h
+++ b/dom/bluetooth/BluetoothAdapter.h
@@ -6,20 +6,22 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothadapter_h__
 #define mozilla_dom_bluetooth_bluetoothadapter_h__
 
 #include "BluetoothCommon.h"
 #include "nsDOMEventTargetHelper.h"
 #include "nsIDOMBluetoothAdapter.h"
 
+class nsIEventTarget;
+
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothAdapter : public nsIDOMBluetoothAdapter
-                        ,public nsDOMEventTargetHelper
+                       , public nsDOMEventTargetHelper
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMBLUETOOTHADAPTER
 
   NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothAdapter,
@@ -31,13 +33,13 @@ public:
 
 protected:
   bool mPower;
 
   NS_DECL_EVENT_HANDLER(powered)
 
 private:
   nsCOMPtr<nsIEventTarget> mToggleBtThread;
-  void ToggleBluetoothAsync();
+  nsresult ToggleBluetoothAsync();
 };
 
 END_BLUETOOTH_NAMESPACE
 #endif
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -12,16 +12,25 @@ else
   debug = function (s) {}
 
 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/DOMRequestHelper.jsm");
+
+XPCOMUtils.defineLazyGetter(Services, "rs", function() {
+  return Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
+});
+
+XPCOMUtils.defineLazyGetter(this, "cpmm", function() {
+  return Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
 
 const nsIClassInfo            = Ci.nsIClassInfo;
 const CONTACTPROPERTIES_CID   = Components.ID("{53ed7c20-ceda-11e0-9572-0800200c9a66}");
 const nsIDOMContactProperties = Ci.nsIDOMContactProperties;
 
 // ContactProperties is not directly instantiated. It is used as interface.
 
 ContactProperties.prototype = {
@@ -179,16 +188,17 @@ const CONTACTMANAGER_CID        = Compon
 const nsIDOMContactManager      = Components.interfaces.nsIDOMContactManager;
 
 function ContactManager()
 {
   debug("Constructor");
 }
 
 ContactManager.prototype = {
+  __proto__: DOMRequestIpcHelper.prototype,
 
   save: function save(aContact) {
     let request;
     if (this.hasPrivileges) {
       debug("save: " + JSON.stringify(aContact) + " :" + aContact.id);
       let newContact = {};
       newContact.properties = {
         name:            [],
@@ -217,31 +227,31 @@ ContactManager.prototype = {
 
       if (aContact.id == "undefined") {
         debug("Create id!");
         aContact.id = this._getRandomId();
       }
 
       this._setMetaData(newContact, aContact);
       debug("send: " + JSON.stringify(newContact));
-      request = this._rs.createRequest(this._window);
-      this._mm.sendAsyncMessage("Contact:Save", {contact: newContact,
-                                                 requestID: this.getRequestId({ request: request })});
+      request = this.createRequest();
+      cpmm.sendAsyncMessage("Contact:Save", {contact: newContact,
+                                             requestID: this.getRequestId(request)});
       return request;
     } else {
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     }
   },
 
   remove: function removeContact(aRecord) {
     let request;
     if (this.hasPrivileges) {
-      request = this._rs.createRequest(this._window);
-      this._mm.sendAsyncMessage("Contact:Remove", {id: aRecord.id,
-                                                   requestID: this.getRequestId({ request: request })});
+      request = this.createRequest();
+      cpmm.sendAsyncMessage("Contact:Remove", {id: aRecord.id,
+                                               requestID: this.getRequestId(request)});
       return request;
     } else {
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     }
   },
 
   _setMetaData: function(aNewContact, aRecord) {
     aNewContact.id = aRecord.id;
@@ -255,147 +265,102 @@ ContactManager.prototype = {
       let newContact = new Contact();
       newContact.init(aContacts[i].properties);
       this._setMetaData(newContact, aContacts[i]);
       contacts.push(newContact);
     }
     return contacts;
   },
 
-  getRequestId: function(aRequest) {
-    let id = "id" + this._getRandomId();
-    this._requests[id] = aRequest;
-    return id;
-  },
-
-  getRequest: function(aId) {
-    if (this._requests[aId])
-      return this._requests[aId].request;
-  },
-
-  removeRequest: function(aId) {
-    if (this._requests[aId])
-      delete this._requests[aId];
-  },
-  
-  _getRandomId: function() {
-    return Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString();
-  },
-
   receiveMessage: function(aMessage) {
     debug("Contactmanager::receiveMessage: " + aMessage.name);
     let msg = aMessage.json;
     let contacts = msg.contacts;
 
     switch (aMessage.name) {
       case "Contacts:Find:Return:OK":
         let req = this.getRequest(msg.requestID);
         if (req) {
           let result = this._convertContactsArray(contacts);
           debug("result: " + JSON.stringify(result));
-          this._rs.fireSuccess(req, result);
+          Services.rs.fireSuccess(req, result);
         } else {
           debug("no request stored!" + msg.requestID);
         }
         break;
       case "Contact:Save:Return:OK":
       case "Contacts:Clear:Return:OK":
       case "Contact:Remove:Return:OK":
         req = this.getRequest(msg.requestID);
         if (req)
-          this._rs.fireSuccess(req, 0);
+          Services.rs.fireSuccess(req, null);
         break;
       case "Contacts:Find:Return:KO":
       case "Contact:Save:Return:KO":
       case "Contact:Remove:Return:KO":
       case "Contacts:Clear:Return:KO":
         req = this.getRequest(msg.requestID);
         if (req)
-          this._rs.fireError(req, msg.errorMsg);
+          Services.rs.fireError(req, msg.errorMsg);
         break;
       default: 
         debug("Wrong message: " + aMessage.name);
     }
     this.removeRequest(msg.requestID);
   },
 
   find: function(aOptions) {
     let request;
     if (this.hasPrivileges) {
-      request = this._rs.createRequest(this._window);
-      this._mm.sendAsyncMessage("Contacts:Find", {findOptions: aOptions, 
-                                                  requestID: this.getRequestId({ request: request })});
+      request = this.createRequest();
+      cpmm.sendAsyncMessage("Contacts:Find", {findOptions: aOptions, 
+                                              requestID: this.getRequestId(request)});
       return request;
     } else {
       debug("find not allowed");
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     }
   },
 
   clear: function() {
     let request;
     if (this.hasPrivileges) {
-      request = this._rs.createRequest(this._window);
-      this._mm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId({ request: request })});
+      request = this.createRequest();
+      cpmm.sendAsyncMessage("Contacts:Clear", {requestID: this.getRequestId(request)});
       return request;
     } else {
       debug("clear not allowed");
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     }
   },
 
   init: function(aWindow) {
     // Set navigator.mozContacts to null.
     if (!Services.prefs.getBoolPref("dom.mozContacts.enabled"))
       return null;
 
-    this._window = aWindow;
-    this._messages = ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
+    this.initHelper(aWindow, ["Contacts:Find:Return:OK", "Contacts:Find:Return:KO",
                      "Contacts:Clear:Return:OK", "Contacts:Clear:Return:KO",
                      "Contact:Save:Return:OK", "Contact:Save:Return:KO",
-                     "Contact:Remove:Return:OK", "Contact:Remove:Return:KO"];
+                     "Contact:Remove:Return:OK", "Contact:Remove:Return:KO"]);
 
-    this._mm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
-    this._messages.forEach((function(msgName) {
-      this._mm.addMessageListener(msgName, this);
-    }).bind(this));
-
-    this._rs = Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci.nsIDOMRequestService);
-    this._requests = [];
     Services.obs.addObserver(this, "inner-window-destroyed", false);
-    let util = this._window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
-    this._innerWindowID = util.currentInnerWindowID;
 
     let principal = aWindow.document.nodePrincipal;
     let secMan = Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci.nsIScriptSecurityManager);
 
     let perm = principal == secMan.getSystemPrincipal() ? 
                  Ci.nsIPermissionManager.ALLOW_ACTION : 
                  Services.perms.testExactPermission(principal.URI, "webcontacts-manage");
  
     //only pages with perm set can use the contacts
     this.hasPrivileges = perm == Ci.nsIPermissionManager.ALLOW_ACTION;
     debug("has privileges :" + this.hasPrivileges);
   },
 
-  observe: function(aSubject, aTopic, aData) {
-    let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
-    if (wId == this.innerWindowID) {
-      Services.obs.removeObserver(this, "inner-window-destroyed");
-      this._messages.forEach((function(msgName) {
-        this._mm.removeMessageListener(msgName, this);
-      }).bind(this));
-      this._mm = null;
-      this._messages = null;
-      this._requests = null;
-      this._window = null;
-      this._innerWindowID = null;
-    }
-  },
-
   classID : CONTACTMANAGER_CID,
   QueryInterface : XPCOMUtils.generateQI([nsIDOMContactManager, Ci.nsIDOMGlobalPropertyInitializer]),
 
   classInfo : XPCOMUtils.generateCI({classID: CONTACTMANAGER_CID,
                                      contractID: CONTACTMANAGER_CONTRACTID,
                                      classDescription: "ContactManager",
                                      interfaces: [nsIDOMContactManager],
                                      flags: nsIClassInfo.DOM_OBJECT})
--- a/dom/contacts/fallback/ContactService.jsm
+++ b/dom/contacts/fallback/ContactService.jsm
@@ -15,26 +15,28 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 let EXPORTED_SYMBOLS = ["DOMContactManager"];
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/ContactDB.jsm");
 
+XPCOMUtils.defineLazyGetter(this, "ppmm", function() {
+  return Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
+});
+
 let myGlobal = this;
 
 let DOMContactManager = {
-
   init: function() {
     debug("Init");
-    this._mm = Cc["@mozilla.org/parentprocessmessagemanager;1"].getService(Ci.nsIFrameMessageManager);
     this._messages = ["Contacts:Find", "Contacts:Clear", "Contact:Save", "Contact:Remove"];
     this._messages.forEach((function(msgName) {
-      this._mm.addMessageListener(msgName, this);
+      ppmm.addMessageListener(msgName, this);
     }).bind(this));
 
     var idbManager = Components.classes["@mozilla.org/dom/indexeddb/manager;1"].getService(Ci.nsIIndexedDatabaseManager);
     idbManager.initWindowless(myGlobal);
     this._db = new ContactDB(myGlobal);
 
     Services.obs.addObserver(this, "profile-before-change", false);
 
@@ -47,50 +49,50 @@ let DOMContactManager = {
                              Ci.nsIPermissionManager.ALLOW_ACTION);
       });
     } catch(e) { debug(e); }
   },
 
   observe: function(aSubject, aTopic, aData) {
     myGlobal = null;
     this._messages.forEach((function(msgName) {
-      this._mm.removeMessageListener(msgName, this);
+      ppmm.removeMessageListener(msgName, this);
     }).bind(this));
     Services.obs.removeObserver(this, "profile-before-change");
-    this._mm = null;
+    ppmm = null;
     this._messages = null;
     if (this._db)
       this._db.close();
   },
 
   receiveMessage: function(aMessage) {
     debug("Fallback DOMContactManager::receiveMessage " + aMessage.name);
     let msg = aMessage.json;
     switch (aMessage.name) {
       case "Contacts:Find":
         let result = new Array();
         this._db.find(
           function(contacts) {
             for (let i in contacts)
               result.push(contacts[i]);
             debug("result:" + JSON.stringify(result));
-            this._mm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result});
+            ppmm.sendAsyncMessage("Contacts:Find:Return:OK", {requestID: msg.requestID, contacts: result});
           }.bind(this),
-          function(aErrorMsg) { this._mm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this), 
+          function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Find:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }) }.bind(this), 
           msg.findOptions);
         break;
       case "Contact:Save":
-        this._db.saveContact(msg.contact, function() {this._mm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID }); }.bind(this), 
-                             function(aErrorMsg) { this._mm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
+        this._db.saveContact(msg.contact, function() { ppmm.sendAsyncMessage("Contact:Save:Return:OK", { requestID: msg.requestID }); }.bind(this), 
+                             function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Save:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
         break;
       case "Contact:Remove":
         this._db.removeContact(msg.id, 
-                               function() {this._mm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID }); }.bind(this), 
-                               function(aErrorMsg) {this._mm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
+                               function() { ppmm.sendAsyncMessage("Contact:Remove:Return:OK", { requestID: msg.requestID }); }.bind(this), 
+                               function(aErrorMsg) { ppmm.sendAsyncMessage("Contact:Remove:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
         break;
       case "Contacts:Clear":
-        this._db.clear(function() { this._mm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this),
-                       function(aErrorMsg) { this._mm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
+        this._db.clear(function() { ppmm.sendAsyncMessage("Contacts:Clear:Return:OK", { requestID: msg.requestID }); }.bind(this),
+                       function(aErrorMsg) { ppmm.sendAsyncMessage("Contacts:Clear:Return:KO", { requestID: msg.requestID, errorMsg: aErrorMsg }); }.bind(this));
     }
   }
 }
 
 DOMContactManager.init();
--- a/dom/indexedDB/AsyncConnectionHelper.cpp
+++ b/dom/indexedDB/AsyncConnectionHelper.cpp
@@ -85,17 +85,17 @@ ConvertCloneReadInfosToArrayInternal(
 {
   JSObject* array = JS_NewArrayObject(aCx, 0, nsnull);
   if (!array) {
     NS_WARNING("Failed to make array!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (!aReadInfos.IsEmpty()) {
-    if (!JS_SetArrayLength(aCx, array, jsuint(aReadInfos.Length()))) {
+    if (!JS_SetArrayLength(aCx, array, uint32_t(aReadInfos.Length()))) {
       NS_WARNING("Failed to set array length!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32_t index = 0, count = aReadInfos.Length(); index < count;
          index++) {
       StructuredCloneReadInfo& readInfo = aReadInfos[index];
 
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -405,28 +405,28 @@ IDBDatabase::CreateObjectStore(const nsA
     // Get keyPath
     jsval val = params.keyPath;
     if (!JSVAL_IS_VOID(val) && !JSVAL_IS_NULL(val)) {
       if (!JSVAL_IS_PRIMITIVE(val) &&
           JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(val))) {
     
         JSObject* obj = JSVAL_TO_OBJECT(val);
     
-        jsuint length;
+        uint32_t length;
         if (!JS_GetArrayLength(aCx, obj, &length)) {
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
     
         if (!length) {
           return NS_ERROR_DOM_SYNTAX_ERR;
         }
     
         keyPathArray.SetCapacity(length);
     
-        for (jsuint index = 0; index < length; index++) {
+        for (uint32_t index = 0; index < length; index++) {
           jsval val;
           JSString* jsstr;
           nsDependentJSString str;
           if (!JS_GetElement(aCx, obj, index, &val) ||
               !(jsstr = JS_ValueToString(aCx, val)) ||
               !str.init(aCx, jsstr)) {
             return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
           }
@@ -561,28 +561,28 @@ IDBDatabase::Transaction(const jsval& aS
   nsresult rv;
   nsTArray<nsString> storesToOpen;
 
   if (!JSVAL_IS_PRIMITIVE(aStoreNames)) {
     JSObject* obj = JSVAL_TO_OBJECT(aStoreNames);
 
     // See if this is a JS array.
     if (JS_IsArrayObject(aCx, obj)) {
-      jsuint length;
+      uint32_t length;
       if (!JS_GetArrayLength(aCx, obj, &length)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
       if (!length) {
         return NS_ERROR_DOM_INVALID_ACCESS_ERR;
       }
 
       storesToOpen.SetCapacity(length);
 
-      for (jsuint index = 0; index < length; index++) {
+      for (uint32_t index = 0; index < length; index++) {
         jsval val;
         JSString* jsstr;
         nsDependentJSString str;
         if (!JS_GetElement(aCx, obj, index, &val) ||
             !(jsstr = JS_ValueToString(aCx, val)) ||
             !str.init(aCx, jsstr)) {
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -899,17 +899,17 @@ GetAllKeysHelper::GetSuccessResult(JSCon
 
   JSObject* array = JS_NewArrayObject(aCx, 0, NULL);
   if (!array) {
     NS_WARNING("Failed to make array!");
     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
   }
 
   if (!keys.IsEmpty()) {
-    if (!JS_SetArrayLength(aCx, array, jsuint(keys.Length()))) {
+    if (!JS_SetArrayLength(aCx, array, uint32_t(keys.Length()))) {
       NS_WARNING("Failed to set array length!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32 index = 0, count = keys.Length(); index < count; index++) {
       const Key& key = keys[index];
       NS_ASSERTION(!key.IsUnset(), "Bad key!");
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -613,22 +613,22 @@ IDBObjectStore::AppendIndexUpdateInfo(PR
 
   jsval key;
   rv = GetJSValFromKeyPath(aCx, aVal, aKeyPath, key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aMultiEntry && !JSVAL_IS_PRIMITIVE(key) &&
       JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(key))) {
     JSObject* array = JSVAL_TO_OBJECT(key);
-    jsuint arrayLength;
+    uint32_t arrayLength;
     if (!JS_GetArrayLength(aCx, array, &arrayLength)) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    for (jsuint arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
+    for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
       jsval arrayItem;
       if (!JS_GetElement(aCx, array, arrayIndex, &arrayItem)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
       Key value;
       if (NS_FAILED(value.SetFromJSVal(aCx, arrayItem)) ||
           value.IsUnset()) {
@@ -1695,28 +1695,28 @@ IDBObjectStore::CreateIndex(const nsAStr
   nsTArray<nsString> keyPathArray;
 
   // See if this is a JS array.
   if (!JSVAL_IS_PRIMITIVE(aKeyPath) &&
       JS_IsArrayObject(aCx, JSVAL_TO_OBJECT(aKeyPath))) {
 
     JSObject* obj = JSVAL_TO_OBJECT(aKeyPath);
 
-    jsuint length;
+    uint32_t length;
     if (!JS_GetArrayLength(aCx, obj, &length)) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     if (!length) {
       return NS_ERROR_DOM_SYNTAX_ERR;
     }
 
     keyPathArray.SetCapacity(length);
 
-    for (jsuint index = 0; index < length; index++) {
+    for (uint32_t index = 0; index < length; index++) {
       jsval val;
       JSString* jsstr;
       nsDependentJSString str;
       if (!JS_GetElement(aCx, obj, index, &val) ||
           !(jsstr = JS_ValueToString(aCx, val)) ||
           !str.init(aCx, jsstr)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -165,22 +165,22 @@ Key::EncodeJSVal(JSContext* aCx, const j
       if (aTypeOffset == eMaxType * MaxArrayCollapse) {
         mBuffer.Append(aTypeOffset);
         aTypeOffset = 0;
       }
       NS_ASSERTION((aTypeOffset % eMaxType) == 0 &&
                    aTypeOffset < (eMaxType * MaxArrayCollapse),
                    "Wrong typeoffset");
 
-      jsuint length;
+      uint32_t length;
       if (!JS_GetArrayLength(aCx, obj, &length)) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
-      for (jsuint index = 0; index < length; index++) {
+      for (uint32_t index = 0; index < length; index++) {
         jsval val;
         if (!JS_GetElement(aCx, obj, index, &val)) {
           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
         }
 
         nsresult rv = EncodeJSVal(aCx, val, aTypeOffset);
         NS_ENSURE_SUCCESS(rv, rv);
 
@@ -215,17 +215,17 @@ Key::DecodeJSVal(const unsigned char*& a
 
     aTypeOffset += eMaxType;
 
     if (aTypeOffset == eMaxType * MaxArrayCollapse) {
       ++aPos;
       aTypeOffset = 0;
     }
 
-    jsuint index = 0;
+    uint32_t index = 0;
     while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) {
       jsval val;
       nsresult rv = DecodeJSVal(aPos, aEnd, aCx, aTypeOffset, &val);
       NS_ENSURE_SUCCESS(rv, rv);
 
       aTypeOffset = 0;
 
       if (!JS_SetElement(aCx, array, index++, &val)) {
--- a/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
+++ b/dom/interfaces/apps/nsIDOMApplicationRegistry.idl
@@ -31,109 +31,84 @@
  * 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 "domstubs.idl"
+#include "nsIArray.idl"
+#include "nsIDOMEvent.idl"
+#include "nsIDOMEventTarget.idl"
 
-[scriptable, uuid(e0c271cb-266b-48c9-a7e4-96590b445c26)]
-interface mozIDOMApplicationRegistryError : nsISupports
-{
-  const unsigned short DENIED = 1;
-  const unsigned short PERMISSION_DENIED = 2;
-  const unsigned short MANIFEST_URL_ERROR = 3;
-  const unsigned short NETWORK_ERROR = 4;
-  const unsigned short MANIFEST_PARSE_ERROR = 5;
-  const unsigned short INVALID_MANIFEST = 6;
+interface nsIDOMDOMRequest;
 
-  readonly attribute short code;
-};
-
-[scriptable, uuid(a6856a3d-dece-43ce-89b9-72dba07f4246)]
-interface mozIDOMApplication : nsISupports
+[scriptable, uuid(b70b84f1-7ac9-4a92-bc32-8b6a7eb7879e)]
+interface mozIDOMApplication  : nsISupports
 {
   readonly attribute jsval manifest;
-  readonly attribute DOMString receipt;
+  readonly attribute DOMString manifestURL;
+  readonly attribute nsIArray receipts; /* an array of strings */
   readonly attribute DOMString origin;
   readonly attribute DOMString installOrigin;
   readonly attribute unsigned long installTime;
+  
+  /* startPoint will be used when several launch_path exists for an app */
+  nsIDOMDOMRequest launch([optional] in DOMString startPoint);
+  nsIDOMDOMRequest uninstall(); 
+};
+
+[scriptable, uuid(870bfbdc-3e13-4042-99dd-18e25720782d)]
+interface mozIDOMApplicationEvent : nsIDOMEvent
+{
+  readonly attribute mozIDOMApplication application;
 };
 
-[scriptable, function, uuid(be170df5-9154-463b-9197-10a6195eba52)]
-interface mozIDOMApplicationRegistryEnumerateCallback : nsISupports
+[scriptable, uuid(a82771f6-ba46-4073-9e6e-f1ad3f42b1f6)]
+interface mozIDOMApplicationMgmt : nsIDOMEventTarget
 {
-  void handleEvent([array, size_is(count)] in mozIDOMApplication apps,
-                    in unsigned long count);
+  /**
+   * the request will return the all the applications installed. Only accessible
+   * to privileged callers.
+   */
+  nsIDOMDOMRequest getAll();
+
+  /**
+   * event listener to get notified of application installs. Only settable by
+   * privileged callers.
+   * the event will be a mozIDOMApplicationEvent
+   */
+  attribute nsIDOMEventListener oninstall;
+
+  /**
+   * event listener to get notified of application uninstalls. Only settable by
+   * privileged callers.
+   * the event will be a mozIDOMApplicationEvent
+   */
+  attribute nsIDOMEventListener onuninstall;
 };
 
-[scriptable, function, uuid(ae0ed33d-35cf-443a-837b-a6cebf16bd49)]
-interface mozIDOMApplicationRegistryErrorCallback : nsISupports
-{
-  void handleEvent(in mozIDOMApplicationRegistryError error);
-};
-
-[scriptable, uuid(ac63c0ba-1f33-4e3e-b9aa-6a3243a9adba)]
+[scriptable, uuid(f6929871-288b-4613-9a37-9a150760ac50)]
 interface mozIDOMApplicationRegistry : nsISupports
 {
   /**
    * Install a web app. onerror can be used to report errors,
    * and oninstall if the caller is privileged.
    *
-   * @param manifestUrl : the URL of the webapps manifest
-   * @param receipt : An opaque string used to track payment status
-   */
-  void install(in DOMString manifestUrl,
-	       [optional] in DOMString receipt);
-
-  /**
-   * This call is only accessible to privileged callers.
-   *
-   * @param origin : the origin of the application to uninstall.
+   * @param manifestUrl : the URL of the webapps manifest.
+   * @param parameters : A structure with optional information. 
+   *                     { receipts: ... } will be used to specify the payment receipts for this installation. 
    */
-  void uninstall(in DOMString origin);
-
-  /**
-   * Enumerate apps : either return itself if caller is an app, or
-   * apps installed from a store if caller is a store
-   *
-   * @param success: the callback that will get the array of applications
-   * @param error: optional error callback
-   */
-  void enumerate(in mozIDOMApplicationRegistryEnumerateCallback success,
-		 [optional] in mozIDOMApplicationRegistryErrorCallback error);
+  nsIDOMDOMRequest install(in DOMString manifestUrl, [optional] in jsval parameters);
 
   /**
-   * Enumerate all apps installed from all stores.
-   * Only usable by privileged callers.
-   *
-   * @param success: the callback that will get the array of applications
-   * @param error: optional error callback
+   * the request will return the application currently installed, or null.
    */
-  void enumerateAll(in mozIDOMApplicationRegistryEnumerateCallback success,
-		 [optional] in mozIDOMApplicationRegistryErrorCallback error);
-
-  /**
-   * launch a webapp. Behavior is application dependant.
-   *
-   * @param origin : the origin of the application to launch
-   */
-  void launch(in DOMString origin);
+  nsIDOMDOMRequest getSelf();
 
   /**
-   * event listener to get notified of application installs. Only settable by
-   * privileged callers
+   * the request will return the applications installed from this origin, or null.
    */
-  attribute nsIDOMEventListener oninstall;
+  nsIDOMDOMRequest getInstalled();
 
-  /**
-   * event listener to get notified of application uninstalls. Only settable by
-   * privileged callers
-   */
-  attribute nsIDOMEventListener onuninstall;
-
-  /**
-   * event listener to get notified of errors. 
-   */
-  attribute nsIDOMEventListener onerror;
+  readonly attribute mozIDOMApplicationMgmt mgmt;
 };
--- a/dom/interfaces/base/domstubs.idl
+++ b/dom/interfaces/base/domstubs.idl
@@ -126,8 +126,9 @@ interface nsIDOMCrypto;
 interface nsIDOMPkcs11;
 
 // Used font face (for inspector)
 interface nsIDOMFontFace;
 interface nsIDOMFontFaceList;
 
 // Power
 interface nsIDOMMozPowerManager;
+interface nsIDOMMozWakeLock;
--- a/dom/interfaces/base/nsIDOMNavigator.idl
+++ b/dom/interfaces/base/nsIDOMNavigator.idl
@@ -34,17 +34,17 @@
  * 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 "domstubs.idl"
 
-[scriptable, uuid(b1f4b1fa-49c2-4375-9ce8-bf97ecf6b428)]
+[scriptable, uuid(e610c037-db58-4cd7-8ed3-0d7f1422b4d3)]
 interface nsIDOMNavigator : nsISupports
 {
   readonly attribute DOMString             appCodeName;
   readonly attribute DOMString             appName;
   readonly attribute DOMString             appVersion;
   readonly attribute DOMString             language;
   readonly attribute nsIDOMMimeTypeArray   mimeTypes;
   readonly attribute DOMString             platform;
@@ -105,9 +105,39 @@ interface nsIDOMNavigator : nsISupports
    *   pass an even number of elements (that is, if your list ends with b_n
    *   instead of a_n), the final element doesn't specify anything meaningful.
    *
    *   We may throw NS_ERROR_DOM_NOT_SUPPORTED_ERR if the vibration pattern is
    *   too long, or if any of its elements is too large.
    */
   [implicit_jscontext]
   void mozVibrate(in jsval aPattern);
+
+  /**
+   * Request a wake lock for a resource.
+   *
+   * A page holds a wake lock to request that a resource not be turned
+   * off (or otherwise made unavailable).
+   *
+   * The topic is the name of a resource that might be made unavailable for
+   * various reasons. For example, on a mobile device the power manager might
+   * decide to turn off the screen after a period of idle time to save power.
+   *
+   * The resource manager checks the lock state of a topic before turning off
+   * the associated resource. For example, a page could hold a lock on the
+   * "screen" topic to prevent the screensaver from appearing or the screen
+   * from turning off.
+   *
+   * The resource manager defines what each topic means and sets policy.  For
+   * example, the resource manager might decide to ignore 'screen' wake locks
+   * held by pages which are not visible.
+   *
+   * One topic can be locked multiple times; it is considered released only when
+   * all locks on the topic have been released.
+   *
+   * The returned nsIDOMMozWakeLock object is a token of the lock.  You can
+   * unlock the lock via the object's |unlock| method.  The lock is released
+   * automatically when its associated window is unloaded.
+   *
+   * @param aTopic resource name
+   */
+  nsIDOMMozWakeLock requestWakeLock(in DOMString aTopic);
 };
--- a/dom/power/Makefile.in
+++ b/dom/power/Makefile.in
@@ -47,25 +47,29 @@ LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 EXPORTS_NAMESPACES = mozilla/dom/power
 
 EXPORTS_mozilla/dom/power = \
   PowerManagerService.h \
+  Types.h \
   $(NULL)
 
 CPPSRCS = \
   PowerManager.cpp \
   PowerManagerService.cpp \
+  WakeLock.cpp \
   $(NULL)
 
 XPIDLSRCS = \
   nsIDOMPowerManager.idl \
+  nsIDOMWakeLock.idl \
+  nsIDOMWakeLockListener.idl \
   nsIPowerManagerService.idl \
   $(NULL)
 
 ifdef ENABLE_TESTS
 DIRS += test
 endif
 
 include $(topsrcdir)/config/config.mk
--- a/dom/power/PowerManager.cpp
+++ b/dom/power/PowerManager.cpp
@@ -31,59 +31,166 @@
  * 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 "PowerManager.h"
+#include "WakeLock.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
+#include "nsIDOMWakeLockListener.h"
 #include "nsIPowerManagerService.h"
+#include "nsIPrincipal.h"
+#include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 
 DOMCI_DATA(MozPowerManager, mozilla::dom::power::PowerManager)
 
 namespace mozilla {
 namespace dom {
 namespace power {
 
 NS_INTERFACE_MAP_BEGIN(PowerManager)
   NS_INTERFACE_MAP_ENTRY(nsIDOMMozPowerManager)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozPowerManager)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozWakeLockListener)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozPowerManager)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(PowerManager)
 NS_IMPL_RELEASE(PowerManager)
 
+nsresult
+PowerManager::Init(nsIDOMWindow *aWindow)
+{
+  mWindow = do_GetWeakReference(aWindow);
+
+  nsCOMPtr<nsIPowerManagerService> pmService =
+    do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+  NS_ENSURE_STATE(pmService);
+
+  // Add ourself to the global notification list.
+  pmService->AddWakeLockListener(this);
+  return NS_OK;
+}
+
+nsresult
+PowerManager::Shutdown()
+{
+  nsCOMPtr<nsIPowerManagerService> pmService =
+    do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+  NS_ENSURE_STATE(pmService);
+
+  // Remove ourself from the global notification list.
+  pmService->RemoveWakeLockListener(this);
+  return NS_OK;
+}
+
+nsresult
+PowerManager::CheckPermission()
+{
+  nsCOMPtr<nsPIDOMWindow> win = do_QueryReferent(mWindow);
+  NS_ENSURE_STATE(win);
+  nsCOMPtr<nsIDocument> doc = do_QueryInterface(win->GetExtantDocument());
+  NS_ENSURE_STATE(doc);
+
+  nsCOMPtr<nsIURI> uri;
+  doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
+
+  if (!nsContentUtils::URIIsChromeOrInPref(uri, "dom.power.whitelist")) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 PowerManager::Reboot()
 {
-  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
+  nsresult rv = CheckPermission();
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPowerManagerService> pmService =
     do_GetService(POWERMANAGERSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(pmService, NS_OK);
+  NS_ENSURE_STATE(pmService);
 
   pmService->Reboot();
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PowerManager::PowerOff()
 {
-  NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_DOM_SECURITY_ERR);
+  nsresult rv = CheckPermission();
+  NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIPowerManagerService> pmService =
     do_GetService(POWERMANAGERSERVICE_CONTRACTID);
-  NS_ENSURE_TRUE(pmService, NS_OK);
+  NS_ENSURE_STATE(pmService);
 
   pmService->PowerOff();
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PowerManager::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+  nsresult rv = CheckPermission();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // already added? bail out.
+  if (mListeners.Contains(aListener))
+    return NS_OK;
+
+  mListeners.AppendElement(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManager::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+  nsresult rv = CheckPermission();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  mListeners.RemoveElement(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManager::GetWakeLockState(const nsAString &aTopic, nsAString &aState)
+{
+  nsresult rv = CheckPermission();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIPowerManagerService> pmService =
+    do_GetService(POWERMANAGERSERVICE_CONTRACTID);
+  NS_ENSURE_STATE(pmService);
+
+  return pmService->GetWakeLockState(aTopic, aState);
+}
+
+NS_IMETHODIMP
+PowerManager::Callback(const nsAString &aTopic, const nsAString &aState)
+{
+  /**
+   * We maintain a local listener list instead of using the global
+   * list so that when the window is destroyed we don't have to
+   * cleanup the mess.
+   * Copy the listeners list before we walk through the callbacks
+   * because the callbacks may install new listeners. We expect no
+   * more than one listener per window, so it shouldn't be too long.
+   */
+  nsAutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mListeners);
+  for (PRUint32 i = 0; i < listeners.Length(); ++i) {
+    listeners[i]->Callback(aTopic, aState);
+  }
+
+  return NS_OK;
+}
+
 } // power
 } // dom
 } // mozilla
--- a/dom/power/PowerManager.h
+++ b/dom/power/PowerManager.h
@@ -32,30 +32,46 @@
  * 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_power_PowerManager_h
 #define mozilla_dom_power_PowerManager_h
 
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
 #include "nsIDOMPowerManager.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIDOMWindow.h"
+#include "nsWeakReference.h"
 
 namespace mozilla {
 namespace dom {
 namespace power {
 
 class PowerManager
   : public nsIDOMMozPowerManager
+  , public nsIDOMMozWakeLockListener
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDOMMOZPOWERMANAGER
+  NS_DECL_NSIDOMMOZWAKELOCKLISTENER
 
   PowerManager() {};
   virtual ~PowerManager() {};
+
+  nsresult Init(nsIDOMWindow *aWindow);
+  nsresult Shutdown();
+
+private:
+  nsresult CheckPermission();
+
+  nsWeakPtr mWindow;
+  nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mListeners;
 };
 
 } // namespace power
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_power_PowerManager_h
--- a/dom/power/PowerManagerService.cpp
+++ b/dom/power/PowerManagerService.cpp
@@ -31,43 +31,145 @@
  * 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/Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "nsIDOMWakeLockListener.h"
+#include "nsIDOMWindow.h"
 #include "PowerManagerService.h"
+#include "WakeLock.h"
 
 namespace mozilla {
 namespace dom {
 namespace power {
 
 NS_IMPL_ISUPPORTS1(PowerManagerService, nsIPowerManagerService)
 
+/* static */ nsRefPtr<PowerManagerService> PowerManagerService::sSingleton;
+
 /* static */ already_AddRefed<nsIPowerManagerService>
 PowerManagerService::GetInstance()
 {
-  nsCOMPtr<nsIPowerManagerService> pmService;
+  if (!sSingleton) {
+    sSingleton = new PowerManagerService();
+    sSingleton->Init();
+    ClearOnShutdown(&sSingleton);
+  }
+
+  nsCOMPtr<nsIPowerManagerService> service(do_QueryInterface(sSingleton));
+  return service.forget();
+}
+
+void
+PowerManagerService::Init()
+{
+  hal::RegisterWakeLockObserver(this);
+}
+
+PowerManagerService::~PowerManagerService()
+{
+  hal::UnregisterWakeLockObserver(this);
+}
 
-  pmService = new PowerManagerService();
+void
+PowerManagerService::ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo,
+                                          nsAString &aState)
+{
+  hal::WakeLockState state = hal::ComputeWakeLockState(aWakeLockInfo.numLocks(),
+                                                       aWakeLockInfo.numHidden());
+  switch (state) {
+  case hal::WAKE_LOCK_STATE_UNLOCKED:
+    aState.AssignLiteral("unlocked");
+    break;
+  case hal::WAKE_LOCK_STATE_HIDDEN:
+    aState.AssignLiteral("locked-background");
+    break;
+  case hal::WAKE_LOCK_STATE_VISIBLE:
+    aState.AssignLiteral("locked-foreground");
+    break;
+  }
+}
 
-  return pmService.forget();
+void
+PowerManagerService::Notify(const hal::WakeLockInformation& aWakeLockInfo)
+{
+  nsAutoString state;
+  ComputeWakeLockState(aWakeLockInfo, state);
+
+  /**
+   * Copy the listeners list before we walk through the callbacks
+   * because the callbacks may install new listeners. We expect no
+   * more than one listener per window, so it shouldn't be too long.
+   */
+  nsAutoTArray<nsCOMPtr<nsIDOMMozWakeLockListener>, 2> listeners(mWakeLockListeners);
+
+  for (PRUint32 i = 0; i < listeners.Length(); ++i) {
+    listeners[i]->Callback(aWakeLockInfo.topic(), state);
+  }
 }
 
 NS_IMETHODIMP
 PowerManagerService::Reboot()
 {
   hal::Reboot();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 PowerManagerService::PowerOff()
 {
   hal::PowerOff();
   return NS_OK;
 }
 
+NS_IMETHODIMP
+PowerManagerService::AddWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+  if (mWakeLockListeners.Contains(aListener))
+    return NS_OK;
+
+  mWakeLockListeners.AppendElement(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::RemoveWakeLockListener(nsIDOMMozWakeLockListener *aListener)
+{
+  mWakeLockListeners.RemoveElement(aListener);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::GetWakeLockState(const nsAString &aTopic, nsAString &aState)
+{
+  hal::WakeLockInformation info;
+  hal::GetWakeLockInfo(aTopic, &info);
+
+  ComputeWakeLockState(info, aState);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+PowerManagerService::NewWakeLock(const nsAString &aTopic,
+                                 nsIDOMWindow *aWindow,
+                                 nsIDOMMozWakeLock **aWakeLock)
+{
+  nsRefPtr<WakeLock> wakelock = new WakeLock();
+  nsresult rv = wakelock->Init(aTopic, aWindow);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIDOMMozWakeLock> wl =
+    do_QueryInterface(NS_ISUPPORTS_CAST(nsIDOMMozWakeLock*, wakelock));
+  wl.forget(aWakeLock);
+
+  return NS_OK;
+}
+
 } // power
 } // dom
 } // mozilla
--- a/dom/power/PowerManagerService.h
+++ b/dom/power/PowerManagerService.h
@@ -32,30 +32,52 @@
  * 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_power_PowerManagerService_h
 #define mozilla_dom_power_PowerManagerService_h
 
+#include "nsCOMPtr.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsTArray.h"
 #include "nsIPowerManagerService.h"
-#include "nsCOMPtr.h" // for already_AddRefed
+#include "mozilla/Observer.h"
+#include "Types.h"
 
 namespace mozilla {
 namespace dom {
 namespace power {
 
 class PowerManagerService
   : public nsIPowerManagerService
+  , public WakeLockObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIPOWERMANAGERSERVICE
 
   static already_AddRefed<nsIPowerManagerService> GetInstance();
+
+  void Init();
+
+  // Implement WakeLockObserver
+  void Notify(const hal::WakeLockInformation& aWakeLockInfo);
+
+private:
+
+  ~PowerManagerService();
+
+  void ComputeWakeLockState(const hal::WakeLockInformation& aWakeLockInfo,
+                            nsAString &aState);
+
+  static nsRefPtr<PowerManagerService> sSingleton;
+
+  nsTArray<nsCOMPtr<nsIDOMMozWakeLockListener> > mWakeLockListeners;
 };
 
 } // namespace power
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_power_PowerManagerService_h
new file mode 100644
--- /dev/null
+++ b/dom/power/Types.h
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+#ifndef mozilla_dom_power_Types_h
+#define mozilla_dom_power_Types_h
+
+namespace mozilla {
+namespace hal {
+class WakeLockInformation;
+} // namespace hal
+
+template <class T>
+class Observer;
+
+typedef Observer<hal::WakeLockInformation> WakeLockObserver;
+
+} // namespace mozilla
+
+#endif // mozilla_dom_power_Types_h
+
new file mode 100644
--- /dev/null
+++ b/dom/power/WakeLock.cpp
@@ -0,0 +1,204 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "mozilla/Hal.h"
+#include "mozilla/HalWakeLock.h"
+#include "nsDOMClassInfoID.h"
+#include "nsDOMError.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMEventTarget.h"
+#include "nsPIDOMWindow.h"
+#include "PowerManager.h"
+#include "WakeLock.h"
+
+DOMCI_DATA(MozWakeLock, mozilla::dom::power::WakeLock)
+
+namespace mozilla {
+namespace dom {
+namespace power {
+
+NS_INTERFACE_MAP_BEGIN(WakeLock)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMMozWakeLock)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMMozWakeLock)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(MozWakeLock)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_ADDREF(WakeLock)
+NS_IMPL_RELEASE(WakeLock)
+
+WakeLock::WakeLock()
+  : mLocked(false)
+  , mHidden(true)
+{
+}
+
+WakeLock::~WakeLock()
+{
+  DoUnlock();
+  DetachEventListener();
+}
+
+nsresult
+WakeLock::Init(const nsAString &aTopic, nsIDOMWindow *aWindow)
+{
+  mTopic.Assign(aTopic);
+
+  mWindow = do_GetWeakReference(aWindow);
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
+
+  /**
+   * Null windows are allowed. A wake lock without associated window
+   * is always considered invisible.
+   */
+  if (window) {
+    nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument();
+    NS_ENSURE_STATE(domDoc);
+    domDoc->GetMozHidden(&mHidden);
+  }
+
+  AttachEventListener();
+  DoLock();
+
+  return NS_OK;
+}
+
+void
+WakeLock::DoLock()
+{
+  if (!mLocked) {
+    // Change the flag immediately to prevent recursive reentering
+    mLocked = true;
+    hal::ModifyWakeLock(mTopic,
+                        hal::WAKE_LOCK_ADD_ONE,
+                        mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_NO_CHANGE);
+  }
+}
+
+void
+WakeLock::DoUnlock()
+{
+  if (mLocked) {
+    // Change the flag immediately to prevent recursive reentering
+    mLocked = false;
+    hal::ModifyWakeLock(mTopic,
+                        hal::WAKE_LOCK_REMOVE_ONE,
+                        mHidden ? hal::WAKE_LOCK_REMOVE_ONE : hal::WAKE_LOCK_NO_CHANGE);
+  }
+}
+
+void
+WakeLock::AttachEventListener()
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+  
+  if (window) {
+    nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument();
+    if (domDoc) {
+      nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(domDoc);
+      target->AddSystemEventListener(NS_LITERAL_STRING("mozvisibilitychange"),
+                                     this,
+                                     /* useCapture = */ true,
+                                     /* wantsUntrusted = */ false);
+
+      target = do_QueryInterface(window);
+      target->AddSystemEventListener(NS_LITERAL_STRING("pagehide"),
+                                     this,
+                                     /* useCapture = */ true,
+                                     /* wantsUntrusted = */ false);
+      target->AddSystemEventListener(NS_LITERAL_STRING("pageshow"),
+                                     this,
+                                     /* useCapture = */ true,
+                                     /* wantsUntrusted = */ false);
+    }
+  }
+}
+
+void
+WakeLock::DetachEventListener()
+{
+  nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
+
+  if (window) {
+    nsCOMPtr<nsIDOMDocument> domDoc = window->GetExtantDocument();
+    if (domDoc) {
+      nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(domDoc);
+      target->RemoveSystemEventListener(NS_LITERAL_STRING("mozvisibilitychange"),
+                                        this,
+                                        /* useCapture = */ true);
+      target = do_QueryInterface(window);
+      target->RemoveSystemEventListener(NS_LITERAL_STRING("pagehide"),
+                                        this,
+                                        /* useCapture = */ true);
+      target->RemoveSystemEventListener(NS_LITERAL_STRING("pageshow"),
+                                        this,
+                                        /* useCapture = */ true);
+    }
+  }
+}
+
+NS_IMETHODIMP
+WakeLock::Unlock()
+{
+  /*
+   * We throw NS_ERROR_DOM_INVALID_STATE_ERR on double unlock.
+   */
+  if (!mLocked) {
+    return NS_ERROR_DOM_INVALID_STATE_ERR;
+  }
+
+  DoUnlock();
+  DetachEventListener();
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WakeLock::GetTopic(nsAString &aTopic)
+{
+  aTopic.Assign(mTopic);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+WakeLock::HandleEvent(nsIDOMEvent *aEvent)
+{
+  nsAutoString type;
+  aEvent->GetType(type);
+
+  if (type.EqualsLiteral("mozvisibilitychange")) {
+    nsCOMPtr<nsIDOMEventTarget> target;
+    aEvent->GetTarget(getter_AddRefs(target));
+    nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(target);
+    NS_ENSURE_STATE(domDoc);
+    domDoc->GetMozHidden(&mHidden);
+
+    if (mLocked) {
+      hal::ModifyWakeLock(mTopic,
+                          hal::WAKE_LOCK_NO_CHANGE,
+                          mHidden ? hal::WAKE_LOCK_ADD_ONE : hal::WAKE_LOCK_REMOVE_ONE);
+    }
+
+    return NS_OK;
+  }
+
+  if (type.EqualsLiteral("pagehide")) {
+    DoUnlock();
+    return NS_OK;
+  }
+
+  if (type.EqualsLiteral("pageshow")) {
+    DoLock();
+    return NS_OK;
+  }
+
+  return NS_OK;
+}
+
+} // power
+} // dom
+} // mozilla
new file mode 100644
--- /dev/null
+++ b/dom/power/WakeLock.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#ifndef mozilla_dom_power_WakeLock_h
+#define mozilla_dom_power_WakeLock_h
+
+#include "nsCOMPtr.h"
+#include "nsIDOMWakeLock.h"
+#include "nsIDOMEventListener.h"
+#include "nsString.h"
+#include "nsWeakReference.h"
+
+class nsIDOMWindow;
+
+namespace mozilla {
+namespace dom {
+namespace power {
+
+class WakeLock
+  : public nsIDOMMozWakeLock
+  , public nsIDOMEventListener
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMMOZWAKELOCK
+  NS_DECL_NSIDOMEVENTLISTENER
+
+  WakeLock();
+  virtual ~WakeLock();
+
+  nsresult Init(const nsAString &aTopic, nsIDOMWindow *aWindow);
+
+private:
+  void     DoUnlock();
+  void     DoLock();
+  void     AttachEventListener();
+  void     DetachEventListener();
+
+  bool      mLocked;
+  bool      mHidden;
+  nsString  mTopic;
+
+  // window that this was created for.  Weak reference.
+  nsWeakPtr mWindow;
+};
+
+} // namespace power
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_power_WakeLock_h
--- a/dom/power/nsIDOMPowerManager.idl
+++ b/dom/power/nsIDOMPowerManager.idl
@@ -32,14 +32,45 @@
  * 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, uuid(6ec16abc-2fe8-4ab3-99b0-0f08405be81b)]
+interface nsIDOMMozWakeLockListener;
+
+/**
+ * This interface implements navigator.mozPower
+ */
+[scriptable, uuid(abf4b2b1-139d-4eff-998d-8f24616910ae)]
 interface nsIDOMMozPowerManager : nsISupports
 {
-    void powerOff();
-    void reboot();
+    void    powerOff();
+    void    reboot();
+
+    /**
+     * The listeners are notified when a resource changes its lock state to:
+     *  - unlocked
+     *  - locked but not visible
+     *  - locked and visible
+     */
+    void    addWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+    void    removeWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+
+    /**
+     * Query the wake lock state of the topic.
+     *
+     * Possible states are:
+     *
+     *  - "unlocked" - nobody holds the wake lock.
+     *
+     *  - "locked-foreground" - at least one window holds the wake lock,
+     *    and it is visible.
+     *
+     *  - "locked-background" - at least one window holds the wake lock,
+     *    but all of them are hidden.
+     *
+     * @param aTopic The resource name related to the wake lock.
+     */
+    DOMString getWakeLockState(in DOMString aTopic);
 };
new file mode 100644
--- /dev/null
+++ b/dom/power/nsIDOMWakeLock.idl
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(2e61eed1-5983-4562-8f26-fd361ab4a00d)]
+interface nsIDOMMozWakeLock : nsISupports
+{
+    readonly attribute DOMString topic;
+
+    /**
+     * Release the wake lock.
+     *
+     * @throw NS_ERROR_DOM_INVALID_STATE_ERR if already unlocked.
+     */
+    void unlock();
+};
new file mode 100644
--- /dev/null
+++ b/dom/power/nsIDOMWakeLockListener.idl
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 40; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+#include "nsISupports.idl"
+
+[scriptable, function, uuid(4e258af8-cffb-47bc-b16d-e8241243426e)]
+interface nsIDOMMozWakeLockListener : nsISupports
+{
+  /**
+   * The callback will be called when a lock topic changes its lock
+   * state.
+   *
+   * Possible states are:
+   *
+   *  - "unlocked" - nobody holds the wake lock.
+   *
+   *  - "locked-foreground" - at least one window holds the wake lock,
+   *    and it is visible.
+   *
+   *  - "locked-background" - at least one window holds the wake lock,
+   *    but all of them are hidden.
+   *
+   * @param aTopic The resource name related to the wake lock.
+   * @param aState The wake lock state
+   */
+  void callback(in DOMString aTopic, in DOMString aState);
+};
--- a/dom/power/nsIPowerManagerService.idl
+++ b/dom/power/nsIPowerManagerService.idl
@@ -37,14 +37,31 @@
 
 #include "nsISupports.idl"
 
 %{C++
 #define NS_POWERMANAGERSERVICE_CID { 0x18c2e238, 0x3a0a, 0x4153, {0x89, 0xfc, 0x16, 0x6b, 0x3b, 0x14, 0x65, 0xa1 } }
 #define POWERMANAGERSERVICE_CONTRACTID "@mozilla.org/power/powermanagerservice;1"
 %}
 
-[scriptable, builtinclass, uuid(38919539-4641-4f0b-9f11-6b6294a9386f)]
+interface nsIDOMMozWakeLock;
+interface nsIDOMMozWakeLockListener;
+interface nsIDOMWindow;
+
+/**
+ * For use with non-content code.
+ */
+[scriptable, builtinclass, uuid(235ca1a1-d0c8-41f3-9b4a-dbaa4437d69c)]
 interface nsIPowerManagerService : nsISupports
 {
-    void powerOff();
-    void reboot();
+  void              powerOff();
+  void              reboot();
+  void              addWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+  void              removeWakeLockListener(in nsIDOMMozWakeLockListener aListener);
+  DOMString         getWakeLockState(in DOMString aTopic);
+
+  /**
+   * Return a wake lock object of aTopic associated with aWindow.
+   * A wake lock without associated window, e.g. used in chrome, is
+   * always considered invisible.
+   */
+  nsIDOMMozWakeLock newWakeLock(in DOMString aTopic, [optional] in nsIDOMWindow aWindow);
 };
--- a/dom/power/test/Makefile.in
+++ b/dom/power/test/Makefile.in
@@ -47,13 +47,20 @@ DIRS = \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
   test_power_basics.html \
   $(NULL)
 
+_BROWSER_TEST_FILES = \
+  browser_bug697132.js \
+  $(NULL)
+
 libs:: $(_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
+libs:: $(_BROWSER_TEST_FILES)
+	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
+
 #libs:: $(_CHROME_TEST_FILES)
 #	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/dom/power/test/browser_bug697132.js
@@ -0,0 +1,235 @@
+"use strict";
+
+waitForExplicitFinish();
+
+let kPrefNode = "dom.power.whitelist";
+let kPageSource1 = "data:text/html,1";
+let kPageSource2 = "data:text/html,2";
+
+let gOldPref;
+let gWin, gWin1, gWin2;
+let gTab, gTab1, gTab2;
+let gLock, gLock1, gLock2;
+let gCurStepIndex = -1;
+let gSteps = [
+  function basicWakeLock() {
+    gTab = gBrowser.addTab(kPageSource1);
+    gWin = gBrowser.getBrowserForTab(gTab).contentWindow;
+    let browser = gBrowser.getBrowserForTab(gTab);
+
+    browser.addEventListener("load", function onLoad(e) {
+      browser.removeEventListener("load", onLoad, true);
+      let nav = gWin.navigator;
+      let power = nav.mozPower;
+      gLock = nav.requestWakeLock("test");
+
+      ok(gLock != null,
+         "navigator.requestWakeLock should return a wake lock");
+      is(gLock.topic, "test",
+         "wake lock should remember the locked topic");
+      isnot(power.getWakeLockState("test"), "unlocked",
+            "topic is locked");
+
+      gLock.unlock();
+
+      is(gLock.topic, "test",
+         "wake lock should remember the locked topic even after unlock");
+      is(power.getWakeLockState("test"), "unlocked",
+         "topic is unlocked");
+
+      try {
+        gLock.unlock();
+        ok(false, "Should have thrown an error.");
+      } catch (e) {
+        is(e.code, DOMException.INVALID_STATE_ERR, "double unlock should throw InvalidStateError");
+      }
+
+      gBrowser.removeTab(gTab);
+
+      executeSoon(runNextStep);
+    }, true);
+  },
+  function multiWakeLock() {
+    gTab = gBrowser.addTab(kPageSource1);
+    gWin = gBrowser.getBrowserForTab(gTab).contentWindow;
+    let browser = gBrowser.getBrowserForTab(gTab);
+
+    browser.addEventListener("load", function onLoad(e) {
+      browser.removeEventListener("load", onLoad, true);
+      let nav = gWin.navigator;
+      let power = nav.mozPower;
+      let count = 0;
+      power.addWakeLockListener(function onWakeLockEvent(topic, state) {
+        is(topic, "test", "gLock topic is test");
+        ok(state == "unlocked" ||
+           state == "locked-foreground" ||
+           state == "locked-background",
+           "wake lock should be either locked or unlocked");
+        count++;
+        if (state == "locked-foreground" ||
+            state == "locked-background") {
+          is(count, 1,
+             "wake lock should be locked and the listener should only fire once");
+        }
+        if (state == "unlocked") {
+          is(count, 2,
+             "wake lock should be unlocked and the listener should only fire once");
+
+          ok(power.getWakeLockState("test") == "unlocked",
+             "topic is unlocked");
+          power.removeWakeLockListener(onWakeLockEvent);
+          gBrowser.removeTab(gTab);
+          executeSoon(runNextStep);
+        }
+      });
+
+      gLock1 = nav.requestWakeLock("test");
+      isnot(power.getWakeLockState("test"), "unlocked",
+            "topic is locked");
+
+      gLock2 = nav.requestWakeLock("test");
+      isnot(power.getWakeLockState("test"), "unlocked",
+            "topic is locked");
+
+      gLock1.unlock();
+      isnot(power.getWakeLockState("test"), "unlocked",
+            "topic is locked");
+
+      gLock2.unlock();
+    }, true);
+  },
+  function crossTabWakeLock1() {
+    gTab1 = gBrowser.addTab(kPageSource1);
+    gWin1 = gBrowser.getBrowserForTab(gTab1).contentWindow;
+    gTab2 = gBrowser.addTab(kPageSource1);
+    gWin2 = gBrowser.getBrowserForTab(gTab2).contentWindow;
+
+    gBrowser.selectedTab = gTab1;
+    let browser = gBrowser.getBrowserForTab(gTab2);
+
+    browser.addEventListener("load", function onLoad(e) {
+      browser.removeEventListener("load", onLoad, true);
+      gLock2 = gWin2.navigator.requestWakeLock("test");
+      is(gWin2.document.mozHidden, true,
+         "window is background")
+      is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-background",
+         "wake lock is background");
+      let doc2 = gWin2.document;
+      doc2.addEventListener("mozvisibilitychange", function onVisibilityChange(e) {
+        if (!doc2.mozHidden) {
+          doc2.removeEventListener("mozvisibilitychange", onVisibilityChange);
+          executeSoon(runNextStep);
+        }
+      });
+      gBrowser.selectedTab = gTab2;
+    }, true);
+  },
+  function crossTabWakeLock2() {
+    is(gWin2.document.mozHidden, false,
+       "window is foreground")
+    is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+      "wake lock is foreground");
+    gWin2.addEventListener("pagehide", function onPageHide(e) {
+      gWin2.removeEventListener("pagehide", onPageHide, true);
+      executeSoon(runNextStep);
+    }, true);
+    gWin2.addEventListener("pageshow", function onPageShow(e) {
+      gWin2.removeEventListener("pageshow", onPageShow, true);
+      executeSoon(runNextStep);
+    }, true);
+    gWin2.location = kPageSource2;
+  },
+  function crossTabWakeLock3() {
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "unlocked",
+       "wake lock should auto-unlock when page is unloaded");
+    gWin2.back();
+    // runNextStep called in onPageShow
+  },
+  function crossTabWakeLock4() {
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+       "wake lock should auto-reacquire when page is available again");
+    gBrowser.selectedTab = gTab1;
+    executeSoon(runNextStep);
+  },
+  function crossTabWakeLock5() {
+    // Test again in background tab
+    is(gWin2.document.mozHidden, true,
+       "window is background")
+    is(gWin2.navigator.mozPower.getWakeLockState("test"), "locked-background",
+      "wake lock is background");
+    gWin2.addEventListener("pagehide", function onPageHide(e) {
+      gWin2.removeEventListener("pagehide", onPageHide, true);
+      executeSoon(runNextStep);
+    }, true);
+    gWin2.addEventListener("pageshow", function onPageShow(e) {
+      gWin2.removeEventListener("pageshow", onPageShow, true);
+      executeSoon(runNextStep);
+    }, true);
+    gWin2.location = kPageSource2;
+  },
+  function crossTabWakeLock6() {
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "unlocked",
+       "wake lock should auto-unlock when page is unloaded");
+    gWin2.back();
+    // runNextStep called in onPageShow
+  },
+  function crossTabWakeLock7() {
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-background",
+       "wake lock should auto-reacquire when page is available again");
+    gLock2.unlock();
+    gBrowser.selectedTab = gTab2;
+    executeSoon(runNextStep);
+  },
+  function crossTabWakeLock8() {
+    is(gWin1.document.mozHidden, true,
+       "gWin1 is background");
+    is(gWin2.document.mozHidden, false,
+       "gWin2 is foreground");
+
+    gLock1 = gWin1.navigator.requestWakeLock("test");
+    gLock2 = gWin2.navigator.requestWakeLock("test");
+
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+       "topic is locked-foreground when one page is foreground and one is background");
+
+    gLock2.unlock();
+
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-background",
+       "topic is locked-background when all locks are background");
+
+    gLock2 = gWin2.navigator.requestWakeLock("test");
+
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+       "topic is locked-foreground when one page is foreground and one is background");
+
+    gLock1.unlock();
+
+    is(gWin1.navigator.mozPower.getWakeLockState("test"), "locked-foreground",
+       "topic is locked-foreground");
+
+    gBrowser.removeTab(gTab1);
+    gBrowser.removeTab(gTab2);
+    executeSoon(runNextStep);
+  },
+];
+
+function runNextStep() {
+  gCurStepIndex++;
+  if (gCurStepIndex < gSteps.length) {
+    gSteps[gCurStepIndex]();
+  } else {
+    Services.prefs.setCharPref(kPrefNode, gOldPref);
+    finish();
+  }
+}
+
+function test() {
+  try {
+    gOldPref = Services.prefs.getCharPref(kPrefNode);
+  } catch (e) {
+    gOldPref = "";
+  }
+  // data url inherits its parent's principal, which is |about:| here.
+  Services.prefs.setCharPref(kPrefNode, "about:");
+  runNextStep();
+}
--- a/dom/sms/src/SmsFilter.cpp
+++ b/dom/sms/src/SmsFilter.cpp
@@ -185,22 +185,22 @@ SmsFilter::SetNumbers(JSContext* aCx, co
     return NS_ERROR_INVALID_ARG;
   }
 
   JSObject& obj = aNumbers.toObject();
   if (!JS_IsArrayObject(aCx, &obj)) {
     return NS_ERROR_INVALID_ARG;
   }
 
-  jsuint size;
+  uint32_t size;
   JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, &obj, &size));
 
   nsTArray<nsString> numbers;
 
-  for (jsuint i=0; i<size; ++i) {
+  for (uint32_t i=0; i<size; ++i) {
     jsval jsNumber;
     if (!JS_GetElement(aCx, &obj, i, &jsNumber)) {
       return NS_ERROR_INVALID_ARG;
     }
 
     if (!jsNumber.isString()) {
       return NS_ERROR_INVALID_ARG;
     }
--- a/dom/sms/src/SmsManager.cpp
+++ b/dom/sms/src/SmsManager.cpp
@@ -194,22 +194,22 @@ SmsManager::Send(const jsval& aNumber, c
 
   if (aNumber.isString()) {
     return Send(cx, global, aNumber.toString(), aMessage, aReturn);
   }
 
   // Must be an array then.
   JSObject& numbers = aNumber.toObject();
 
-  jsuint size;
+  uint32_t size;
   JS_ALWAYS_TRUE(JS_GetArrayLength(cx, &numbers, &size));
 
   jsval* requests = new jsval[size];
 
-  for (jsuint i=0; i<size; ++i) {
+  for (uint32_t i=0; i<size; ++i) {
     jsval number;
     if (!JS_GetElement(cx, &numbers, i, &number)) {
       return NS_ERROR_INVALID_ARG;
     }
 
     nsresult rv = Send(cx, global, number.toString(), aMessage, &requests[i]);
     NS_ENSURE_SUCCESS(rv, rv);
   }
--- a/dom/sms/src/ril/SmsDatabaseService.js
+++ b/dom/sms/src/ril/SmsDatabaseService.js
@@ -6,46 +6,618 @@
 
 const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 const RIL_SMSDATABASESERVICE_CONTRACTID = "@mozilla.org/sms/rilsmsdatabaseservice;1";
 const RIL_SMSDATABASESERVICE_CID = Components.ID("{a1fa610c-eb6c-4ac2-878f-b005d5e89249}");
 
+const DEBUG = true;
+const DB_NAME = "sms";
+const DB_VERSION = 1;
+const STORE_NAME = "sms";
+
+const DELIVERY_SENT = "sent";
+const DELIVERY_RECEIVED = "received";
+
+const FILTER_TIMESTAMP = "timestamp";
+const FILTER_NUMBERS = "numbers";
+const FILTER_DELIVERY = "delivery";
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
+                                   "@mozilla.org/sms/smsservice;1",
+                                   "nsISmsService");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsRequestManager",
+                                   "@mozilla.org/sms/smsrequestmanager;1",
+                                   "nsISmsRequestManager");
+
+XPCOMUtils.defineLazyServiceGetter(this, "gIDBManager",
+                                   "@mozilla.org/dom/indexeddb/manager;1",
+                                   "nsIIndexedDatabaseManager");
+
+const GLOBAL_SCOPE = this;
+
 /**
  * SmsDatabaseService
  */
 function SmsDatabaseService() {
+  gIDBManager.initWindowless(GLOBAL_SCOPE);
+
+  let that = this;
+  this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function(error, txn, store){
+    if (error) {
+      return;
+    }
+    // In order to get the highest key value, we open a key cursor in reverse
+    // order and get only the first pointed value.
+    let request = store.openCursor(null, Ci.nsIIDBCursor.PREV);
+    request.onsuccess = function onsuccess(event) {
+      let cursor = event.target.result;
+      if (!cursor) {
+        if (DEBUG) {
+          debug("Could not get the last key from sms database. " +
+                "Probably empty database");
+        }
+        return;
+      }
+      that.lastKey = cursor.key || 0;
+      if (DEBUG) debug("Last assigned message ID was " + that.lastKey);
+    };
+    request.onerror = function onerror(event) {
+      if (DEBUG) {
+        debug("Could not get the last key from sms database " +
+              event.target.errorCode);
+      }
+    };
+  });
+
+  this.messageLists = {};
 }
 SmsDatabaseService.prototype = {
 
   classID:   RIL_SMSDATABASESERVICE_CID,
-  QueryInterface: XPCOMUtils.generateQI([Ci.nsISmsDatabaseService]),
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISmsDatabaseService,
+                                         Ci.nsIObserver]),
+
+  /**
+   * Cache the DB here.
+   */
+  db: null,
+
+  /**
+   * This object keeps the message lists associated with each search. Each
+   * message list is stored as an array of primary keys.
+   */
+  messageLists: null,
+
+  lastMessageListId: 0,
+
+  /**
+   * Last key value stored in the database.
+   */
+  lastKey: 0,
+
+  /**
+   * nsIObserver
+   */
+  observe: function observe() {},
+
+  /**
+   * Prepare the database. This may include opening the database and upgrading
+   * it to the latest schema version.
+   *
+   * @param callback
+   *        Function that takes an error and db argument. It is called when
+   *        the database is ready to use or if an error occurs while preparing
+   *        the database.
+   *
+   * @return (via callback) a database ready for use.
+   */
+  ensureDB: function ensureDB(callback) {
+    if (this.db) {
+      if (DEBUG) debug("ensureDB: already have a database, returning early.");
+      callback(null, this.db);
+      return;
+    }
+
+    let self = this;
+    function gotDB(db) {
+      self.db = db;
+      callback(null, db);
+    }
+
+    let request = GLOBAL_SCOPE.mozIndexedDB.open(DB_NAME, DB_VERSION);
+    request.onsuccess = function (event) {
+      if (DEBUG) debug("Opened database:", DB_NAME, DB_VERSION);
+      gotDB(event.target.result);
+    };
+    request.onupgradeneeded = function (event) {
+      if (DEBUG) {
+        debug("Database needs upgrade:", DB_NAME,
+              event.oldVersion, event.newVersion);
+        debug("Correct new database version:", event.newVersion == DB_VERSION);
+      }
+
+      let db = event.target.result;
+
+      switch (event.oldVersion) {
+        case 0:
+          if (DEBUG) debug("New database");
+          self.createSchema(db);
+          break;
+
+        default:
+          event.target.transaction.abort();
+          callback("Old database version: " + event.oldVersion, null);
+          break;
+      }
+    };
+    request.onerror = function (event) {
+      //TODO look at event.target.Code and change error constant accordingly
+      callback("Error opening database!", null);
+    };
+    request.onblocked = function (event) {
+      callback("Opening database request is blocked.", null);
+    };
+  },
 
-  // nsISmsDatabaseService
+  /**
+   * Start a new transaction.
+   *
+   * @param txn_type
+   *        Type of transaction (e.g. IDBTransaction.READ_WRITE)
+   * @param callback
+   *        Function to call when the transaction is available. It will
+   *        be invoked with the transaction and the 'sms' object store.
+   */
+  newTxn: function newTxn(txn_type, callback) {
+    this.ensureDB(function (error, db) {
+      if (error) {
+        if (DEBUG) debug("Could not open database: " + error);
+        callback(error);
+        return;
+      }
+      let txn = db.transaction([STORE_NAME], txn_type);
+      if (DEBUG) debug("Started transaction " + txn + " of type " + txn_type);
+      if (DEBUG) {
+        txn.oncomplete = function oncomplete(event) {
+          debug("Transaction " + txn + " completed.");
+        };
+        txn.onerror = function onerror(event) {
+          //TODO check event.target.errorCode and show an appropiate error
+          //     message according to it.
+          debug("Error occurred during transaction: " + event.target.errorCode);
+        };
+      }
+      if (DEBUG) debug("Retrieving object store", STORE_NAME);
+      let store = txn.objectStore(STORE_NAME);
+      callback(null, txn, store);
+    });
+  },
+
+  /**
+   * Create the initial database schema.
+   *
+   * TODO need to worry about number normalization somewhere...
+   * TODO full text search on body???
+   * TODO We probably want to add a 'read' index
+   */
+  createSchema: function createSchema(db) {
+    let objectStore = db.createObjectStore(STORE_NAME, { keyPath: "id" });
+    objectStore.createIndex("id", "id", { unique: true });
+    objectStore.createIndex("delivery", "delivery", { unique: false });
+    objectStore.createIndex("sender", "sender", { unique: false });
+    objectStore.createIndex("receiver", "receiver", { unique: false });
+    objectStore.createIndex("timestamp", "timestamp", { unique:false });
+    if (DEBUG) debug("Created object stores and indexes");
+  },
+
+  /**
+   * Helper function to make the intersection of the partial result arrays
+   * obtained within createMessageList.
+   *
+   * @param keys
+   *        Object containing the partial result arrays.
+   * @param fiter
+   *        Object containing the filter search criteria used to retrieved the
+   *        partial results.
+   *
+   * return Array of keys containing the final result of createMessageList.
+   */
+  keyIntersection: function keyIntersection(keys, filter) {
+    let result = keys[FILTER_TIMESTAMP];
+    if (keys[FILTER_NUMBERS].length || filter.numbers) {
+      result = keys[FILTER_NUMBERS].filter(function(i) {
+        return result.indexOf(i) != -1;
+      });
+    }
+    if (keys[FILTER_DELIVERY].length || filter.delivery) {
+      result = keys[FILTER_DELIVERY].filter(function(i) {
+        return result.indexOf(i) != -1;
+      });
+    }
+    return result;
+  },
+
+  /**
+   * Helper function called after createMessageList gets the final result array
+   * containing the list of primary keys of records that matches the provided
+   * search criteria. This function retrieves from the store the message with
+   * the primary key matching the first one in the message list array and keeps
+   * the rest of this array in memory. It also notifies via gSmsRequestManager.
+   *
+   * @param messageList
+   *        Array of primary keys retrieved within createMessageList.
+   * @param requestId
+   *        Id used by the SmsRequestManager
+   */
+  onMessageListCreated: function onMessageListCreated(messageList, requestId) {
+    if (DEBUG) debug("Message list created: " + messageList);
+    let self = this;
+    self.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (error) {
+        gSmsRequestManager.notifyReadMessageListFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+        return;
+      }
+
+      let messageId = messageList.shift();
+      if (DEBUG) debug ("Fetching message " + messageId);
+      let request = store.get(messageId);
+      let message;
+      request.onsuccess = function (event) {
+        message = request.result;
+      };
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        if (!message) {
+          gSmsRequestManager.notifyReadMessageListFailed(
+            requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+          return;
+        }
+        self.lastMessageListId += 1;
+        self.messageLists[self.lastMessageListId] = messageList;
+        let sms = gSmsService.createSmsMessage(message.id,
+                                               message.delivery,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.timestamp);
+        gSmsRequestManager.notifyCreateMessageList(requestId,
+                                                   self.lastMessageListId,
+                                                   sms);
+      };
+    });
+  },
+
+  saveMessage: function saveMessage(message) {
+    this.lastKey += 1;
+    message.id = this.lastKey;
+    if (DEBUG) debug("Going to store " + JSON.stringify(message));
+    this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function(error, txn, store) {
+      if (error) {
+        return;
+      }
+      let request = store.put(message);
+    });
+    // We return the key that we expect to store in the db
+    return message.id;
+  },
+
+
+  /**
+   * nsISmsDatabaseService API
+   */
 
   saveReceivedMessage: function saveReceivedMessage(sender, body, date) {
-    return -1;
+    let message = {delivery:  DELIVERY_RECEIVED,
+                   sender:    sender,
+                   receiver:  null,  //TODO see bug 733266
+                   body:      body,
+                   timestamp: date};
+    return this.saveMessage(message);
   },
 
   saveSentMessage: function saveSentMessage(receiver, body, date) {
-    return -1;
+    let message = {delivery:  DELIVERY_SENT,
+                   sender:    null, //TODO see bug 733266
+                   receiver:  receiver,
+                   body:      body,
+                   timestamp: date};
+    return this.saveMessage(message);
   },
 
   getMessage: function getMessage(messageId, requestId) {
+    if (DEBUG) debug("Retrieving message with ID " + messageId);
+    this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (error) {
+        if (DEBUG) debug(error);
+        gSmsRequestManager.notifyGetSmsFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+        return;
+      }
+      let request = store.getAll(messageId);
+
+      txn.oncomplete = function oncomplete() {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        if (request.result.length > 1) {
+          if (DEBUG) debug("Got too many results for id " + messageId);
+          gSmsRequestManager.notifyGetSmsFailed(
+            requestId, Ci.nsISmsRequestManager.UNKNOWN_ERROR);
+          return;
+        }
+        let data = request.result[0];
+        if (!data) {
+          if (DEBUG) debug("Message ID " + messageId + " not found");
+          gSmsRequestManager.notifyGetSmsFailed(
+            requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
+          return;
+        }
+        if (data.id != messageId) {
+          if (DEBUG) {
+            debug("Requested message ID (" + messageId + ") is " +
+                  "different from the one we got");
+          }
+          gSmsRequestManager.notifyGetSmsFailed(
+            requestId, Ci.nsISmsRequestManager.UNKNOWN_ERROR);
+          return;
+        }
+        let message = gSmsService.createSmsMessage(data.id,
+                                                   data.delivery,
+                                                   data.sender,
+                                                   data.receiver,
+                                                   data.body,
+                                                   data.timestamp);
+        gSmsRequestManager.notifyGotSms(requestId, message);
+      };
+
+      txn.onerror = function onerror(event) {
+        if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
+        //TODO look at event.target.errorCode, pick appropriate error constant
+        gSmsRequestManager.notifyGetSmsFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+    });
   },
 
   deleteMessage: function deleteMessage(messageId, requestId) {
+    let self = this;
+    this.newTxn(Ci.nsIIDBTransaction.READ_WRITE, function (error, txn, store) {
+      if (error) {
+        gSmsRequestManager.notifySmsDeleteFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+        return;
+      }
+      let request = store.delete(messageId);
+
+      request.onerror = function onerror(event) {
+        if (DEBUG) debug("Caught error on request ", event.target.errorCode);
+        //TODO look at event.target.errorCode
+        gSmsRequestManager.notifySmsDeleteFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        // Once we transaction is done, we need to check if we actually deleted
+        // the message. As IndexedDB does not provide the affected records info,
+        // we need to try to get the message from the database again to check
+        // that it is actually gone.
+        self.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+          let request = store.getAll(messageId);
+          request.onsuccess = function onsuccess(event) {
+            let deleted = (event.target.result.length == 0);
+            gSmsRequestManager.notifySmsDeleted(requestId, deleted);
+          };
+          request.onerror = function onerror(event) {
+            if (DEBUG) {
+              debug("Error checking the message deletion " +
+                    event.target.errorCode);
+            }
+            //TODO should we notify here as an internal error? The failed check
+            //     does not mean that the deletion has failed, so maybe we
+            //     should notify successfully.
+            gSmsRequestManager.notifySmsDeleteFailed(
+              requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+          };
+        });
+      };
+
+      txn.onerror = function onerror(event) {
+        if (DEBUG) debug("Caught error on transaction", event.target.errorCode);
+        //TODO look at event.target.errorCode, pick appropriate error constant
+        gSmsRequestManager.notifySmsDeleteFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+    });
   },
 
   createMessageList: function createMessageList(filter, reverse, requestId) {
+    if (DEBUG) {
+      debug("Creating a message list. Filters:" +
+            " startDate: " + filter.startDate +
+            " endDate: " + filter.endDate +
+            " delivery: " + filter.delivery +
+            " numbers: " + filter.numbers +
+            " reverse: " + reverse);
+    }
+    // This object keeps the lists of keys retrieved by the search specific to
+    // each nsIMozSmsFilter. Once all the keys have been retrieved from the
+    // store, the final intersection of this arrays will contain all the
+    // keys for the message list that we are creating.
+    let filteredKeys = {};
+    filteredKeys[FILTER_TIMESTAMP] = [];
+    filteredKeys[FILTER_NUMBERS] = [];
+    filteredKeys[FILTER_DELIVERY] = [];
+
+    // Callback function to iterate through request results via IDBCursor.
+    let successCb = function onsuccess(result, filter) {
+      // Once the cursor has retrieved all keys that matches its key range,
+      // the filter search is done.
+      if (!result) {
+        if (DEBUG) {
+          debug("These messages match the " + filter + " filter: " +
+                filteredKeys[filter]);
+      }
+        return;
+      }
+      // The cursor primaryKey is stored in its corresponding partial array
+      // according to the filter parameter.
+      let primaryKey = result.primaryKey;
+      filteredKeys[filter].push(primaryKey);
+      result.continue();
+    };
+
+    let errorCb = function onerror(event) {
+      //TODO look at event.target.errorCode, pick appropriate error constant.
+      if (DEBUG) debug("IDBRequest error " + event.target.errorCode);
+      gSmsRequestManager.notifyReadMessageListFailed(
+        requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      return;
+    };
+
+    let self = this;
+    this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (error) {
+        errorCb(error);
+        return;
+      }
+
+      // In first place, we retrieve the keys that match the filter.startDate
+      // and filter.endDate search criteria.
+      let timeKeyRange = null;
+      if (!filter.startDate != null && filter.endDate != null) {
+        timeKeyRange = IDBKeyRange.bound(filter.startDate.getTime(),
+                                         filter.endDate.getTime());
+      } else if (filter.startDate != null) {
+        timeKeyRange = IDBKeyRange.lowerBound(filter.startDate.getTime());
+      } else if (filter.endDate != null) {
+        timeKeyRange = IDBKeyRange.upperBound(filter.endDate.getTime());
+      }
+      let direction = reverse ? Ci.nsIIDBCursor.PREV : Ci.nsIIDBCursor.NEXT;
+      let timeRequest = store.index("timestamp").openKeyCursor(timeKeyRange,
+                                                               direction);
+
+      timeRequest.onsuccess = function onsuccess(event) {
+        successCb(event.target.result, FILTER_TIMESTAMP);
+      };
+      timeRequest.onerror = errorCb;
+
+      // Retrieve the keys from the 'delivery' index that matches the
+      // value of filter.delivery.
+      if (filter.delivery) {
+        let deliveryKeyRange = IDBKeyRange.only(filter.delivery);
+        let deliveryRequest = store.index("delivery")
+                                   .openKeyCursor(deliveryKeyRange);
+        deliveryRequest.onsuccess = function onsuccess(event) {
+          successCb(event.target.result, FILTER_DELIVERY);
+        };
+        deliveryRequest.onerror = errorCb;
+      }
+
+      // Retrieve the keys from the 'sender' and 'receiver' indexes that
+      // match the values of filter.numbers
+      if (filter.numbers) {
+        for (let i = 0; i < filter.numbers.length; i++) {
+          let numberKeyRange = IDBKeyRange.only(filter.numbers[i]);
+          let senderRequest = store.index("sender")
+                                   .openKeyCursor(numberKeyRange);
+          let receiverRequest = store.index("receiver")
+                                     .openKeyCursor(numberKeyRange);
+          senderRequest.onsuccess = receiverRequest.onsuccess =
+            function onsuccess(event){
+              successCb(event.target.result, FILTER_NUMBERS);
+            };
+          senderRequest.onerror = receiverRequest.onerror = errorCb;
+        }
+      }
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        // We need to get the intersection of all the partial searches to
+        // get the final result array.
+        let result =  self.keyIntersection(filteredKeys, filter);
+        if (!result.length) {
+          if (DEBUG) debug("No messages matching the filter criteria");
+          gSmsRequestManager.notifyNoMessageInList(requestId);
+          return;
+        }
+
+        // At this point, filteredKeys should have all the keys that matches
+        // all the search filters. So we take the first key and retrieve the
+        // corresponding message. The rest of the keys are added to the
+        // messageLists object as a new list.
+        self.onMessageListCreated(result, requestId);
+      };
+
+      txn.onerror = function onerror(event) {
+        errorCb(event);
+      };
+    });
   },
 
   getNextMessageInList: function getNextMessageInList(listId, requestId) {
+    if (DEBUG) debug("Getting next message in list " + listId);
+    let messageId;
+    let list = this.messageLists[listId];
+    if (!list) {
+      if (DEBUG) debug("Wrong list id");
+      gSmsRequestManager.notifyReadMessageListFailed(
+        requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
+      return;
+    }
+    messageId = list.shift();
+    if (messageId == null) {
+      if (DEBUG) debug("Reached the end of the list!");
+      gSmsRequestManager.notifyNoMessageInList(requestId);
+      return;
+    }
+    this.newTxn(Ci.nsIIDBTransaction.READ_ONLY, function (error, txn, store) {
+      if (DEBUG) debug("Fetching message " + messageId);
+      let request = store.get(messageId);
+      let message;
+      request.onsuccess = function onsuccess(event) {
+        message = request.result;
+      };
+
+      txn.oncomplete = function oncomplete(event) {
+        if (DEBUG) debug("Transaction " + txn + " completed.");
+        if (!message) {
+          if (DEBUG) debug("Could not get message id " + messageId);
+          gSmsRequestManager.notifyReadMessageListFailed(
+            requestId, Ci.nsISmsRequestManager.NOT_FOUND_ERROR);
+        }
+        let sms = gSmsService.createSmsMessage(message.id,
+                                               message.delivery,
+                                               message.sender,
+                                               message.receiver,
+                                               message.body,
+                                               message.timestamp);
+        gSmsRequestManager.notifyGotNextMessage(requestId, sms);
+      };
+
+      txn.onerror = function onerror(event) {
+        //TODO check event.target.errorCode
+        if (DEBUG) {
+          debug("Error retrieving message id: " + messageId +
+                ". Error code: " + event.target.errorCode);
+        }
+        gSmsRequestManager.notifyReadMessageListFailed(
+          requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
+      };
+    });
   },
 
   clearMessageList: function clearMessageList(listId) {
+    if (DEBUG) debug("Clearing message list: " + listId);
+    delete this.messageLists[listId];
   }
 
 };
 
 const NSGetFactory = XPCOMUtils.generateNSGetFactory([SmsDatabaseService]);
+
+function debug() {
+  dump("SmsDatabaseService: " + Array.slice(arguments).join(" ") + "\n");
+}
--- a/dom/system/b2g/RadioInterfaceLayer.js
+++ b/dom/system/b2g/RadioInterfaceLayer.js
@@ -64,16 +64,20 @@ const DOM_SMS_DELIVERY_SENT             
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsService",
                                    "@mozilla.org/sms/smsservice;1",
                                    "nsISmsService");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gSmsRequestManager",
                                    "@mozilla.org/sms/smsrequestmanager;1",
                                    "nsISmsRequestManager");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gSmsDatabaseService",
+                                   "@mozilla.org/sms/rilsmsdatabaseservice;1",
+                                   "nsISmsDatabaseService");
+
 function convertRILCallState(state) {
   switch (state) {
     case RIL.CALL_STATE_ACTIVE:
       return nsIRadioInterfaceLayer.CALL_STATE_CONNECTED;
     case RIL.CALL_STATE_HOLDING:
       return nsIRadioInterfaceLayer.CALL_STATE_HELD;
     case RIL.CALL_STATE_DIALING:
       return nsIRadioInterfaceLayer.CALL_STATE_DIALING;
@@ -303,34 +307,39 @@ RadioInterfaceLayer.prototype = {
       }
       if (!keepGoing) {
         break;
       }
     }
   },
 
   handleSmsReceived: function handleSmsReceived(message) {
-    //TODO: put the sms into a database, assign it a proper id, yada yada
-    let sms = gSmsService.createSmsMessage(-1,
+    debug("handleSmsReceived: " + JSON.stringify(message));
+    let id = gSmsDatabaseService.saveReceivedMessage(message.sender || null,
+                                                     message.body || null,
+                                                     message.timestamp);
+    let sms = gSmsService.createSmsMessage(id,
                                            DOM_SMS_DELIVERY_RECEIVED,
                                            message.sender || null,
                                            message.receiver || null,
                                            message.body || null,
                                            message.timestamp);
     Services.obs.notifyObservers(sms, kSmsReceivedObserverTopic, null);
   },
 
   handleSmsSent: function handleSmsSent(message) {
-    let sms = gSmsService.createSmsMessage(-1,
+    debug("handleSmsSent: " + JSON.stringify(message));
+    let timestamp = Date.now();
+    let id = gSmsDatabaseService.saveSentMessage(message.number, message.body, timestamp);
+    let sms = gSmsService.createSmsMessage(id,
                                            DOM_SMS_DELIVERY_SENT,
                                            null,
                                            message.number,
                                            message.body,
-                                           Date.now());
-    //TODO At this point we should save the sms into the DB (bug 712809)
+                                           timestamp);
     //TODO handle errors (bug 727319)
     gSmsRequestManager.notifySmsSent(message.requestId, sms);
   },
 
   /**
    * Handle data call state changes.
    */
   handleDataCallState: function handleDataCallState(datacall) {
--- a/dom/system/b2g/ril_consts.js
+++ b/dom/system/b2g/ril_consts.js
@@ -173,16 +173,33 @@ const UNSOLICITED_RESTRICTED_STATE_CHANG
 const UNSOLICITED_ENTER_EMERGENCY_CALLBACK_MODE = 1024;
 const UNSOLICITED_CDMA_CALL_WAITING = 1025;
 const UNSOLICITED_CDMA_OTA_PROVISION_STATUS = 1026;
 const UNSOLICITED_CDMA_INFO_REC = 1027;
 const UNSOLICITED_OEM_HOOK_RAW = 1028;
 const UNSOLICITED_RINGBACK_TONE = 1029;
 const UNSOLICITED_RESEND_INCALL_MUTE = 1030;
 
+const ERROR_SUCCESS = 0;
+const ERROR_RADIO_NOT_AVAILABLE = 1;
+const ERROR_GENERIC_FAILURE = 2;
+const ERROR_PASSWORD_INCORRECT = 3;
+const ERROR_SIM_PIN2 = 4;
+const ERROR_SIM_PUK2 = 5;
+const ERROR_REQUEST_NOT_SUPPORTED = 6;
+const ERROR_CANCELLED = 7;
+const ERROR_OP_NOT_ALLOWED_DURING_VOICE_CALL = 8;
+const ERROR_OP_NOT_ALLOWED_BEFORE_REG_TO_NW = 9;
+const ERROR_SMS_SEND_FAIL_RETRY = 10;
+const ERROR_SIM_ABSENT = 11;
+const ERROR_SUBSCRIPTION_NOT_AVAILABLE = 12;
+const ERROR_MODE_NOT_SUPPORTED = 13;
+const ERROR_FDN_CHECK_FAILURE = 14;
+const ERROR_ILLEGAL_SIM_OR_ME = 15;
+
 const RADIO_STATE_OFF = 0;
 const RADIO_STATE_UNAVAILABLE = 1;
 const RADIO_STATE_SIM_NOT_READY = 2;
 const RADIO_STATE_SIM_LOCKED_OR_ABSENT = 3;
 const RADIO_STATE_SIM_READY = 4;
 const RADIO_STATE_RUIM_NOT_READY = 5;
 const RADIO_STATE_RUIM_READY = 6;
 const RADIO_STATE_RUIM_LOCKED_OR_ABSENT = 7;
@@ -238,16 +255,90 @@ const TOA_UNKNOWN = 0x81;
 
 const CALL_PRESENTATION_ALLOWED = 0;
 const CALL_PRESENTATION_RESTRICTED = 1;
 const CALL_PRESENTATION_UNKNOWN = 2;
 const CALL_PRESENTATION_PAYPHONE = 3;
 
 const SMS_HANDLED = 0;
 
+// ICC commands, see TS 27.007 +CRSM commands
+const ICC_COMMAND_READ_BINARY = 0xb0;
+const ICC_COMMAND_UPDATE_BINARY = 0xd6;
+const ICC_COMMAND_READ_RECORD = 0xb2;
+const ICC_COMMAND_UPDATE_RECORD = 0xdc;
+const ICC_COMMAND_SEEK = 0xa2;
+const ICC_COMMAND_GET_RESPONSE = 0xc0;
+
+// ICC constants, GSM SIM file ids from TS 51.011
+const ICC_EF_ADN = 0x6F3A;
+const ICC_EF_FDN = 0x6F3B;
+const ICC_EF_SDN = 0x6F49;
+const ICC_EF_EXT1 = 0x6F4A;
+const ICC_EF_EXT2 = 0x6F4B;
+const ICC_EF_EXT3 = 0x6F4C;
+const ICC_EF_EXT6 = 0x6fc8;   // Ext record for EF[MBDN]
+const ICC_EF_MWIS = 0x6FCA;
+const ICC_EF_MBDN = 0x6fc7;
+const ICC_EF_PNN = 0x6fc5;
+const ICC_EF_SPN = 0x6F46;
+const ICC_EF_SMS = 0x6F3C;
+const ICC_EF_ICCID = 0x2fe2;
+const ICC_EF_AD = 0x6FAD;
+const ICC_EF_MBI = 0x6fc9;
+const ICC_EF_MSISDN = 0x6f40;
+const ICC_EF_SPDI = 0x6fcd;
+const ICC_EF_SST = 0x6f38;
+const ICC_EF_CFIS = 0x6FCB;
+const ICC_EF_IMG = 0x4f20;
+
+// Types of files  TS 11.11 9.3
+const TYPE_RFU = 0;
+const TYPE_MF  = 1;
+const TYPE_DF  = 2;
+const TYPE_EF  = 4;
+
+const RESPONSE_DATA_FILE_ID_1 = 4;
+const RESPONSE_DATA_FILE_ID_2 = 5;
+const RESPONSE_DATA_FILE_TYPE = 6;
+const RESPONSE_DATA_RFU_3 = 7;
+const RESPONSE_DATA_ACCESS_CONDITION_1 = 8;
+const RESPONSE_DATA_ACCESS_CONDITION_2 = 9;
+const RESPONSE_DATA_ACCESS_CONDITION_3 = 10;
+const RESPONSE_DATA_FILE_STATUS = 11;
+const RESPONSE_DATA_LENGTH = 12;
+const RESPONSE_DATA_STRUCTURE = 13;
+const RESPONSE_DATA_RECORD_LENGTH = 14;
+
+// Types of files  TS 11.11 9.3
+const EF_TYPE_TRANSPARENT = 0;
+const EF_TYPE_LINEAR_FIXED = 1;
+const EF_TYPE_CYCLIC = 3;
+
+// For retriveing MSISDN
+const FOOTER_SIZE_BYTES = 14;
+const MAX_NUMBER_SIZE_BYTES = 11;
+
+// READ_RECORD mode,  TS 102.221
+const READ_RECORD_ABSOLUTE_MODE = 4;
+
+// GET_RESPONSE mandatory response size for EF, see TS 51.011 clause 9, 
+// 'Response data in case of an EF.'
+const GET_RESPONSE_EF_SIZE_BYTES = 15;
+
+// EF path
+const EF_PATH_MF_SIM = "3f00";
+const EF_PATH_DF_TELECOM = "7f10";
+
+// Status code for ICC I/O, 
+// see GSM11.11 and TS 51.011 clause 9.4.
+const STATUS_NORMAL_ENDING = 0x90;
+const STATUS_NORMAL_ENDING_WITH_EXTRA = 0x91;
+const STATUS_WITH_SIM_DATA = 0x9e;
+const STATUS_WITH_RESPONSE_DATA = 0x9f;
 
 /**
  * GSM PDU constants
  */
 
 // PDU TYPE-OF-ADDRESS
 const PDU_TOA_UNKNOWN       = 0x80; // Unknown. This is used when the user or
                                     // network has no a priori information
@@ -331,90 +422,677 @@ const PDU_DCS_MSG_CODING_8BITS_ALPHABET 
 const PDU_DCS_MSG_CODING_16BITS_ALPHABET= 0x08;
 const PDU_DCS_MSG_CLASS_ME_SPECIFIC     = 0xF1;
 const PDU_DCS_MSG_CLASS_SIM_SPECIFIC    = 0xF2;
 const PDU_DCS_MSG_CLASS_TE_SPECIFIC     = 0xF3;
 
 // Because service center timestamp omit the century. Yay.
 const PDU_TIMESTAMP_YEAR_OFFSET = 2000;
 
-// 7bit Default Alphabet
-//TODO: maybe convert this to a string? might be faster/cheaper
-const PDU_ALPHABET_7BIT_DEFAULT = [
-  "@",      // COMMERCIAL AT
-  "\xa3",   // POUND SIGN
-  "$",      // DOLLAR SIGN
-  "\xa5",   // YEN SIGN
-  "\xe8",   // LATIN SMALL LETTER E WITH GRAVE
-  "\xe9",   // LATIN SMALL LETTER E WITH ACUTE
-  "\xf9",   // LATIN SMALL LETTER U WITH GRAVE
-  "\xec",   // LATIN SMALL LETTER I WITH GRAVE
-  "\xf2",   // LATIN SMALL LETTER O WITH GRAVE
-  "\xc7",   // LATIN CAPITAL LETTER C WITH CEDILLA
-  "\n",     // LINE FEED
-  "\xd8",   // LATIN CAPITAL LETTER O WITH STROKE
-  "\xf8",   // LATIN SMALL LETTER O WITH STROKE
-  "\r",     // CARRIAGE RETURN
-  "\xc5",   // LATIN CAPITAL LETTER A WITH RING ABOVE
-  "\xe5",   // LATIN SMALL LETTER A WITH RING ABOVE
-  "\u0394", // GREEK CAPITAL LETTER DELTA
-  "_",      // LOW LINE
-  "\u03a6", // GREEK CAPITAL LETTER PHI
-  "\u0393", // GREEK CAPITAL LETTER GAMMA
-  "\u039b", // GREEK CAPITAL LETTER LAMBDA
-  "\u03a9", // GREEK CAPITAL LETTER OMEGA
-  "\u03a0", // GREEK CAPITAL LETTER PI
-  "\u03a8", // GREEK CAPITAL LETTER PSI
-  "\u03a3", // GREEK CAPITAL LETTER SIGMA
-  "\u0398", // GREEK CAPITAL LETTER THETA
-  "\u039e", // GREEK CAPITAL LETTER XI
-  "\u20ac", // (escape to extension table)
-  "\xc6",   // LATIN CAPITAL LETTER AE
-  "\xe6",   // LATIN SMALL LETTER AE
-  "\xdf",   // LATIN SMALL LETTER SHARP S (German)
-  "\xc9",   // LATIN CAPITAL LETTER E WITH ACUTE
-  " ",      // SPACE
-  "!",      // EXCLAMATION MARK
-  "\"",     // QUOTATION MARK
-  "#",      // NUMBER SIGN
-  "\xa4",   // CURRENCY SIGN
-  "%",      // PERCENT SIGN
-  "&",      // AMPERSAND
-  "'",      // APOSTROPHE
-  "(",      // LEFT PARENTHESIS
-  ")",      // RIGHT PARENTHESIS
-  "*",      // ASTERISK
-  "+",      // PLUS SIGN
-  ",",      // COMMA
-  "-",      // HYPHEN-MINUS
-  ".",      // FULL STOP
-  "/",      // SOLIDUS (SLASH)
-  "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
-  ":",      // COLON
-  ";",      // SEMICOLON
-  "<",      // LESS-THAN SIGN
-  "=",      // EQUALS SIGN
-  ">",      // GREATER-THAN SIGN
-  "?",      // QUESTION MARK
-  "\xa1",   // INVERTED EXCLAMATION MARK
-  "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
-  "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
-  "\xc4",   // LATIN CAPITAL LETTER A WITH DIAERESIS
-  "\xd6",   // LATIN CAPITAL LETTER O WITH DIAERESIS
-  "\xd1",   // LATIN CAPITAL LETTER N WITH TILDE
-  "\xdc",   // LATIN CAPITAL LETTER U WITH DIAERESIS
-  "\xa7",   // SECTION SIGN
-  "\xbf",   // INVERTED QUESTION MARK
-  "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
-  "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
-  "\xe4",   // LATIN SMALL LETTER A WITH DIAERESIS
-  "\xf6",   // LATIN SMALL LETTER O WITH DIAERESIS
-  "\xf1",   // LATIN SMALL LETTER N WITH TILDE
-  "\xfc",   // LATIN SMALL LETTER U WITH DIAERESIS
-  "\xe0"    // LATIN SMALL LETTER A WITH GRAVE
+// See 9.2.3.24 TP‑User Data (TP‑UD)
+const PDU_IEI_CONCATENATED_SHORT_MESSAGES_8BIT         = 0x00;
+const PDU_IEI_SPECIAL_SMS_MESSAGE_INDICATION           = 0x01;
+const PDU_IEI_APPLICATION_PORT_ADDREESING_SCHEME_8BIT  = 0x04;
+const PDU_IEI_APPLICATION_PORT_ADDREESING_SCHEME_16BIT = 0x05;
+const PDU_IEI_SMSC_CONTROL_PARAMS                      = 0x06;
+const PDU_IEI_UDH_SOURCE_INDICATOR                     = 0x07;
+const PDU_IEI_CONCATENATED_SHORT_MESSAGES_16BIT        = 0x08;
+const PDU_IEI_WIRELESS_CONTROL_MESSAGE_PROTOCOL        = 0x09;
+const PDU_IEI_TEXT_FORMATING                           = 0x0A;
+const PDU_IEI_PREDEFINED_SOUND                         = 0x0B;
+const PDU_IEI_USER_DATA_SOUND                          = 0x0C;
+const PDU_IEI_PREDEFINED_ANIMATION                     = 0x0D;
+const PDU_IEI_LARGE_ANIMATION                          = 0x0E;
+const PDU_IEI_SMALL_ANIMATION                          = 0x0F;
+const PDU_IEI_LARGE_PICTURE                            = 0x10;
+const PDU_IEI_SMALL_PICTURE                            = 0x11;
+const PDU_IEI_VARIABLE_PICTURE                         = 0x12;
+const PDU_IEI_USER_PROMPT_INDICATOR                    = 0x13;
+const PDU_IEI_EXTENDED_OBJECT                          = 0x14;
+const PDU_IEI_REUSED_EXTENDED_OBJECT                   = 0x15;
+const PDU_IEI_COMPRESS_CONTROL                         = 0x16;
+const PDU_IEI_OBJECT_DISTRIBUTION_INDICATOR            = 0x17;
+const PDU_IEI_STANDARD_WVG_OBJECT                      = 0x18;
+const PDU_IEI_CHARACTER_SIZE_WVG_OBJECT                = 0x19;
+const PDU_IEI_EXTENDED_OBJECT_DATA_REQUEST_COMMAND     = 0x1A;
+const PDU_IEI_RFC822_EMAIL_HEADER                      = 0x20;
+const PDU_IEI_HYPERLINK_FORMAT_ELEMENT                 = 0x21;
+const PDU_IEI_REPLY_ADDRESS_ELEMENT                    = 0x22;
+const PDU_IEI_ENHANCED_VOICE_MAIL_INFORMATION          = 0x23;
+const PDU_IEI_NATIONAL_LANGUAGE_SINGLE_SHIFT           = 0x24;
+const PDU_IEI_NATIONAL_LANGUAGE_LOCKING_SHIFT          = 0x25;
+
+// 7bit alphabet escape character. The encoded value of this code point is left
+// undefined in official spec. Its code value is internally assigned to \uffff,
+// <noncharacter-FFFF> in Unicode basic multilingual plane.
+const PDU_NL_EXTENDED_ESCAPE = 0x1B;
+
+// <SP>, <LF>, <CR> are only defined in locking shift tables.
+const PDU_NL_SPACE = 0x20;
+const PDU_NL_LINE_FEED = 0x0A;
+const PDU_NL_CARRIAGE_RETURN = 0x0D;
+
+// 7bit alphabet page break character, only defined in single shift tables.
+// The encoded value of this code point is left undefined in official spec, but
+// the code point itself maybe be used for example in compressed CBS messages.
+// Its code value is internally assigned to \u000c, ASCII form feed, or new page.
+const PDU_NL_PAGE_BREAK = 0x0A;
+// 7bit alphabet reserved control character, only defined in single shift
+// tables. The encoded value of this code point is left undefined in official
+// spec. Its code value is internally assigned to \ufffe, <noncharacter-FFFE>
+// in Unicode basic multilingual plane.
+const PDU_NL_RESERVED_CONTROL = 0x0D;
+
+const PDU_NL_IDENTIFIER_DEFAULT    = 0;
+const PDU_NL_IDENTIFIER_TURKISH    = 1;
+const PDU_NL_IDENTIFIER_SPANISH    = 2;
+const PDU_NL_IDENTIFIER_PORTUGUESE = 3;
+const PDU_NL_IDENTIFIER_BENGALI    = 4;
+const PDU_NL_IDENTIFIER_GUJARATI   = 5;
+const PDU_NL_IDENTIFIER_HINDI      = 6;
+const PDU_NL_IDENTIFIER_KANNADA    = 7;
+const PDU_NL_IDENTIFIER_MALAYALAM  = 8;
+const PDU_NL_IDENTIFIER_ORIYA      = 9;
+const PDU_NL_IDENTIFIER_PUNJABI    = 10;
+const PDU_NL_IDENTIFIER_TAMIL      = 11;
+const PDU_NL_IDENTIFIER_TELUGU     = 12;
+const PDU_NL_IDENTIFIER_URDU       = 13;
+
+// National Language Locking Shift Tables, see 3GPP TS 23.038
+const PDU_NL_LOCKING_SHIFT_TABLES = [
+  /**
+   * National Language Identifier: 0x00
+   * 6.2.1 GSM 7 bit Default Alphabet
+   */
+  // 01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "@\u00a3$\u00a5\u00e8\u00e9\u00f9\u00ec\u00f2\u00c7\n\u00d8\u00f8\r\u00c5\u00e5"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0394_\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e\uffff\u00c6\u00e6\u00df\u00c9"
+  // 012.34.....56789ABCDEF
+  + " !\"#\u00a4%&'()*+,-./"
+  // 0123456789ABCDEF
+  + "0123456789:;<=>?"
+  // 0.....123456789ABCDEF
+  + "\u00a1ABCDEFGHIJKLMNO"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "PQRSTUVWXYZ\u00c4\u00d6\u00d1\u00dc\u00a7"
+  // 0.....123456789ABCDEF
+  + "\u00bfabcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
+
+  /**
+   * National Language Identifier: 0x01
+   * A.3.1 Turkish National Language Locking Shift Table
+   */
+  // 01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "@\u00a3$\u00a5\u20ac\u00e9\u00f9\u0131\u00f2\u00c7\n\u011e\u011f\r\u00c5\u00e5"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0394_\u03a6\u0393\u039b\u03a9\u03a0\u03a8\u03a3\u0398\u039e\uffff\u015e\u015f\u00df\u00c9"
+  // 012.34.....56789ABCDEF
+  + " !\"#\u00a4%&'()*+,-./"
+  // 0123456789ABCDEF
+  + "0123456789:;<=>?"
+  // 0.....123456789ABCDEF
+  + "\u0130ABCDEFGHIJKLMNO"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "PQRSTUVWXYZ\u00c4\u00d6\u00d1\u00dc\u00a7"
+  // 0.....123456789ABCDEF
+  + "\u00e7abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u00e4\u00f6\u00f1\u00fc\u00e0",
+
+  /**
+   * National Language Identifier: 0x02
+   * A.3.2 Void
+   */
+  // 0123456789A.BCD.EF
+    "          \n  \r  "
+  // 0123456789AB.....CDEF
+  + "           \uffff    "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x03
+   * A.3.3 Portuguese National Language Locking Shift Table
+   */
+  // 01.....23.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "@\u00a3$\u00a5\u00ea\u00e9\u00fa\u00ed\u00f3\u00e7\n\u00d4\u00f4\r\u00c1\u00e1"
+  // 0.....12.....3.....4.....5.....67.8.....9.....AB.....C.....D.....E.....F.....
+  + "\u0394_\u00aa\u00c7\u00c0\u221e^\\\u20ac\u00d3|\uffff\u00c2\u00e2\u00ca\u00c9"
+  // 012.34.....56789ABCDEF
+  + " !\"#\u00ba%&'()*+,-./"
+  // 0123456789ABCDEF
+  + "0123456789:;<=>?"
+  // 0.....123456789ABCDEF
+  + "\u00cdABCDEFGHIJKLMNO"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "PQRSTUVWXYZ\u00c3\u00d5\u00da\u00dc\u00a7"
+  // 0123456789ABCDEF
+  + "~abcdefghijklmno"
+  // 0123456789AB.....C.....DE.....F.....
+  + "pqrstuvwxyz\u00e3\u00f5`\u00fc\u00e0",
+
+  /**
+   * National Language Identifier: 0x04
+   * A.3.4 Bengali National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.EF.....
+    "\u0981\u0982\u0983\u0985\u0986\u0987\u0988\u0989\u098a\u098b\n\u098c \r \u098f"
+  // 0.....123.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0990  \u0993\u0994\u0995\u0996\u0997\u0998\u0999\u099a\uffff\u099b\u099c\u099d\u099e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u099f\u09a0\u09a1\u09a2\u09a3\u09a4)(\u09a5\u09a6,\u09a7.\u09a8"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u09aa\u09ab?"
+  // 0.....1.....2.....3.....4.....56.....789A.....B.....C.....D.....E.....F.....
+  + "\u09ac\u09ad\u09ae\u09af\u09b0 \u09b2   \u09b6\u09b7\u09b8\u09b9\u09bc\u09bd"
+  // 0.....1.....2.....3.....4.....5.....6.....789.....A.....BCD.....E.....F.....
+  + "\u09be\u09bf\u09c0\u09c1\u09c2\u09c3\u09c4  \u09c7\u09c8  \u09cb\u09cc\u09cd"
+  // 0.....123456789ABCDEF
+  + "\u09ceabcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u09d7\u09dc\u09dd\u09f0\u09f1",
+
+  /**
+   * National Language Identifier: 0x05
+   * A.3.5 Gujarati National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.EF.....
+    "\u0a81\u0a82\u0a83\u0a85\u0a86\u0a87\u0a88\u0a89\u0a8a\u0a8b\n\u0a8c\u0a8d\r \u0a8f"
+  // 0.....1.....23.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0a90\u0a91 \u0a93\u0a94\u0a95\u0a96\u0a97\u0a98\u0a99\u0a9a\uffff\u0a9b\u0a9c\u0a9d\u0a9e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0a9f\u0aa0\u0aa1\u0aa2\u0aa3\u0aa4)(\u0aa5\u0aa6,\u0aa7.\u0aa8"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0aaa\u0aab?"
+  // 0.....1.....2.....3.....4.....56.....7.....89.....A.....B.....C.....D.....E.....F.....
+  + "\u0aac\u0aad\u0aae\u0aaf\u0ab0 \u0ab2\u0ab3 \u0ab5\u0ab6\u0ab7\u0ab8\u0ab9\u0abc\u0abd"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....CD.....E.....F.....
+  + "\u0abe\u0abf\u0ac0\u0ac1\u0ac2\u0ac3\u0ac4\u0ac5 \u0ac7\u0ac8\u0ac9 \u0acb\u0acc\u0acd"
+  // 0.....123456789ABCDEF
+  + "\u0ad0abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0ae0\u0ae1\u0ae2\u0ae3\u0af1",
+
+  /**
+   * National Language Identifier: 0x06
+   * A.3.6 Hindi National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "\u0901\u0902\u0903\u0905\u0906\u0907\u0908\u0909\u090a\u090b\n\u090c\u090d\r\u090e\u090f"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0910\u0911\u0912\u0913\u0914\u0915\u0916\u0917\u0918\u0919\u091a\uffff\u091b\u091c\u091d\u091e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u091f\u0920\u0921\u0922\u0923\u0924)(\u0925\u0926,\u0927.\u0928"
+  // 0123456789ABC.....D.....E.....F
+  + "0123456789:;\u0929\u092a\u092b?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u092c\u092d\u092e\u092f\u0930\u0931\u0932\u0933\u0934\u0935\u0936\u0937\u0938\u0939\u093c\u093d"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u093e\u093f\u0940\u0941\u0942\u0943\u0944\u0945\u0946\u0947\u0948\u0949\u094a\u094b\u094c\u094d"
+  // 0.....123456789ABCDEF
+  + "\u0950abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0972\u097b\u097c\u097e\u097f",
+
+  /**
+   * National Language Identifier: 0x07
+   * A.3.7 Kannada National Language Locking Shift Table
+   */
+  // 01.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....
+    " \u0c82\u0c83\u0c85\u0c86\u0c87\u0c88\u0c89\u0c8a\u0c8b\n\u0c8c \r\u0c8e\u0c8f"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0c90 \u0c92\u0c93\u0c94\u0c95\u0c96\u0c97\u0c98\u0c99\u0c9a\uffff\u0c9b\u0c9c\u0c9d\u0c9e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0c9f\u0ca0\u0ca1\u0ca2\u0ca3\u0ca4)(\u0ca5\u0ca6,\u0ca7.\u0ca8"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0caa\u0cab?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....C.....D.....E.....F.....
+  + "\u0cac\u0cad\u0cae\u0caf\u0cb0\u0cb1\u0cb2\u0cb3 \u0cb5\u0cb6\u0cb7\u0cb8\u0cb9\u0cbc\u0cbd"
+  // 0.....1.....2.....3.....4.....5.....6.....78.....9.....A.....BC.....D.....E.....F.....
+  + "\u0cbe\u0cbf\u0cc0\u0cc1\u0cc2\u0cc3\u0cc4 \u0cc6\u0cc7\u0cc8 \u0cca\u0ccb\u0ccc\u0ccd"
+  // 0.....123456789ABCDEF
+  + "\u0cd5abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0cd6\u0ce0\u0ce1\u0ce2\u0ce3",
+
+  /**
+   * National Language Identifier: 0x08
+   * A.3.8 Malayalam National Language Locking Shift Table
+   */
+  // 01.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....
+    " \u0d02\u0d03\u0d05\u0d06\u0d07\u0d08\u0d09\u0d0a\u0d0b\n\u0d0c \r\u0d0e\u0d0f"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0d10 \u0d12\u0d13\u0d14\u0d15\u0d16\u0d17\u0d18\u0d19\u0d1a\uffff\u0d1b\u0d1c\u0d1d\u0d1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0d1f\u0d20\u0d21\u0d22\u0d23\u0d24)(\u0d25\u0d26,\u0d27.\u0d28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0d2a\u0d2b?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....EF.....
+  + "\u0d2c\u0d2d\u0d2e\u0d2f\u0d30\u0d31\u0d32\u0d33\u0d34\u0d35\u0d36\u0d37\u0d38\u0d39 \u0d3d"
+  // 0.....1.....2.....3.....4.....5.....6.....78.....9.....A.....BC.....D.....E.....F.....
+  + "\u0d3e\u0d3f\u0d40\u0d41\u0d42\u0d43\u0d44 \u0d46\u0d47\u0d48 \u0d4a\u0d4b\u0d4c\u0d4d"
+  // 0.....123456789ABCDEF
+  + "\u0d57abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0d60\u0d61\u0d62\u0d63\u0d79",
+
+  /**
+   * National Language Identifier: 0x09
+   * A.3.9 Oriya National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.EF.....
+    "\u0b01\u0b02\u0b03\u0b05\u0b06\u0b07\u0b08\u0b09\u0b0a\u0b0b\n\u0b0c \r \u0b0f"
+  // 0.....123.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0b10  \u0b13\u0b14\u0b15\u0b16\u0b17\u0b18\u0b19\u0b1a\uffff\u0b1b\u0b1c\u0b1d\u0b1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0b1f\u0b20\u0b21\u0b22\u0b23\u0b24)(\u0b25\u0b26,\u0b27.\u0b28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0b2a\u0b2b?"
+  // 0.....1.....2.....3.....4.....56.....7.....89.....A.....B.....C.....D.....E.....F.....
+  + "\u0b2c\u0b2d\u0b2e\u0b2f\u0b30 \u0b32\u0b33 \u0b35\u0b36\u0b37\u0b38\u0b39\u0b3c\u0b3d"
+  // 0.....1.....2.....3.....4.....5.....6.....789.....A.....BCD.....E.....F.....
+  + "\u0b3e\u0b3f\u0b40\u0b41\u0b42\u0b43\u0b44  \u0b47\u0b48  \u0b4b\u0b4c\u0b4d"
+  // 0.....123456789ABCDEF
+  + "\u0b56abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0b57\u0b60\u0b61\u0b62\u0b63",
+
+  /**
+   * National Language Identifier: 0x0A
+   * A.3.10 Punjabi National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9A.BCD.EF.....
+    "\u0a01\u0a02\u0a03\u0a05\u0a06\u0a07\u0a08\u0a09\u0a0a \n  \r \u0a0f"
+  // 0.....123.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0a10  \u0a13\u0a14\u0a15\u0a16\u0a17\u0a18\u0a19\u0a1a\uffff\u0a1b\u0a1c\u0a1d\u0a1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0a1f\u0a20\u0a21\u0a22\u0a23\u0a24)(\u0a25\u0a26,\u0a27.\u0a28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0a2a\u0a2b?"
+  // 0.....1.....2.....3.....4.....56.....7.....89.....A.....BC.....D.....E.....F
+  + "\u0a2c\u0a2d\u0a2e\u0a2f\u0a30 \u0a32\u0a33 \u0a35\u0a36 \u0a38\u0a39\u0a3c "
+  // 0.....1.....2.....3.....4.....56789.....A.....BCD.....E.....F.....
+  + "\u0a3e\u0a3f\u0a40\u0a41\u0a42    \u0a47\u0a48  \u0a4b\u0a4c\u0a4d"
+  // 0.....123456789ABCDEF
+  + "\u0a51abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0a70\u0a71\u0a72\u0a73\u0a74",
+
+  /**
+   * National Language Identifier: 0x0B
+   * A.3.11 Tamil National Language Locking Shift Table
+   */
+  // 01.....2.....3.....4.....5.....6.....7.....8.....9A.BCD.E.....F.....
+    " \u0b82\u0b83\u0b85\u0b86\u0b87\u0b88\u0b89\u0b8a \n  \r\u0b8e\u0b8f"
+  // 0.....12.....3.....4.....5.....6789.....A.....B.....CD.....EF.....
+  + "\u0b90 \u0b92\u0b93\u0b94\u0b95   \u0b99\u0b9a\uffff \u0b9c \u0b9e"
+  // 012.....3456.....7.....89ABCDEF.....
+  + " !\u0b9f   \u0ba3\u0ba4)(  , .\u0ba8"
+  // 0123456789ABC.....D.....EF
+  + "0123456789:;\u0ba9\u0baa ?"
+  // 012.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....EF
+  + "  \u0bae\u0baf\u0bb0\u0bb1\u0bb2\u0bb3\u0bb4\u0bb5\u0bb6\u0bb7\u0bb8\u0bb9  "
+  // 0.....1.....2.....3.....4.....5678.....9.....A.....BC.....D.....E.....F.....
+  + "\u0bbe\u0bbf\u0bc0\u0bc1\u0bc2   \u0bc6\u0bc7\u0bc8 \u0bca\u0bcb\u0bcc\u0bcd"
+  // 0.....123456789ABCDEF
+  + "\u0bd0abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0bd7\u0bf0\u0bf1\u0bf2\u0bf9",
+
+  /**
+   * National Language Identifier: 0x0C
+   * A.3.12 Telugu National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....CD.E.....F.....
+    "\u0c01\u0c02\u0c03\u0c05\u0c06\u0c07\u0c08\u0c09\u0c0a\u0c0b\n\u0c0c \r\u0c0e\u0c0f"
+  // 0.....12.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0c10 \u0c12\u0c13\u0c14\u0c15\u0c16\u0c17\u0c18\u0c19\u0c1a\uffff\u0c1b\u0c1c\u0c1d\u0c1e"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u0c1f\u0c20\u0c21\u0c22\u0c23\u0c24)(\u0c25\u0c26,\u0c27.\u0c28"
+  // 0123456789ABCD.....E.....F
+  + "0123456789:; \u0c2a\u0c2b?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89.....A.....B.....C.....D.....EF.....
+  + "\u0c2c\u0c2d\u0c2e\u0c2f\u0c30\u0c31\u0c32\u0c33 \u0c35\u0c36\u0c37\u0c38\u0c39 \u0c3d"
+  // 0.....1.....2.....3.....4.....5.....6.....78.....9.....A.....BC.....D.....E.....F.....
+  + "\u0c3e\u0c3f\u0c40\u0c41\u0c42\u0c43\u0c44 \u0c46\u0c47\u0c48 \u0c4a\u0c4b\u0c4c\u0c4d"
+  // 0.....123456789ABCDEF
+  + "\u0c55abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0c56\u0c60\u0c61\u0c62\u0c63",
+
+  /**
+   * National Language Identifier: 0x0D
+   * A.3.13 Urdu National Language Locking Shift Table
+   */
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.B.....C.....D.E.....F.....
+    "\u0627\u0622\u0628\u067b\u0680\u067e\u06a6\u062a\u06c2\u067f\n\u0679\u067d\r\u067a\u067c"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u062b\u062c\u0681\u0684\u0683\u0685\u0686\u0687\u062d\u062e\u062f\uffff\u068c\u0688\u0689\u068a"
+  // 012.....3.....4.....5.....6.....7.....89A.....B.....CD.....EF.....
+  + " !\u068f\u068d\u0630\u0631\u0691\u0693)(\u0699\u0632,\u0696.\u0698"
+  // 0123456789ABC.....D.....E.....F
+  + "0123456789:;\u069a\u0633\u0634?"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u0635\u0636\u0637\u0638\u0639\u0641\u0642\u06a9\u06aa\u06ab\u06af\u06b3\u06b1\u0644\u0645\u0646"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....C.....D.....E.....F.....
+  + "\u06ba\u06bb\u06bc\u0648\u06c4\u06d5\u06c1\u06be\u0621\u06cc\u06d0\u06d2\u064d\u0650\u064f\u0657"
+  // 0.....123456789ABCDEF
+  + "\u0654abcdefghijklmno"
+  // 0123456789AB.....C.....D.....E.....F.....
+  + "pqrstuvwxyz\u0655\u0651\u0653\u0656\u0670"
+];
+
+// National Language Single Shift Tables, see 3GPP TS 23.038
+const PDU_NL_SINGLE_SHIFT_TABLES = [
+  /**
+   * National Language Identifier: 0x00
+   * 6.2.1.1 GSM 7 bit default alphabet extension table
+   */
+  // 0123456789A.....BCD.....EF
+    "          \u000c  \ufffe  "
+  // 0123456789AB.....CDEF
+  + "    ^      \uffff    "
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|               "
+  // 0123456789ABCDEF
+  + "                "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x01
+   * A.2.1 Turkish National Language Single Shift Table
+   */
+  // 0123456789A.....BCD.....EF
+    "          \u000c  \ufffe  "
+  // 0123456789AB.....CDEF
+  + "    ^      \uffff    "
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 01234567.....89.....ABCDEF
+  + "|      \u011e \u0130      "
+  // 0123.....456789ABCDEF
+  + "   \u015e            "
+  // 0123.....45.....67.....89.....ABCDEF
+  + "   \u00e7 \u20ac \u011f \u0131      "
+  // 0123.....456789ABCDEF
+  + "   \u015f            ",
+
+  /**
+   * National Language Identifier: 0x02
+   * A.2.2 Spanish National Language Single Shift Table
+   */
+  // 0123456789.....A.....BCD.....EF
+    "         \u00e7\u000c  \ufffe  "
+  // 0123456789AB.....CDEF
+  + "    ^      \uffff    "
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 01.....23456789.....ABCDEF.....
+  + "|\u00c1       \u00cd     \u00d3"
+  // 012345.....6789ABCDEF
+  + "     \u00da          "
+  // 01.....2345.....6789.....ABCDEF.....
+  + " \u00e1   \u20ac   \u00ed     \u00f3"
+  // 012345.....6789ABCDEF
+  + "     \u00fa          ",
+
+  /**
+   * National Language Identifier: 0x03
+   * A.2.3 Portuguese National Language Single Shift Table
+   */
+  // 012345.....6789.....A.....B.....C.....D.....E.....F.....
+    "     \u00ea   \u00e7\u000c\u00d4\u00f4\ufffe\u00c1\u00e1"
+  // 012.....3.....45.....6.....7.....8.....9.....AB.....CDEF.....
+  + "  \u03a6\u0393^\u03a9\u03a0\u03a8\u03a3\u0398 \uffff   \u00ca"
+  // 0123456789ABCDEF.
+  + "        {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 01.....23456789.....ABCDEF.....
+  + "|\u00c0       \u00cd     \u00d3"
+  // 012345.....6789AB.....C.....DEF
+  + "     \u00da     \u00c3\u00d5   "
+  // 01.....2345.....6789.....ABCDEF.....
+  + " \u00c2   \u20ac   \u00ed     \u00f3"
+  // 012345.....6789AB.....C.....DEF.....
+  + "     \u00fa     \u00e3\u00f5  \u00e2",
+
+  /**
+   * National Language Identifier: 0x04
+   * A.2.4 Bengali National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u09e6\u09e7\uffff\u09e8\u09e9\u09ea\u09eb"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u09ec\u09ed\u09ee\u09ef\u09df\u09e0\u09e1\u09e2{}\u09e3\u09f2\u09f3\u09f4\u09f5\\"
+  // 0.....1.....2.....3.....4.....56789ABCDEF
+  + "\u09f6\u09f7\u09f8\u09f9\u09fa       [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x05
+   * A.2.5 Gujarati National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0ae6\u0ae7\u0ae8\u0ae9"
+  // 0.....1.....2.....3.....4.....5.....6789ABCDEF.
+  + "\u0aea\u0aeb\u0aec\u0aed\u0aee\u0aef  {}     \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x06
+   * A.2.6 Hindi National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0966\u0967\u0968\u0969"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u096a\u096b\u096c\u096d\u096e\u096f\u0951\u0952{}\u0953\u0954\u0958\u0959\u095a\\"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....BCDEF
+  + "\u095b\u095c\u095d\u095e\u095f\u0960\u0961\u0962\u0963\u0970\u0971 [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x07
+   * A.2.7 Kannada National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0ce6\u0ce7\u0ce8\u0ce9"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....BCDEF.
+  + "\u0cea\u0ceb\u0cec\u0ced\u0cee\u0cef\u0cde\u0cf1{}\u0cf2    \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x08
+   * A.2.8 Malayalam National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0d66\u0d67\u0d68\u0d69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u0d6a\u0d6b\u0d6c\u0d6d\u0d6e\u0d6f\u0d70\u0d71{}\u0d72\u0d73\u0d74\u0d75\u0d7a\\"
+  // 0.....1.....2.....3.....4.....56789ABCDEF
+  + "\u0d7b\u0d7c\u0d7d\u0d7e\u0d7f       [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x09
+   * A.2.9 Oriya National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0b66\u0b67\u0b68\u0b69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....DEF.
+  + "\u0b6a\u0b6b\u0b6c\u0b6d\u0b6e\u0b6f\u0b5c\u0b5d{}\u0b5f\u0b70\u0b71  \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0A
+   * A.2.10 Punjabi National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0a66\u0a67\u0a68\u0a69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....EF.
+  + "\u0a6a\u0a6b\u0a6c\u0a6d\u0a6e\u0a6f\u0a59\u0a5a{}\u0a5b\u0a5c\u0a5e\u0a75 \\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0B
+   * A.2.11 Tamil National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0964\u0965\uffff\u0be6\u0be7\u0be8\u0be9"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u0bea\u0beb\u0bec\u0bed\u0bee\u0bef\u0bf3\u0bf4{}\u0bf5\u0bf6\u0bf7\u0bf8\u0bfa\\"
+  // 0123456789ABCDEF
+  + "            [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0C
+   * A.2.12 Telugu National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789AB.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*  \uffff\u0c66\u0c67\u0c68\u0c69"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u0c6a\u0c6b\u0c6c\u0c6d\u0c6e\u0c6f\u0c58\u0c59{}\u0c78\u0c79\u0c7a\u0c7b\u0c7c\\"
+  // 0.....1.....2.....3456789ABCDEF
+  + "\u0c7d\u0c7e\u0c7f         [~] "
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                ",
+
+  /**
+   * National Language Identifier: 0x0D
+   * A.2.13 Urdu National Language Single Shift Table
+   */
+  // 01.....23.....4.....5.6.....789A.....BCD.....EF
+    "@\u00a3$\u00a5\u00bf\"\u00a4%&'\u000c*+\ufffe-/"
+  // 0123.....45.....6789.....A.....B.....C.....D.....E.....F.....
+  + "<=>\u00a1^\u00a1_#*\u0600\u0601\uffff\u06f0\u06f1\u06f2\u06f3"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....89A.....B.....C.....D.....E.....F.
+  + "\u06f4\u06f5\u06f6\u06f7\u06f8\u06f9\u060c\u060d{}\u060e\u060f\u0610\u0611\u0612\\"
+  // 0.....1.....2.....3.....4.....5.....6.....7.....8.....9.....A.....B.....CDEF.....
+  + "\u0613\u0614\u061b\u061f\u0640\u0652\u0658\u066b\u066c\u0672\u0673\u06cd[~]\u06d4"
+  // 0123456789ABCDEF
+  + "|ABCDEFGHIJKLMNO"
+  // 0123456789ABCDEF
+  + "PQRSTUVWXYZ     "
+  // 012345.....6789ABCDEF
+  + "     \u20ac          "
+  // 0123456789ABCDEF
+  + "                "
 ];
 
 const DATACALL_RADIOTECHNOLOGY_CDMA = 0;
 const DATACALL_RADIOTECHNOLOGY_GSM = 1;
 
 const DATACALL_AUTH_NONE = 0;
 const DATACALL_AUTH_PAP = 1;
 const DATACALL_AUTH_CHAP = 2;
--- a/dom/system/b2g/ril_worker.js
+++ b/dom/system/b2g/ril_worker.js
@@ -458,22 +458,24 @@ let Buf = {
 
     let request_type, options;
     if (response_type == RESPONSE_TYPE_SOLICITED) {
       let token = this.readUint32();
       let error = this.readUint32();
 
       options = this.tokenRequestMap[token];
       request_type = options.rilRequestType;
-      if (error) {
-        //TODO
+
+      options.rilRequestError = error;
+      if (error) {   	  
         if (DEBUG) {
           debug("Received error " + error + " for solicited parcel type " +
                 request_type);
         }
+        RIL.handleRequestError(options);
         return;
       }
       if (DEBUG) {
         debug("Solicited response for request type " + request_type +
               ", token " + token);
       }
       delete this.tokenRequestMap[token];
       this.lastSolicitedToken = token;
@@ -504,16 +506,17 @@ let Buf = {
     this.writeUint32(type);
     let token = this.token;
     this.writeUint32(token);
 
     if (!options) {
       options = {};
     }
     options.rilRequestType = type;
+    options.rilRequestError = null;
     this.tokenRequestMap[token] = options;
     this.token++;
     return token;
   },
 
   /**
    * Communicate with the RIL IPC thread.
    */
@@ -785,31 +788,36 @@ let RIL = {
    *        String containing the SMSC PDU in hex format.
    * @param number
    *        String containing the recipients address.
    * @param body
    *        String containing the message body.
    * @param dcs
    *        Data coding scheme. One of the PDU_DCS_MSG_CODING_*BITS_ALPHABET
    *        constants.
-   * @param bodyLengthInOctets
-   *        Byte length of the message body when encoded with the given DCS.
+   * @param userDataHeaderLength
+   *        Length of embedded user data header, in bytes. The whole header
+   *        size will be userDataHeaderLength + 1; 0 for no header.
+   * @param encodedBodyLength
+   *        Length of the message body when encoded with the given DCS. For
+   *        UCS2, in bytes; for 7-bit, in septets.
+   * @param langIndex
+   *        Table index used for normal 7-bit encoded character lookup.
+   * @param langShiftIndex
+   *        Table index used for escaped 7-bit encoded character lookup.
    */
   sendSMS: function sendSMS(options) {
     let token = Buf.newParcel(REQUEST_SEND_SMS, options);
     //TODO we want to map token to the input values so that on the
     // response from the RIL device we know which SMS request was successful
     // or not. Maybe we should build that functionality into newParcel() and
     // handle it within tokenRequestMap[].
     Buf.writeUint32(2);
     Buf.writeString(options.SMSC);
-    GsmPDUHelper.writeMessage(options.number,
-                              options.body,
-                              options.dcs,
-                              options.bodyLengthInOctets);
+    GsmPDUHelper.writeMessage(options);
     Buf.sendParcel();
   },
 
   /**
    * Acknowledge the receipt and handling of an SMS.
    *
    * @param success
    *        Boolean indicating whether the message was successfuly handled.
@@ -899,16 +907,51 @@ let RIL = {
     Buf.writeString(user);
     Buf.writeString(passwd);
     Buf.writeString(chappap.toString());
     Buf.writeString(pdptype);
     Buf.sendParcel();
     return token;
   },
 
+   /**
+   *  Request an ICC I/O operation.
+   * 
+   *  See TS 27.007 "restricted SIM" operation, "AT Command +CRSM".
+   *  The sequence is in the same order as how libril reads this parcel,
+   *  see the struct RIL_SIM_IO_v5 or RIL_SIM_IO_v6 defined in ril.h
+   *
+   *  @param command 
+   *         The I/O command, one of the ICC_COMMAND_* constants.
+   *  @param fileid
+   *         The file to operate on, one of the ICC_EF_* constants.
+   *  @param pathid
+   *         String type, check pathid from TS 27.007 +CRSM  
+   *  @param p1, p2, p3
+   *         Arbitrary integer parameters for the command.
+   *  @param data
+   *         String parameter for the command.
+   *  @param pin2 [optional]
+   *         String containing the PIN2.
+   */
+  iccIO: function iccIO (options) {
+    let token = Buf.newParcel(REQUEST_SIM_IO, options);
+    Buf.writeUint32(options.command);
+    Buf.writeUint32(options.fileid);
+    Buf.writeString(options.path);
+    Buf.writeUint32(options.p1);
+    Buf.writeUint32(options.p2);
+    Buf.writeUint32(options.p3);
+    Buf.writeString(options.data);
+    if (request.pin2 != null) {
+      Buf.writeString(pin2);
+    }
+    Buf.sendParcel();
+  },
+  
   /**
    * Deactivate a data call.
    *
    * @param cid
    *        String containing CID.
    * @param reason
    *        One of DATACALL_DEACTIVATE_* constants.
    */
@@ -929,16 +972,24 @@ let RIL = {
   },
 
   /**
    * Get failure casue code for the most recently failed PDP context.
    */
   getFailCauseCode: function getFailCauseCode() {
     Buf.simpleRequest(REQUEST_LAST_CALL_FAIL_CAUSE);
   },
+  
+  /**
+   * Handle the RIL request errors
+   */ 
+  handleRequestError: function handleRequestError(options) {	  
+	options.type = "error";
+	Phone.sendDOMMessage(options);
+  },   
 
   /**
    * Handle incoming requests from the RIL. We find the method that
    * corresponds to the request type. Incidentally, the request type
    * _is_ the method name, so that's easy.
    */
 
   handleParcel: function handleParcel(request_type, length, options) {
@@ -1104,17 +1155,19 @@ RIL[REQUEST_SEND_SMS] = function REQUEST
   options.errorCode = Buf.readUint32();
   Phone.onSendSMS(options);
 };
 RIL[REQUEST_SEND_SMS_EXPECT_MORE] = null;
 RIL[REQUEST_SETUP_DATA_CALL] = function REQUEST_SETUP_DATA_CALL() {
   let [cid, ifname, ipaddr, dns, gw] = Buf.readStringList();
   Phone.onSetupDataCall(Buf.lastSolicitedToken, cid, ifname, ipaddr, dns, gw);
 };
-RIL[REQUEST_SIM_IO] = null;
+RIL[REQUEST_SIM_IO] = function REQUEST_SIM_IO(length, options) {
+  Phone.onICCIO(options);
+};
 RIL[REQUEST_SEND_USSD] = null;
 RIL[REQUEST_CANCEL_USSD] = null;
 RIL[REQUEST_GET_CLIR] = null;
 RIL[REQUEST_SET_CLIR] = null;
 RIL[REQUEST_QUERY_CALL_FORWARD_STATUS] = null;
 RIL[REQUEST_SET_CALL_FORWARD] = null;
 RIL[REQUEST_QUERY_CALL_WAITING] = null;
 RIL[REQUEST_SET_CALL_WAITING] = null;
@@ -1317,16 +1370,17 @@ let Phone = {
 
   /**
    * Strings
    */
   IMEI: null,
   IMEISV: null,
   IMSI: null,
   SMSC: null,
+  MSISDN: null,
 
   registrationState: {},
   gprsRegistrationState: {},
 
   /**
    * List of strings identifying the network operator.
    */
   operator: null,
@@ -1463,16 +1517,17 @@ let Phone = {
     if (newState == RADIO_STATE_SIM_READY  ||
         newState == RADIO_STATE_RUIM_READY ||
         newState == RADIO_STATE_NV_READY) {
       // The ICC has become available. Get all the things.
       RIL.getICCStatus();
       this.requestNetworkInfo();
       RIL.getSignalStrength();
       RIL.getSMSCAddress();
+      this.getMSISDN();
       this.sendDOMMessage({type: "cardstatechange",
                            cardState: GECKO_CARDSTATE_READY});
     }
     if (newState == RADIO_STATE_SIM_LOCKED_OR_ABSENT  ||
         newState == RADIO_STATE_RUIM_LOCKED_OR_ABSENT) {
       RIL.getICCStatus();
       this.sendDOMMessage({type: "cardstatechange",
                            cardState: GECKO_CARDSTATE_UNAVAILABLE});
@@ -1666,16 +1721,62 @@ let Phone = {
   onIMEI: function onIMEI(imei) {
     this.IMEI = imei;
   },
 
   onIMEISV: function onIMEISV(imeiSV) {
     this.IMEISV = imeiSV;
   },
 
+  onICCIO: function onICCIO(options) {
+    switch (options.fileid) {
+      case ICC_EF_MSISDN:
+        this.readMSISDNResponse(options);
+        break;
+    }
+  },
+  
+  readMSISDNResponse: function readMSISDNResponse(options) {
+    let sw1 = Buf.readUint32();
+    let sw2 = Buf.readUint32();
+    // See GSM11.11 section 9.4 for sw1 and sw2
+    if (sw1 != STATUS_NORMAL_ENDING) {
+      // TODO: error 
+      // Wait for fix for Bug 713451 to report error.
+      debug("Error in iccIO");
+    }
+    if (DEBUG) debug("ICC I/O (" + sw1 + "/" + sw2 + ")");
+
+    switch (options.command) {
+      case ICC_COMMAND_GET_RESPONSE:
+        let response = Buf.readString();
+        let recordSize = parseInt(
+            response.substr(RESPONSE_DATA_RECORD_LENGTH * 2, 2), 16) & 0xff;
+        let request = {
+          command: ICC_COMMAND_READ_RECORD,
+          fileid:  ICC_EF_MSISDN,
+          pathid:  EF_PATH_MF_SIM + EF_PATH_DF_TELECOM,
+          p1:      1, // Record number, MSISDN is always in the 1st record
+          p2:      READ_RECORD_ABSOLUTE_MODE,
+          p3:      recordSize,
+          data:    null,
+          pin2:    null,
+        };
+        RIL.iccIO(request);
+        break;
+
+      case ICC_COMMAND_READ_RECORD:
+        // Ignore 2 bytes prefix, which is 4 chars
+        let number = GsmPDUHelper.readStringAsBCD().toString().substr(4); 
+        if (DEBUG) debug("MSISDN: " + number);
+        this.MSISDN = number;
+        break;
+    } 
+  },
+
   onRegistrationState: function onRegistrationState(state) {
     let rs = this.registrationState;
     let stateChanged = false;
 
     let regState = RIL.parseInt(state[0], NETWORK_CREG_STATE_UNKNOWN);
     if (rs.regState != regState) {
       rs.regState = regState;
       stateChanged = true;
@@ -2122,16 +2223,33 @@ let Phone = {
   /**
    * Get failure cause code for the last failed PDP context.
    */
   getFailCauseCode: function getFailCauseCode(options) {
     RIL.getFailCauseCode();
   },
 
   /**
+   *  Get MSISDN
+   */ 
+  getMSISDN: function getMSISDN() {
+    let request = {
+      command: ICC_COMMAND_GET_RESPONSE,
+      fileid:  ICC_EF_MSISDN,
+      pathid:  EF_PATH_MF_SIM + EF_PATH_DF_TELECOM,
+      p1:      0, // For GET_RESPONSE, p1 = 0
+      p2:      0, // For GET_RESPONSE, p2 = 0
+      p3:      GET_RESPONSE_EF_SIZE_BYTES,
+      data:    null,
+      pin2:    null,
+    };
+    RIL.iccIO(request);
+  },
+
+  /**
    * Handle incoming messages from the main UI thread.
    *
    * @param message
    *        Object containing the message. Messages are supposed
    */
   handleDOMMessage: function handleMessage(message) {
     if (DEBUG) debug("Received DOM message " + JSON.stringify(message));
     let method = this[message.type];
@@ -2160,16 +2278,23 @@ let Phone = {
  * A PDU is a string containing a series of hexadecimally encoded octets
  * or nibble-swapped binary-coded decimals (BCDs). It contains not only the
  * message text but information about the sender, the SMS service center,
  * timestamp, etc.
  */
 let GsmPDUHelper = {
 
   /**
+   * List of tuples of national language identifier pairs.
+   */
+  enabledGsmTableTuples: [
+    [PDU_NL_IDENTIFIER_DEFAULT, PDU_NL_IDENTIFIER_DEFAULT],
+  ],
+
+  /**
    * Read one character (2 bytes) from a RIL string and decode as hex.
    *
    * @return the nibble as a number.
    */
   readHexNibble: function readHexNibble() {
     let nibble = Buf.readUint16();
     if (nibble >= 48 && nibble <= 57) {
       nibble -= 48; // ASCII '0'..'9'
@@ -2242,30 +2367,47 @@ let GsmPDUHelper = {
    *        Number of nibble *pairs* to read.
    *
    * @return the decimal as a number.
    */
   readSwappedNibbleBCD: function readSwappedNibbleBCD(length) {
     let number = 0;
     for (let i = 0; i < length; i++) {
       let octet = this.readHexOctet();
+      if (octet == 0xff)
+        continue;
       // If the first nibble is an "F" , only the second nibble is to be taken
       // into account.
       if ((octet & 0xf0) == 0xf0) {
         number *= 10;
         number += octet & 0x0f;
         continue;
       }
       number *= 100;
       number += this.octetToBCD(octet);
     }
     return number;
   },
 
   /**
+   *  Read a string from Buf and convert it to BCD
+   * 
+   *  @return the decimal as a number.
+   */ 
+  readStringAsBCD: function readStringAsBCD() {
+    let length = Buf.readUint32();
+    let bcd = this.readSwappedNibbleBCD(length / 2);
+    let delimiter = Buf.readUint16();
+    if (!(length & 1)) {
+      delimiter |= Buf.readUint16();
+    }
+    return bcd;
+  },
+
+  /**
    * Write numerical data as swapped nibble BCD.
    *
    * @param data
    *        Data to write (as a string or a number)
    */
   writeSwappedNibbleBCD: function writeSwappedNibbleBCD(data) {
     data = data.toString();