Merge mozilla-central and mozilla-inbound
authorMatt Brubeck <mbrubeck@mozilla.com>
Fri, 23 Mar 2012 16:06:54 -0700
changeset 90225 78d8cc585047711a70adddbe8a16007937ba9a34
parent 90127 6470fe2fc4de48077605605aa052448c6586b7a6 (current diff)
parent 90224 8794d663b5f451831c8c60f2c1e737434c62c1fa (diff)
child 90226 dc11394d4693e3e10dcc4dcb67e1b29d7f0cb1c5
push id22323
push userbmo@edmorley.co.uk
push dateSat, 24 Mar 2012 16:06:07 +0000
treeherdermozilla-central@20a01901480f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone14.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 mozilla-central and mozilla-inbound
content/base/src/nsObjectLoadingContent.cpp
mobile/android/base/resources/menu-v11/gecko_menu.xml
mobile/android/base/resources/menu/gecko_menu.xml
mobile/android/chrome/content/browser.js
toolkit/components/places/nsPlacesImportExportService.cpp
toolkit/components/places/nsPlacesImportExportService.h
xulrunner/installer/mac/Description.plist.in
xulrunner/installer/mac/Info.plist.in
xulrunner/installer/mac/Makefile.in
--- a/Makefile.in
+++ b/Makefile.in
@@ -67,16 +67,21 @@ tier_base_dirs = \
 
 ifndef LIBXUL_SDK
 ifeq (android,$(MOZ_WIDGET_TOOLKIT))
 tier_base_dirs += \
   other-licenses/android \
   other-licenses/skia-npapi \
   $(NULL)
 endif
+ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
+tier_base_dirs += \
+  other-licenses/android \
+  $(NULL)
+endif
 
 ifdef MOZ_MEMORY
 tier_base_dirs += memory/jemalloc
 endif
 tier_base_dirs += \
   mozglue \
   memory/mozalloc \
   $(NULL)
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -645,17 +645,17 @@ protected:
    * Cache accessible children.
    */
   virtual void CacheChildren();
 
   /**
    * Set accessible parent and index in parent.
    */
   virtual void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
-  void UnbindFromParent();
+  virtual void UnbindFromParent();
 
   /**
    * Return sibling accessible at the given offset.
    */
   virtual nsAccessible* GetSiblingAtOffset(PRInt32 aOffset,
                                            nsresult *aError = nsnull) const;
 
   /**
--- a/accessible/src/base/nsBaseWidgetAccessible.cpp
+++ b/accessible/src/base/nsBaseWidgetAccessible.cpp
@@ -243,16 +243,26 @@ nsLinkableAccessible::BindToParent(nsAcc
     if (nsCoreUtils::HasClickListener(walkUpAcc->GetContent())) {
       mActionAcc = walkUpAcc;
       mIsOnclick = true;
       return;
     }
   }
 }
 
+void
+nsLinkableAccessible::UnbindFromParent()
+{
+  mActionAcc = nsnull;
+  mIsLink = false;
+  mIsOnclick = false;
+
+  nsAccessibleWrap::UnbindFromParent();
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // nsEnumRoleAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsEnumRoleAccessible::
   nsEnumRoleAccessible(nsIContent* aNode, nsDocAccessible* aDoc,
                        roles::Role aRole) :
   nsAccessibleWrap(aNode, aDoc), mRole(aRole)
--- a/accessible/src/base/nsBaseWidgetAccessible.h
+++ b/accessible/src/base/nsBaseWidgetAccessible.h
@@ -103,16 +103,17 @@ public:
   virtual KeyBinding AccessKey() const;
 
   // HyperLinkAccessible
   virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);
 
 protected:
   // nsAccessible
   virtual void BindToParent(nsAccessible* aParent, PRUint32 aIndexInParent);
+  virtual void UnbindFromParent();
 
   /**
    * Parent accessible that provides an action for this linkable accessible.
    */
   nsAccessible* mActionAcc;
   bool mIsLink;
   bool mIsOnclick;
 };
--- a/accessible/src/html/nsHTMLImageMapAccessible.cpp
+++ b/accessible/src/html/nsHTMLImageMapAccessible.cpp
@@ -211,63 +211,16 @@ nsHTMLAreaAccessible::Description(nsStri
   aDescription.Truncate();
 
   // Still to do - follow IE's standard here
   nsCOMPtr<nsIDOMHTMLAreaElement> area(do_QueryInterface(mContent));
   if (area) 
     area->GetShape(aDescription);
 }
 
-NS_IMETHODIMP
-nsHTMLAreaAccessible::GetBounds(PRInt32 *aX, PRInt32 *aY,
-                                PRInt32 *aWidth, PRInt32 *aHeight)
-{
-  NS_ENSURE_ARG_POINTER(aX);
-  *aX = 0;
-  NS_ENSURE_ARG_POINTER(aY);
-  *aY = 0;
-  NS_ENSURE_ARG_POINTER(aWidth);
-  *aWidth = 0;
-  NS_ENSURE_ARG_POINTER(aHeight);
-  *aHeight = 0;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
-  // Essentially this uses GetRect on mAreas of nsImageMap from nsImageFrame.
-  nsPresContext *presContext = GetPresContext();
-  NS_ENSURE_TRUE(presContext, NS_ERROR_FAILURE);
-
-  nsIFrame *frame = GetFrame();
-  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
-  nsImageFrame *imageFrame = do_QueryFrame(frame);
-
-  nsImageMap* map = imageFrame->GetImageMap();
-  NS_ENSURE_TRUE(map, NS_ERROR_FAILURE);
-
-  nsRect rect;
-  nsresult rv = map->GetBoundsForAreaContent(mContent, rect);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  *aX = presContext->AppUnitsToDevPixels(rect.x);
-  *aY = presContext->AppUnitsToDevPixels(rect.y);
-
-  // XXX Areas are screwy; they return their rects as a pair of points, one pair
-  // stored into the width and height.
-  *aWidth  = presContext->AppUnitsToDevPixels(rect.width - rect.x);
-  *aHeight = presContext->AppUnitsToDevPixels(rect.height - rect.y);
-
-  // Put coords in absolute screen coords
-  nsIntRect orgRectPixels = frame->GetScreenRectExternal();
-  *aX += orgRectPixels.x;
-  *aY += orgRectPixels.y;
-
-  return NS_OK;
-}
-
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLAreaAccessible: nsAccessNode public
 
 bool
 nsHTMLAreaAccessible::IsPrimaryForNode() const
 {
   // Make HTML area DOM element not accessible. HTML image map accessible
   // manages its tree itself.
@@ -321,8 +274,30 @@ nsHTMLAreaAccessible::EndOffset()
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLAreaAccessible: nsAccessible protected
 
 void
 nsHTMLAreaAccessible::CacheChildren()
 {
   // No children for aria accessible.
 }
+
+void
+nsHTMLAreaAccessible::GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame)
+{
+  nsIFrame* frame = GetFrame();
+  if (!frame)
+    return;
+
+  nsImageFrame* imageFrame = do_QueryFrame(frame);
+  nsImageMap* map = imageFrame->GetImageMap();
+
+  nsresult rv = map->GetBoundsForAreaContent(mContent, aBounds);
+  if (NS_FAILED(rv))
+    return;
+
+  // XXX Areas are screwy; they return their rects as a pair of points, one pair
+  // stored into the width and height.
+  aBounds.width -= aBounds.x;
+  aBounds.height -= aBounds.y;
+
+  *aBoundingFrame = frame;
+}
--- a/accessible/src/html/nsHTMLImageMapAccessible.h
+++ b/accessible/src/html/nsHTMLImageMapAccessible.h
@@ -91,20 +91,16 @@ nsAccessible::AsImageMap()
  * Accessible for image map areas - must be child of image.
  */
 class nsHTMLAreaAccessible : public nsHTMLLinkAccessible
 {
 public:
 
   nsHTMLAreaAccessible(nsIContent* aContent, nsDocAccessible* aDoc);
 
-  // nsIAccessible
-
-  NS_IMETHOD GetBounds(PRInt32 *x, PRInt32 *y, PRInt32 *width, PRInt32 *height);
-
   // nsAccessNode
   virtual bool IsPrimaryForNode() const;
 
   // nsAccessible
   virtual void Description(nsString& aDescription);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual PRUint64 NativeState();
   virtual nsAccessible* ChildAtPoint(PRInt32 aX, PRInt32 aY,
@@ -113,11 +109,12 @@ public:
   // HyperLinkAccessible
   virtual PRUint32 StartOffset();
   virtual PRUint32 EndOffset();
 
 protected:
 
   // nsAccessible
   virtual void CacheChildren();
+  virtual void GetBoundsRect(nsRect& aBounds, nsIFrame** aBoundingFrame);
 };
 
 #endif  
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -1219,17 +1219,17 @@ nsHyperTextAccessible::GetAttributesInte
   nsIFrame *frame = GetFrame();
   if (frame && frame->GetType() == nsGkAtoms::blockFrame) {
     nsAutoString oldValueUnused;
     aAttributes->SetStringProperty(NS_LITERAL_CSTRING("formatting"), NS_LITERAL_STRING("block"),
                                    oldValueUnused);
   }
 
   if (FocusMgr()->IsFocused(this)) {
-    PRInt32 lineNumber = GetCaretLineNumber();
+    PRInt32 lineNumber = CaretLineNumber();
     if (lineNumber >= 1) {
       nsAutoString strLineNumber;
       strLineNumber.AppendInt(lineNumber);
       nsAccUtils::SetAccAttr(aAttributes, nsGkAtoms::lineNumber,
                              strLineNumber);
     }
   }
 
@@ -1675,17 +1675,17 @@ nsHyperTextAccessible::GetCaretOffset(PR
       return NS_OK;
   }
 
   DOMPointToHypertextOffset(focusNode, focusOffset, aCaretOffset);
   return NS_OK;
 }
 
 PRInt32
-nsHyperTextAccessible::GetCaretLineNumber()
+nsHyperTextAccessible::CaretLineNumber()
 {
   // Provide the line number for the caret, relative to the
   // currently focused node. Use a 1-based index
   nsRefPtr<nsFrameSelection> frameSelection = FrameSelection();
   if (!frameSelection)
     return -1;
 
   nsISelection* domSel =
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -259,16 +259,32 @@ public:
    *
    * @param  aOffset  [in] the given text offset
    */
   nsAccessible* GetChildAtOffset(PRUint32 aOffset)
   {
     return GetChildAt(GetChildIndexAtOffset(aOffset));
   }
 
+  /**
+   * Return the bounds of the text between given start and end offset.
+   */
+  nsIntRect GetTextBounds(PRInt32 aStartOffset, PRInt32 aEndOffset)
+  {
+    nsIntRect bounds;
+    GetPosAndText(aStartOffset, aEndOffset, nsnull, nsnull, &bounds);
+    return bounds;
+  }
+
+  /**
+   * Provide the line number for the caret.
+   * @return 1-based index for the line number with the caret
+   */
+  PRInt32 CaretLineNumber();
+
   //////////////////////////////////////////////////////////////////////////////
   // EditableTextAccessible
 
   /**
    * Return the editor associated with the accessible.
    */
   virtual already_AddRefed<nsIEditor> GetEditor() const;
 
@@ -368,23 +384,16 @@ protected:
 
   /**
    * Return selection ranges within the accessible subtree.
    */
   void GetSelectionDOMRanges(PRInt16 aType, nsTArray<nsRange*>* aRanges);
 
   nsresult SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos);
 
-  /**
-   * Provide the line number for the caret, relative to the
-   * current DOM node.
-   * @return 1-based index for the line number with the caret
-   */
-  PRInt32 GetCaretLineNumber();
-
   // Helpers
   nsresult GetDOMPointByFrameOffset(nsIFrame *aFrame, PRInt32 aOffset,
                                     nsIAccessible *aAccessible,
                                     nsIDOMNode **aNode, PRInt32 *aNodeOffset);
 
   
   /**
    * Return hyper text offset for the specified bound of the given DOM range.
--- a/accessible/src/mac/mozAccessible.h
+++ b/accessible/src/mac/mozAccessible.h
@@ -39,16 +39,28 @@
 #include "nsAccessibleWrap.h"
 
 #import <Cocoa/Cocoa.h>
 
 #import "mozAccessibleProtocol.h"
 
 @class mozRootAccessible;
 
+/**
+ * All mozAccessibles are either abstract objects (that correspond to XUL
+ * widgets, HTML frames, etc) or are attached to a certain view; for example
+ * a document view. When we hand an object off to an AT, we always want
+ * to give it the represented view, in the latter case.
+ */
+inline id <mozAccessible>
+GetObjectOrRepresentedView(id <mozAccessible> aObject)
+{
+  return [aObject hasRepresentedView] ? [aObject representedView] : aObject;
+}
+
 @interface mozAccessible : NSObject <mozAccessible>
 {
   /**
    * Weak reference; it owns us.
    */
   nsAccessibleWrap* mGeckoAccessible;
   
   /**
--- a/accessible/src/mac/mozAccessible.mm
+++ b/accessible/src/mac/mozAccessible.mm
@@ -66,31 +66,16 @@ using namespace mozilla::a11y;
 // of the screen), into a top-left screen point, that gecko can use.
 static inline void
 ConvertCocoaToGeckoPoint(NSPoint &aInPoint, nsPoint &aOutPoint)
 {
   float mainScreenHeight = [(NSView*)[[NSScreen screens] objectAtIndex:0] frame].size.height;
   aOutPoint.MoveTo ((nscoord)aInPoint.x, (nscoord)(mainScreenHeight - aInPoint.y));
 }
 
-// all mozAccessibles are either abstract objects (that correspond to XUL widgets, HTML frames, etc) or are
-// attached to a certain view; for example a document view. when we hand an object off to an AT, we always want
-// to give it the represented view, in the latter case.
-static inline id <mozAccessible>
-GetObjectOrRepresentedView(id <mozAccessible> anObject)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  if ([anObject hasRepresentedView])
-    return [anObject representedView];
-  return anObject;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
-}
-
 // returns the passed in object if it is not ignored. if it's ignored, will return
 // the first unignored ancestor.
 static inline id
 GetClosestInterestingAccessible(id anObject)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   // this object is not ignored, so let's return it.
--- a/accessible/src/mac/mozTextAccessible.h
+++ b/accessible/src/mac/mozTextAccessible.h
@@ -5,18 +5,8 @@
 @interface mozTextAccessible : mozAccessible
 {
   // both of these are the same old mGeckoAccessible, but already
   // QI'd for us, to the right type, for convenience.
   nsHyperTextAccessible     *mGeckoTextAccessible;         // strong
   nsIAccessibleEditableText *mGeckoEditableTextAccessible; // strong
 }
 @end
-
-/* A combobox (in the mac world) is a textfield with an associated menu, for example
-   the location bar. */
-@interface mozComboboxAccessible : mozTextAccessible
-// equivalent to pressing return key in this textfield.
-- (void)confirm;
-
-// shows the menu for this combobox.
-- (void)showMenu;
-@end
--- a/accessible/src/mac/mozTextAccessible.mm
+++ b/accessible/src/mac/mozTextAccessible.mm
@@ -1,8 +1,10 @@
+/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
 #include "nsAccessibleWrap.h"
 
 #include "nsCocoaUtils.h"
 #include "nsObjCExceptions.h"
 
 #import "mozTextAccessible.h"
 
 using namespace mozilla::a11y;
@@ -238,112 +240,15 @@ using namespace mozilla::a11y;
 }
 
 #pragma mark -
 
 - (void)valueDidChange
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
-  NSAccessibilityPostNotification([self hasRepresentedView] ? [self representedView] : self, 
+  NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
                                   NSAccessibilityValueChangedNotification);
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 @end
-
-@implementation mozComboboxAccessible
-
-- (NSArray*)accessibilityAttributeNames
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  static NSArray *supportedAttributes = nil;
-  if (!supportedAttributes) {
-    // standard attributes that are shared and supported by all generic elements.
-    supportedAttributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required
-                                                           NSAccessibilityRoleAttribute,   // required
-                                                           NSAccessibilityTitleAttribute,
-                                                           NSAccessibilityValueAttribute, // required
-                                                           NSAccessibilityHelpAttribute,
-                                                           NSAccessibilityRoleDescriptionAttribute,
-                                                           NSAccessibilityPositionAttribute, // required
-                                                           NSAccessibilitySizeAttribute, // required
-                                                           NSAccessibilityWindowAttribute, // required
-                                                           NSAccessibilityFocusedAttribute, // required
-                                                           NSAccessibilityEnabledAttribute, // required
-                                                           NSAccessibilityChildrenAttribute, // required
-                                                           NSAccessibilityHelpAttribute,
-                                                           // NSAccessibilityExpandedAttribute, // required
-                                                           NSAccessibilityTopLevelUIElementAttribute, // required
-                                                           NSAccessibilityDescriptionAttribute, // required
-                                                           /* text-specific attributes */
-                                                           NSAccessibilitySelectedTextAttribute, // required
-                                                           NSAccessibilitySelectedTextRangeAttribute, // required
-                                                           NSAccessibilityNumberOfCharactersAttribute, // required
-                                                           // TODO: NSAccessibilityVisibleCharacterRangeAttribute, // required
-                                                           // TODO: NSAccessibilityInsertionPointLineNumberAttribute
-#if DEBUG
-                                                           @"AXMozDescription",
-#endif
-                                                           nil];
-  }
-  return supportedAttributes;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
-}
-
-- (NSArray *)accessibilityActionNames
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  if ([self isEnabled]) {
-    return [NSArray arrayWithObjects:NSAccessibilityConfirmAction,
-                                     NSAccessibilityShowMenuAction,
-                                     nil];
-  }
-  return nil;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
-}
-
-- (NSString *)accessibilityActionDescription:(NSString *)action
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
-
-  if ([action isEqualToString:NSAccessibilityShowMenuAction])
-    return @"show menu";
-  if ([action isEqualToString:NSAccessibilityConfirmAction])
-    return @"confirm";
-    
-  return [super accessibilityActionDescription:action];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
-}
-
-- (void)accessibilityPerformAction:(NSString *)action
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
-  // both the ShowMenu and Click action do the same thing.
-  if ([self isEnabled]) {
-    if ([action isEqualToString:NSAccessibilityShowMenuAction])
-      [self showMenu];
-    if ([action isEqualToString:NSAccessibilityConfirmAction])
-      [self confirm];
-  }
-
-  NS_OBJC_END_TRY_ABORT_BLOCK;
-}
-
-- (void)showMenu
-{
-  // currently unimplemented. waiting for support in bug 363697
-}
-
-- (void)confirm
-{
-  // should be the same as pressing enter/return in this textfield.
-  // not yet implemented
-}
-
-@end
--- a/accessible/src/mac/nsAccessibleWrap.mm
+++ b/accessible/src/mac/nsAccessibleWrap.mm
@@ -105,19 +105,16 @@ nsAccessibleWrap::GetNativeType ()
     }
     
     case roles::PAGETAB:
       return [mozButtonAccessible class];
 
     case roles::CHECKBUTTON:
       return [mozCheckboxAccessible class];
       
-    case roles::AUTOCOMPLETE:
-      return [mozComboboxAccessible class];
-
     case roles::HEADING:
       return [mozHeadingAccessible class];
 
     case roles::PAGETABLIST:
       return [mozTabsAccessible class];
       
     case roles::ENTRY:
     case roles::STATICTEXT:
--- a/accessible/src/mac/nsRoleMap.h
+++ b/accessible/src/mac/nsRoleMap.h
@@ -137,17 +137,17 @@ static const NSString* AXRoles [] = {
   NSAccessibilityGroupRole,                     // ROLE_TEXT_CONTAINER
   NSAccessibilityButtonRole,                    // ROLE_TOGGLE_BUTTON
   NSAccessibilityTableRole,                     // ROLE_TREE_TABLE
   NSAccessibilityUnknownRole,                   // ROLE_VIEWPORT
   NSAccessibilityGroupRole,                     // ROLE_HEADER
   NSAccessibilityGroupRole,                     // ROLE_FOOTER
   NSAccessibilityGroupRole,                     // ROLE_PARAGRAPH
   @"AXRuler",                                   // ROLE_RULER. 10.4+ only, so we re-define the constant.
-  NSAccessibilityComboBoxRole,                  // ROLE_AUTOCOMPLETE
+  NSAccessibilityUnknownRole,                   // ROLE_AUTOCOMPLETE
   NSAccessibilityTextFieldRole,                 // ROLE_EDITBAR
   NSAccessibilityTextFieldRole,                 // ROLE_ENTRY
   NSAccessibilityStaticTextRole,                // ROLE_CAPTION
   NSAccessibilityScrollAreaRole,                // ROLE_DOCUMENT_FRAME
   @"AXHeading",                                 // ROLE_HEADING
   NSAccessibilityGroupRole,                     // ROLE_PAG
   NSAccessibilityGroupRole,                     // ROLE_SECTION
   NSAccessibilityUnknownRole,                   // ROLE_REDUNDANT_OBJECT
--- a/accessible/tests/mochitest/bounds/test_zoom.html
+++ b/accessible/tests/mochitest/bounds/test_zoom.html
@@ -2,48 +2,68 @@
 <html>
 <head>
   <title>Accessible boundaries when page is zoomed</title>
   <link rel="stylesheet" type="text/css"
         href="chrome://mochikit/content/tests/SimpleTest/test.css" />
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
 
   <script type="application/javascript"
           src="../common.js"></script>
   <script type="application/javascript"
           src="../role.js"></script>
   <script type="application/javascript"
           src="../layout.js"></script>
   <script type="application/javascript"
           src="../browser.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
       var tabDocument = currentTabDocument();
-      var p1 = tabDocument.body.firstElementChild;
-      var p2 = tabDocument.body.lastElementChild;
+      var p1 = tabDocument.getElementById("p1");
+      var p2 = tabDocument.getElementById("p2");
+
+      var imgMap = tabDocument.getElementById("imgmap");
+      ensureImageMapTree(imgMap);
+      var imgMapAcc = getAccessible(imgMap);
+      var area = imgMapAcc.firstChild;
 
       testBounds(p1);
       testBounds(p2);
+      testBounds(area);
 
       zoomDocument(tabDocument, 2.0);
 
       testBounds(p1);
       testBounds(p2);
+      testBounds(area);
 
       closeBrowserWindow();
       SimpleTest.finish();
     }
 
+    var url = "data:text/html,<html><body>";
+    url += "<p id='p1'>para 1</p><p id='p2'>para 2</p>";
+    url += "<map name='atoz_map' id='map'>";
+    url += "  <area id='area1' href='http%3A%2F%2Fmozilla.org'";
+    url += "        coords=17,0,30,14' alt='mozilla.org' shape='rect'>";
+    url += "</map>";
+    url += "<img id='imgmap' width='447' height='15'";
+    url += "     usemap='%23atoz_map'";
+    url += "     src='chrome%3A%2F%2Fmochitests%2Fcontent%2Fa11y%2Faccessible%2Fletters.gif'>";
+    url += "</body></html>";
+
     SimpleTest.waitForExplicitFinish();
     openBrowserWindow(doTest,
-                      "data:text/html,<html><body><p>para 1</p><p>para 2</p></body></html>",
+                      url,
                       { left: 0, top: 0, width: 600, height: 600 });
   </script>
 </head>
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=650241"
      title="Location returned by accessibles incorrect when page zoomed">
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -489,17 +489,19 @@ function testDefunctAccessible(aAcc, aNo
 /**
  * Ensure that image map accessible tree is created.
  */
 function ensureImageMapTree(aID)
 {
   // XXX: We send a useless mouse move to the image to force it to setup its
   // image map, because flushing layout won't do it. Hopefully bug 135040
   // will make this not suck.
-  synthesizeMouse(getNode(aID), 10, 10, { type: "mousemove" });
+  var image = getNode(aID);
+  synthesizeMouse(image, 10, 10, { type: "mousemove" },
+                  image.ownerDocument.defaultView);
 
   // XXX This may affect a11y more than other code because imagemaps may not
   // get drawn or have an mouse event over them. Bug 570322 tracks a11y
   // dealing with this.
   todo(false, "Need to remove this image map workaround.");
 }
 
 /**
--- a/accessible/tests/mochitest/layout.js
+++ b/accessible/tests/mochitest/layout.js
@@ -102,21 +102,47 @@ function getBounds(aID)
 }
 
 /**
  * Return DOM node coordinates relative the screen and its size in device
  * pixels.
  */
 function getBoundsForDOMElm(aID)
 {
+  var x = 0, y = 0, width = 0, height = 0;
+
   var elm = getNode(aID);
+  if (elm.localName == "area") {
+    var mapName = elm.parentNode.getAttribute("name");
+    var selector = "[usemap='#" + mapName + "']";
+    var img = elm.ownerDocument.querySelector(selector);
+
+    var areaCoords = elm.coords.split(",");
+    var areaX = parseInt(areaCoords[0]);
+    var areaY = parseInt(areaCoords[1]);
+    var areaWidth = parseInt(areaCoords[2]) - areaX;
+    var areaHeight = parseInt(areaCoords[3]) - areaY;
+
+    var rect = img.getBoundingClientRect();
+    x = rect.left + areaX;
+    y = rect.top + areaY;
+    width = areaWidth;
+    height = areaHeight;
+  }
+  else {
+    var rect = elm.getBoundingClientRect();
+    x = rect.left;
+    y = rect.top;
+    width = rect.width;
+    height = rect.height;
+  }
+
   var elmWindow = elm.ownerDocument.defaultView;
   var winUtil = elmWindow.
     QueryInterface(Components.interfaces.nsIInterfaceRequestor).
     getInterface(Components.interfaces.nsIDOMWindowUtils);
 
   var ratio = winUtil.screenPixelsPerCSSPixel;
-  var rect = elm.getBoundingClientRect();
-  return [ (rect.left + elmWindow.mozInnerScreenX) * ratio,
-           (rect.top + elmWindow.mozInnerScreenY) * ratio,
-           rect.width * ratio,
-           rect.height * ratio ];
+  return [ (x + elmWindow.mozInnerScreenX) * ratio,
+           (y + elmWindow.mozInnerScreenY) * ratio,
+           width * ratio,
+           height * ratio ];
 }
--- a/allmakefiles.sh
+++ b/allmakefiles.sh
@@ -69,16 +69,22 @@ config/doxygen.cfg
 config/expandlibs_config.py
 config/tests/src-simple/Makefile
 mfbt/Makefile
 probes/Makefile
 extensions/Makefile
 "
 
 if [ ! "$LIBXUL_SDK" ]; then
+  if [ "$STLPORT_SOURCES" ]; then
+    add_makefiles "
+      build/stlport/Makefile
+      build/stlport/stl/config/_android.h
+    "
+  fi
   add_makefiles "
     memory/mozalloc/Makefile
     mozglue/Makefile
     mozglue/build/Makefile
   "
   if [ "$MOZ_MEMORY" ]; then
     add_makefiles "
       memory/jemalloc/Makefile
--- a/b2g/chrome/content/shell.js
+++ b/b2g/chrome/content/shell.js
@@ -322,27 +322,46 @@ var shell = {
   sendEvent: function shell_sendEvent(content, type, details) {
     let event = content.document.createEvent('CustomEvent');
     event.initCustomEvent(type, true, true, details ? details : {});
     content.dispatchEvent(event);
   }
 };
 
 (function PowerManager() {
-  let idleHandler = {
-    observe: function(subject, topic, time) {
-      if (topic === "idle") {
-        // TODO: Check wakelock status. See bug 697132.
+  // This will eventually be moved to content, so use content API as
+  // much as possible here. TODO: Bug 738530
+  let power = navigator.mozPower;
+  let idleHandler = function idleHandler(subject, topic, time) {
+    if (topic === "idle") {
+      if (power.getWakeLockState("screen") != "locked-foreground") {
         screen.mozEnabled = false;
       }
-    },
+    }
+  }
+  let wakeLockHandler = function wakeLockHandler(topic, state) {
+    // Turn off the screen when no one needs the it or all of them are
+    // invisible, otherwise turn the screen on. Note that the CPU
+    // might go to sleep as soon as the screen is turned off and
+    // acquiring wake lock will not bring it back (actually the code
+    // is not executed at all).
+    if (topic == "screen") {
+      if (state != "locked-foreground") {
+        if (Services.idle.idleTime > idleTimeout*1000) {
+          screen.mozEnabled = false;
+        }
+      } else {
+        screen.mozEnabled = true;
+      }
+    }
   }
   let idleTimeout = Services.prefs.getIntPref("power.screen.timeout");
   if (idleTimeout) {
     Services.idle.addIdleObserver(idleHandler, idleTimeout);
+    power.addWakeLockListener(wakeLockHandler);
   }
 })();
 
 function nsBrowserAccess() {
 }
 
 nsBrowserAccess.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]),
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -231,16 +231,17 @@
 @BINPATH@/components/necko.xpt
 @BINPATH@/components/loginmgr.xpt
 @BINPATH@/components/parentalcontrols.xpt
 @BINPATH@/components/places.xpt
 @BINPATH@/components/plugin.xpt
 @BINPATH@/components/pref.xpt
 @BINPATH@/components/prefetch.xpt
 @BINPATH@/components/profile.xpt
+@BINPATH@/components/profiler.xpt
 @BINPATH@/components/proxyObject.xpt
 @BINPATH@/components/rdf.xpt
 @BINPATH@/components/satchel.xpt
 @BINPATH@/components/saxparser.xpt
 @BINPATH@/components/sessionstore.xpt
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/services-crypto.xpt
 #endif
--- a/browser/base/content/test/browser_bug329212.js
+++ b/browser/base/content/test/browser_bug329212.js
@@ -2,40 +2,40 @@ function test () {
   waitForExplicitFinish();
   gBrowser.selectedTab = gBrowser.addTab();
   gBrowser.selectedBrowser.addEventListener("load", function () {
     gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
 
     let doc = gBrowser.contentDocument;
     let tooltip = document.getElementById("aHTMLTooltip");
 
-    ok(FillInHTMLTooltip(doc.getElementById("svg1"), "should get title"));
+    ok(FillInHTMLTooltip(doc.getElementById("svg1")), "should get title");
     is(tooltip.getAttribute("label"), "This is a non-root SVG element title");
 
-    ok(FillInHTMLTooltip(doc.getElementById("text1"), "should get title"));
+    ok(FillInHTMLTooltip(doc.getElementById("text1")), "should get title");
     is(tooltip.getAttribute("label"), "\n\n\n    This            is a title\n\n    ");
 
-    ok(!FillInHTMLTooltip(doc.getElementById("text2"), "should not get title"));
+    ok(!FillInHTMLTooltip(doc.getElementById("text2")), "should not get title");
 
-    ok(!FillInHTMLTooltip(doc.getElementById("text3"), "should not get title"));
+    ok(!FillInHTMLTooltip(doc.getElementById("text3")), "should not get title");
 
-    ok(FillInHTMLTooltip(doc.getElementById("link1"), "should get title"));
+    ok(FillInHTMLTooltip(doc.getElementById("link1")), "should get title");
     is(tooltip.getAttribute("label"), "\n      This is a title\n    ");
-    ok(FillInHTMLTooltip(doc.getElementById("text4"), "should get title"));
+    ok(FillInHTMLTooltip(doc.getElementById("text4")), "should get title");
     is(tooltip.getAttribute("label"), "\n      This is a title\n    ");
 
-    ok(!FillInHTMLTooltip(doc.getElementById("link2"), "should not get title"));
+    ok(!FillInHTMLTooltip(doc.getElementById("link2")), "should not get title");
 
-    ok(FillInHTMLTooltip(doc.getElementById("link3"), "should get title"));
-    ok(tooltip.getAttribute("label") != "");
+    ok(FillInHTMLTooltip(doc.getElementById("link3")), "should get title");
+    isnot(tooltip.getAttribute("label"), "");
 
-    ok(FillInHTMLTooltip(doc.getElementById("link4"), "should get title"));
+    ok(FillInHTMLTooltip(doc.getElementById("link4")), "should get title");
     is(tooltip.getAttribute("label"), "This is an xlink:title attribute");
 
-    ok(!FillInHTMLTooltip(doc.getElementById("text5"), "should not get title"));
+    ok(!FillInHTMLTooltip(doc.getElementById("text5")), "should not get title");
 
     gBrowser.removeCurrentTab();
     finish();
   }, true);
 
   content.location = 
     "http://mochi.test:8888/browser/browser/base/content/test/title_test.svg";
 }
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
+++ b/browser/components/migration/src/nsBrowserProfileMigratorUtils.cpp
@@ -158,25 +158,8 @@ GetProfilePath(nsIProfileStartup* aStart
       (do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
     if (dirSvc) {
       dirSvc->Get(NS_APP_USER_PROFILE_50_DIR, NS_GET_IID(nsIFile),
                   (void**) getter_AddRefs(aProfileDir));
     }
   }
 }
 
-nsresult
-ImportDefaultBookmarks()
-{
-  nsCOMPtr<nsIPlacesImportExportService> importer =
-    do_GetService(NS_PLACESIMPORTEXPORTSERVICE_CONTRACTID);
-  NS_ENSURE_STATE(importer);
-
-  nsCOMPtr<nsIIOService> ioService = mozilla::services::GetIOService();
-  NS_ENSURE_STATE(ioService);
-  nsCOMPtr<nsIURI> bookmarksURI;
-  nsresult rv = ioService->NewURI(DEFAULT_BOOKMARKS, nsnull, nsnull,
-                                  getter_AddRefs(bookmarksURI));
-  if (NS_FAILED(rv))
-    return rv;
-
-  return importer->ImportHTMLFromURI(bookmarksURI, true);
-}
--- a/browser/components/migration/src/nsBrowserProfileMigratorUtils.h
+++ b/browser/components/migration/src/nsBrowserProfileMigratorUtils.h
@@ -94,15 +94,10 @@ void GetMigrateDataFromArray(MigrationDa
                              nsIFile* aSourceProfile, 
                              PRUint16* aResult);
 
 
 // get the base directory of the *target* profile
 // this is already cloned, modify it to your heart's content
 void GetProfilePath(nsIProfileStartup* aStartup, nsCOMPtr<nsIFile>& aProfileDir);
 
-/**
- * Imports default bookmarks to the profile.
- */
-nsresult ImportDefaultBookmarks();
-
 #endif
 
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -1393,21 +1393,16 @@ nsIEProfileMigrator::CopyFavoritesBatche
 
     rv = bms->CreateFolder(bookmarksMenuFolderId,
                            NS_ConvertUTF16toUTF8(importedIEFavsTitle),
                            nsINavBookmarksService::DEFAULT_INDEX,
                            &folder);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
-    // If importing defaults fails for whatever reason, let the import process
-    // continue.
-    DebugOnly<nsresult> rv = ImportDefaultBookmarks();
-    MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to import default bookmarks");
-
     // Locate the Links toolbar folder, we want to replace the Personal Toolbar
     // content with Favorites in this folder.
     // On versions minor or equal to IE6 the folder name is stored in the
     // LinksFolderName registry key, but in newer versions it may be just a
     // Links subfolder inside the default Favorites folder.
     nsCOMPtr<nsIWindowsRegKey> regKey =
       do_CreateInstance("@mozilla.org/windows-registry-key;1");
     if (regKey &&
--- a/browser/components/migration/src/nsSafariProfileMigrator.cpp
+++ b/browser/components/migration/src/nsSafariProfileMigrator.cpp
@@ -969,21 +969,16 @@ nsSafariProfileMigrator::CopyBookmarksBa
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = bms->CreateFolder(bookmarksMenuFolderId,
                            NS_ConvertUTF16toUTF8(importedSafariBookmarksTitle),
                            nsINavBookmarksService::DEFAULT_INDEX, &folder);
     NS_ENSURE_SUCCESS(rv, rv);
   }
   else {
-    // If importing defaults fails for whatever reason, let the import process
-    // continue.
-    DebugOnly<nsresult> rv = ImportDefaultBookmarks();
-    MOZ_ASSERT(NS_SUCCEEDED(rv), "Should be able to import default bookmarks");
-
     // In replace mode we are merging at the top level.
     folder = bookmarksMenuFolderId;
   }
 
   nsCOMPtr<nsIProperties> fileLocator(do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID));
   nsCOMPtr<nsILocalFile> safariBookmarksFile;
   fileLocator->Get(NS_MAC_USER_LIB_DIR, NS_GET_IID(nsILocalFile),
                    getter_AddRefs(safariBookmarksFile));
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -57,16 +57,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/AddonManager.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "BookmarkHTMLUtils",
+                                  "resource://gre/modules/BookmarkHTMLUtils.jsm");
+
 XPCOMUtils.defineLazyModuleGetter(this, "KeywordURLResetPrompter",
                                   "resource:///modules/KeywordURLResetPrompter.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "webappsUI", 
                                   "resource:///modules/webappsUI.jsm");
 
 const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser";
 const PREF_PLUGINS_UPDATEURL  = "plugins.update.url";
@@ -223,20 +226,16 @@ BrowserGlue.prototype = {
         break;
       case "places-init-complete":
         this._initPlaces();
         Services.obs.removeObserver(this, "places-init-complete");
         this._isPlacesInitObserver = false;
         // no longer needed, since history was initialized completely.
         Services.obs.removeObserver(this, "places-database-locked");
         this._isPlacesLockedObserver = false;
-
-        // Now apply distribution customized bookmarks.
-        // This should always run after Places initialization.
-        this._distributionCustomizer.applyBookmarks();
         break;
       case "places-database-locked":
         this._isPlacesDatabaseLocked = true;
         // Stop observing, so further attempts to load history service
         // will not show the prompt.
         Services.obs.removeObserver(this, "places-database-locked");
         this._isPlacesLockedObserver = false;
         break;
@@ -252,23 +251,16 @@ BrowserGlue.prototype = {
         if (this._idleService.idleTime > BOOKMARKS_BACKUP_IDLE_TIME * 1000)
           this._backupBookmarks();
         break;
       case "distribution-customization-complete":
         Services.obs.removeObserver(this, "distribution-customization-complete");
         // Customization has finished, we don't need the customizer anymore.
         delete this._distributionCustomizer;
         break;
-      case "bookmarks-restore-success":
-      case "bookmarks-restore-failed":
-        Services.obs.removeObserver(this, "bookmarks-restore-success");
-        Services.obs.removeObserver(this, "bookmarks-restore-failed");
-        if (topic == "bookmarks-restore-success" && data == "html-initial")
-          this.ensurePlacesDefaultQueriesInitialized();
-        break;
       case "browser-glue-test": // used by tests
         if (data == "post-update-notification") {
           if (Services.prefs.prefHasUserValue("app.update.postupdate"))
             this._showUpdateNotification();
         }
         else if (data == "force-ui-migration") {
           this._migrateUI();
         }
@@ -283,16 +275,19 @@ BrowserGlue.prototype = {
         break;
       case "defaultURIFixup-using-keyword-pref":
         if (KeywordURLResetPrompter.shouldPrompt) {
           let keywordURI = subject.QueryInterface(Ci.nsIURI);
           KeywordURLResetPrompter.prompt(this.getMostRecentBrowserWindow(),
                                          keywordURI);
         }
         break;
+      case "initial-migration":
+        this._initialMigrationPerformed = true;
+        break;
     }
   }, 
 
   // initialization (called on application startup) 
   _init: function BG__init() {
     let os = Services.obs;
     os.addObserver(this, "xpcom-shutdown", false);
     os.addObserver(this, "prefservice:after-app-defaults", false);
@@ -985,28 +980,19 @@ BrowserGlue.prototype = {
    */
   _initPlaces: function BG__initPlaces() {
     // We must instantiate the history service since it will tell us if we
     // need to import or restore bookmarks due to first-run, corruption or
     // forced migration (due to a major schema change).
     // If the database is corrupt or has been newly created we should
     // import bookmarks.
     var dbStatus = PlacesUtils.history.databaseStatus;
-    var importBookmarks = dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE ||
-                          dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT;
-
-    if (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE) {
-      // If the database has just been created, but we already have any
-      // bookmark, this is not the initial import.  This can happen after a
-      // migration from a different browser since migrators run before us.
-      // In such a case we should not import, unless some pref has been set.
-      if (PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 0) != -1 ||
-          PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0) != -1)
-        importBookmarks = false;
-    }
+    var importBookmarks = !this._initialMigrationPerformed &&
+                          (dbStatus == PlacesUtils.history.DATABASE_STATUS_CREATE ||
+                           dbStatus == PlacesUtils.history.DATABASE_STATUS_CORRUPT);
 
     // Check if user or an extension has required to import bookmarks.html
     var importBookmarksHTML = false;
     try {
       importBookmarksHTML =
         Services.prefs.getBoolPref("browser.places.importBookmarksHTML");
       if (importBookmarksHTML)
         importBookmarks = true;
@@ -1053,16 +1039,19 @@ BrowserGlue.prototype = {
     }
 
     // If bookmarks are not imported, then initialize smart bookmarks.  This
     // happens during a common startup.
     // Otherwise, if any kind of import runs, smart bookmarks creation should be
     // delayed till the import operations has finished.  Not doing so would
     // cause them to be overwritten by the newly imported bookmarks.
     if (!importBookmarks) {
+      // Now apply distribution customized bookmarks.
+      // This should always run after Places initialization.
+      this._distributionCustomizer.applyBookmarks();
       this.ensurePlacesDefaultQueriesInitialized();
     }
     else {
       // An import operation is about to run.
       // Don't try to recreate smart bookmarks if autoExportHTML is true or
       // smart bookmarks are disabled.
       var autoExportHTML = false;
       try {
@@ -1086,35 +1075,38 @@ BrowserGlue.prototype = {
       }
       else {
         var bookmarksFile = dirService.get("BMarks", Ci.nsILocalFile);
         if (bookmarksFile.exists())
           bookmarksURI = NetUtil.newURI(bookmarksFile);
       }
 
       if (bookmarksURI) {
-        // Add an import observer.  It will ensure that smart bookmarks are
-        // created once the operation is complete.
-        Services.obs.addObserver(this, "bookmarks-restore-success", false);
-        Services.obs.addObserver(this, "bookmarks-restore-failed", false);
-
         // Import from bookmarks.html file.
         try {
-          var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
-                         getService(Ci.nsIPlacesImportExportService);
-          importer.importHTMLFromURI(bookmarksURI, true /* overwrite existing */);
+          BookmarkHTMLUtils.importFromURL(bookmarksURI.spec, true, (function (success) {
+            if (success) {
+              // Now apply distribution customized bookmarks.
+              // This should always run after Places initialization.
+              this._distributionCustomizer.applyBookmarks();
+              // Ensure that smart bookmarks are created once the operation is
+              // complete.
+              this.ensurePlacesDefaultQueriesInitialized();
+            }
+            else {
+              Cu.reportError("Bookmarks.html file could be corrupt.");
+            }
+          }).bind(this));
         } catch (err) {
-          // Report the error, but ignore it.
           Cu.reportError("Bookmarks.html file could be corrupt. " + err);
-          Services.obs.removeObserver(this, "bookmarks-restore-success");
-          Services.obs.removeObserver(this, "bookmarks-restore-failed");
         }
       }
-      else
+      else {
         Cu.reportError("Unable to find bookmarks.html file.");
+      }
 
       // Reset preferences, so we won't try to import again at next run
       if (importBookmarksHTML)
         Services.prefs.setBoolPref("browser.places.importBookmarksHTML", false);
       if (restoreDefaultBookmarks)
         Services.prefs.setBoolPref("browser.bookmarks.restore_default_bookmarks",
                                    false);
     }
--- a/browser/components/places/content/places.js
+++ b/browser/components/places/content/places.js
@@ -395,20 +395,19 @@ var PlacesOrganizer = {
    */
   importFromFile: function PO_importFromFile() {
     var fp = Cc["@mozilla.org/filepicker;1"].
              createInstance(Ci.nsIFilePicker);
     fp.init(window, PlacesUIUtils.getString("SelectImport"),
             Ci.nsIFilePicker.modeOpen);
     fp.appendFilters(Ci.nsIFilePicker.filterHTML);
     if (fp.show() != Ci.nsIFilePicker.returnCancel) {
-      if (fp.file) {
-        var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
-                       getService(Ci.nsIPlacesImportExportService);
-        importer.importHTMLFromFile(fp.file, false);
+      if (fp.fileURL) {
+        Components.utils.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+        BookmarkHTMLUtils.importFromURL(fp.fileURL.spec, false);
       }
     }
   },
 
   /**
    * Allows simple exporting of bookmarks.
    */
   exportBookmarks: function PO_exportBookmarks() {
--- a/browser/components/places/tests/unit/test_384370.js
+++ b/browser/components/places/tests/unit/test_384370.js
@@ -52,18 +52,18 @@ function run_test() {
     - export as html, import, test (tests integrity of json > html)
 
     BACKUP/RESTORE SUMMARY:
     - create a bookmark in each root
     - tag multiple URIs with multiple tags
     - export as json, import, test
   */
 
-  // get places import/export service
-  var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].getService(Ci.nsIPlacesImportExportService);
+  // import the importer
+  Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
 
   // avoid creating the places smart folder during tests
   Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch).
   setIntPref("browser.places.smartBookmarksVersion", -1);
 
   // file pointer to legacy bookmarks file
   //var bookmarksFileOld = do_get_file("bookmarks.large.html");
   var bookmarksFileOld = do_get_file("bookmarks.preplaces.html");
@@ -78,28 +78,36 @@ function run_test() {
   if (!jsonFile.exists())
     do_throw("couldn't create file: bookmarks.exported.json");
 
   // Test importing a pre-Places canonical bookmarks file.
   // 1. import bookmarks.preplaces.html
   // 2. run the test-suite
   // Note: we do not empty the db before this import to catch bugs like 380999
   try {
-    importer.importHTMLFromFile(bookmarksFileOld, true);
+    BookmarkHTMLUtils.importFromFile(bookmarksFileOld, true, after_import);
   } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
+}
+
+function after_import(success) {
+  if (!success) {
+    do_throw("Couldn't import legacy bookmarks file.");
+  }
   populate();
   validate();
 
   waitForAsyncUpdates(function () {
     // Test exporting a Places canonical json file.
     // 1. export to bookmarks.exported.json
     // 2. empty bookmarks db
     // 3. import bookmarks.exported.json
     // 4. run the test-suite
     try {
+      var jsonFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
+      jsonFile.append("bookmarks.exported.json");
       PlacesUtils.backups.saveBookmarksToJSONFile(jsonFile);
     } catch(ex) { do_throw("couldn't export to file: " + ex); }
     LOG("exported json");
     try {
       PlacesUtils.restoreBookmarksFromJSONFile(jsonFile);
     } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
     LOG("imported json");
     validate();
--- a/browser/components/places/tests/unit/test_421483.js
+++ b/browser/components/places/tests/unit/test_421483.js
@@ -51,16 +51,18 @@ try {
 } catch(ex) {
   do_throw("Could not get Annotation service\n");
 }
 
 // Get browser glue
 try {
   var gluesvc = Cc["@mozilla.org/browser/browserglue;1"].
                 getService(Ci.nsIBrowserGlue);
+  // Avoid default bookmarks import.
+  gluesvc.QueryInterface(Ci.nsIObserver).observe(null, "initial-migration", null);
 } catch(ex) {
   do_throw("Could not get BrowserGlue service\n");
 }
 
 // Get pref service
 try {
   var pref =  Cc["@mozilla.org/preferences-service;1"].
               getService(Ci.nsIPrefBranch);
--- a/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
+++ b/browser/components/places/tests/unit/test_457441-import-export-corrupt-bookmarks-html.js
@@ -50,16 +50,17 @@ var bs = Cc["@mozilla.org/browser/nav-bo
 var as = Cc["@mozilla.org/browser/annotation-service;1"].
          getService(Ci.nsIAnnotationService);
 var icos = Cc["@mozilla.org/browser/favicon-service;1"].
            getService(Ci.nsIFaviconService);
 var ps = Cc["@mozilla.org/preferences-service;1"].
          getService(Ci.nsIPrefBranch);
 var ies = Cc["@mozilla.org/browser/places/import-export-service;1"].
           getService(Ci.nsIPlacesImportExportService);
+Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
 
 const DESCRIPTION_ANNO = "bookmarkProperties/description";
 const LOAD_IN_SIDEBAR_ANNO = "bookmarkProperties/loadInSidebar";
 const POST_DATA_ANNO = "bookmarkProperties/POSTData";
 
 const TEST_FAVICON_PAGE_URL = "http://en-US.www.mozilla.com/en-US/firefox/central/";
 const TEST_FAVICON_DATA_URL = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAAK/INwWK6QAAABl0RVh0U29mdHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAHWSURBVHjaYvz//z8DJQAggJiQOe/fv2fv7Oz8rays/N+VkfG/iYnJfyD/1+rVq7ffu3dPFpsBAAHEAHIBCJ85c8bN2Nj4vwsDw/8zQLwKiO8CcRoQu0DxqlWrdsHUwzBAAIGJmTNnPgYa9j8UqhFElwPxf2MIDeIrKSn9FwSJoRkAEEAM0DD4DzMAyPi/G+QKY4hh5WAXGf8PDQ0FGwJ22d27CjADAAIIrLmjo+MXA9R2kAHvGBA2wwx6B8W7od6CeQcggKCmCEL8bgwxYCbUIGTDVkHDBia+CuotgACCueD3TDQN75D4xmAvCoK9ARMHBzAw0AECiBHkAlC0Mdy7x9ABNA3obAZXIAa6iKEcGlMVQHwWyjYuL2d4v2cPg8vZswx7gHyAAAK7AOif7SAbOqCmn4Ha3AHFsIDtgPq/vLz8P4MSkJ2W9h8ggBjevXvHDo4FQUQg/kdypqCg4H8lUIACnQ/SOBMYI8bAsAJFPcj1AAEEjwVQqLpAbXmH5BJjqI0gi9DTAAgDBBCcAVLkgmQ7yKCZxpCQxqUZhAECCJ4XgMl493ug21ZD+aDAXH0WLM4A9MZPXJkJIIAwTAR5pQMalaCABQUULttBGCCAGCnNzgABBgAMJ5THwGvJLAAAAABJRU5ErkJggg==";
 
@@ -67,18 +68,24 @@ function run_test() {
   do_test_pending();
 
   // avoid creating the places smart folder during tests
   ps.setIntPref("browser.places.smartBookmarksVersion", -1);
 
   // import bookmarks from corrupt file
   var corruptBookmarksFile = do_get_file("bookmarks.corrupt.html");
   try {
-    ies.importHTMLFromFile(corruptBookmarksFile, true);
+    BookmarkHTMLUtils.importFromFile(corruptBookmarksFile, true, after_import);
   } catch(ex) { do_throw("couldn't import corrupt bookmarks file: " + ex); }
+}
+
+function after_import(success) {
+  if (!success) {
+    do_throw("Couldn't import corrupt bookmarks file.");
+  }
 
   // Check that every bookmark is correct
   // Corrupt bookmarks should not have been imported
   database_check();
   waitForAsyncUpdates(function() {
     // Create corruption in database
     var corruptItemId = bs.insertBookmark(bs.toolbarFolder,
                                           uri("http://test.mozilla.org"),
@@ -100,24 +107,26 @@ function run_test() {
       ies.exportHTMLToFile(bookmarksFile);
     } catch(ex) { do_throw("couldn't export to bookmarks.exported.html: " + ex); }
 
     // Clear all bookmarks
     remove_all_bookmarks();
 
     // Import bookmarks
     try {
-      ies.importHTMLFromFile(bookmarksFile, true);
+      BookmarkHTMLUtils.importFromFile(bookmarksFile, true, before_database_check);
     } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+  });
+}
 
+function before_database_check(success) {
     // Check that every bookmark is correct
     database_check();
 
     waitForAsyncUpdates(do_test_finished);
-  });
 }
 
 /*
  * Check for imported bookmarks correctness
  */
 function database_check() {
   // BOOKMARKS MENU
   var query = hs.getNewQuery();
--- a/browser/components/places/tests/unit/test_bookmarksRestoreNotification.js
+++ b/browser/components/places/tests/unit/test_bookmarksRestoreNotification.js
@@ -31,16 +31,18 @@
  * 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 ***** */
 
+Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+
 /**
  * Tests the bookmarks-restore-* nsIObserver notifications after restoring
  * bookmarks from JSON and HTML.  See bug 470314.
  */
 
 // The topics and data passed to nsIObserver.observe() on bookmarks restore
 const NSIOBSERVER_TOPIC_BEGIN    = "bookmarks-restore-begin";
 const NSIOBSERVER_TOPIC_SUCCESS  = "bookmarks-restore-success";
@@ -129,37 +131,45 @@ var tests = [
     desc:       "HTML restore: normal restore should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.html");
       addBookmarks();
-      importer.exportHTMLToFile(this.file);
+      exporter.exportHTMLToFile(this.file);
       remove_all_bookmarks();
       try {
-        importer.importHTMLFromFile(this.file, false);
+        BookmarkHTMLUtils.importFromFile(this.file, false, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
     desc:       "HTML restore: empty file should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.init.html");
       try {
-        importer.importHTMLFromFile(this.file, false);
+        BookmarkHTMLUtils.importFromFile(this.file, false, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");            
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
@@ -167,53 +177,65 @@ var tests = [
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_FAILED,
     data:       NSIOBSERVER_DATA_HTML,
     folderId:   null,
     run:        function () {
       this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
       this.file.append("this file doesn't exist because nobody created it");
       try {
-        importer.importHTMLFromFile(this.file, false);
-        do_throw("  Restore should have failed");
+        BookmarkHTMLUtils.importFromFile(this.file, false, function (success) {
+          print("callback");
+          if (success) {
+            do_throw("  Restore should have failed");
+          }
+        });
       }
       catch (e) {}
     }
   },
 
   {
     desc:       "HTML initial restore: normal restore should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML_INIT,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.init.html");
       addBookmarks();
-      importer.exportHTMLToFile(this.file);
+      exporter.exportHTMLToFile(this.file);
       remove_all_bookmarks();
       try {
-        importer.importHTMLFromFile(this.file, true);
+        BookmarkHTMLUtils.importFromFile(this.file, true, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
     desc:       "HTML initial restore: empty file should succeed",
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_SUCCESS,
     data:       NSIOBSERVER_DATA_HTML_INIT,
     folderId:   null,
     run:        function () {
       this.file = createFile("bookmarks-test_restoreNotification.init.html");
       try {
-        importer.importHTMLFromFile(this.file, true);
+        BookmarkHTMLUtils.importFromFile(this.file, true, function (success) {
+          if (!success) {
+            do_throw("  Restore should not have failed");
+          }
+        });
       }
       catch (e) {
         do_throw("  Restore should not have failed");
       }
     }
   },
 
   {
@@ -221,23 +243,25 @@ var tests = [
     currTopic:  NSIOBSERVER_TOPIC_BEGIN,
     finalTopic: NSIOBSERVER_TOPIC_FAILED,
     data:       NSIOBSERVER_DATA_HTML_INIT,
     folderId:   null,
     run:        function () {
       this.file = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
       this.file.append("this file doesn't exist because nobody created it");
       try {
-        importer.importHTMLFromFile(this.file, true);
-        do_throw("  Restore should have failed");
+        BookmarkHTMLUtils.importFromFile(this.file, true, function (success) {
+          if (success) {
+            do_throw("  Restore should have failed");
+          }
+        });
       }
       catch (e) {}
     }
   }
-
 ];
 
 // nsIObserver that observes bookmarks-restore-begin.
 var beginObserver = {
   observe: function _beginObserver(aSubject, aTopic, aData) {
     var test = tests[currTestIndex];
 
     print("  Observed " + aTopic);
@@ -276,30 +300,30 @@ var successAndFailedObserver = {
     if (aSubject) {
       do_check_eq(aSubject.QueryInterface(Ci.nsISupportsPRInt64).data,
                   test.folderId);
     }
     else
       do_check_eq(test.folderId, null);
 
     remove_all_bookmarks();
-    doNextTest();
+    do_execute_soon(doNextTest);
   }
 };
 
 // Index of the currently running test.  See doNextTest().
 var currTestIndex = -1;
 
 var bmsvc = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
             getService(Ci.nsINavBookmarksService);
 
 var obssvc = Cc["@mozilla.org/observer-service;1"].
              getService(Ci.nsIObserverService);
 
-var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
+var exporter = Cc["@mozilla.org/browser/places/import-export-service;1"].
                getService(Ci.nsIPlacesImportExportService);
 
 ///////////////////////////////////////////////////////////////////////////////
 
 /**
  * Adds some bookmarks for the URIs in |uris|.
  */
 function addBookmarks() {
--- a/browser/components/places/tests/unit/test_bookmarks_html.js
+++ b/browser/components/places/tests/unit/test_bookmarks_html.js
@@ -94,19 +94,21 @@ let test_bookmarks = {
   ]
 };
 
 // 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"].
+let exporter = Cc["@mozilla.org/browser/places/import-export-service;1"].
                getService(Ci.nsIPlacesImportExportService);
 
+Cu.import("resource://gre/modules/BookmarkHTMLUtils.jsm");
+
 function run_test()
 {
   run_next_test();
 }
 
 add_test(function setup() {
   // Avoid creating smart bookmarks during the test.
   Services.prefs.setIntPref("browser.places.smartBookmarksVersion", -1);
@@ -126,131 +128,157 @@ add_test(function setup() {
   }
 
   // This test must be the first one, since it setups the new bookmarks.html.
   // Test importing a pre-Places canonical bookmarks file.
   // 1. import bookmarks.preplaces.html
   // 2. run the test-suite
   // Note: we do not empty the db before this import to catch bugs like 380999
   try {
-    importer.importHTMLFromFile(gBookmarksFileOld, true);
-  } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileOld, true, function(success) {
+      if (success) {
+        waitForAsyncUpdates(function () {
+          testImportedBookmarks();
 
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+          // Prepare for next tests.
+          try {
+            exporter.exportHTMLToFile(gBookmarksFileNew);
+          } catch(ex) { do_throw("couldn't export to file: " + ex); }
 
-    // Prepare for next tests.
-    try {
-      importer.exportHTMLToFile(gBookmarksFileNew);
-    } catch(ex) { do_throw("couldn't export to file: " + ex); }
-
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+          waitForAsyncUpdates(function () {
+            remove_all_bookmarks();
+            run_next_test();
+          });
+        });
+      } else {
+        do_throw("couldn't import legacy bookmarks file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import legacy bookmarks file: " + ex); }
 });
 
 add_test(function test_import_new()
 {
   // Test importing a Places bookmarks.html file.
   // 1. import bookmarks.exported.html
   // 2. run the test-suite
 
   try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
-
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+      if (success) {
+        waitForAsyncUpdates(function () {
+          testImportedBookmarks();
 
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+          waitForAsyncUpdates(function () {
+            remove_all_bookmarks();
+            run_next_test();
+          });
+        });
+      } else {
+        do_throw("couldn't import the exported file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
 add_test(function test_emptytitle_export()
 {
   // Test exporting and importing with an empty-titled bookmark.
   // 1. import bookmarks
   // 1. create an empty-titled bookmark.
   // 2. export to bookmarks.exported.html
   // 3. empty bookmarks db
   // 4. import bookmarks.exported.html
   // 5. run the test-suite
 
   try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+      if (success) {
+        const NOTITLE_URL = "http://notitle.mozilla.org/";
+        let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
+                                                      NetUtil.newURI(NOTITLE_URL),
+                                                      PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                                      "");
+        test_bookmarks.unfiled.push({ title: "", url: NOTITLE_URL });
 
-  const NOTITLE_URL = "http://notitle.mozilla.org/";
-  let id = PlacesUtils.bookmarks.insertBookmark(PlacesUtils.unfiledBookmarksFolderId,
-                                                NetUtil.newURI(NOTITLE_URL),
-                                                PlacesUtils.bookmarks.DEFAULT_INDEX,
-                                                "");
-  test_bookmarks.unfiled.push({ title: "", url: NOTITLE_URL });
+        try {
+          exporter.exportHTMLToFile(gBookmarksFileNew);
+        } catch(ex) { do_throw("couldn't export to file: " + ex); }
+
+        remove_all_bookmarks();
 
-  try {
-    importer.exportHTMLToFile(gBookmarksFileNew);
-  } catch(ex) { do_throw("couldn't export to file: " + ex); }
-
-  remove_all_bookmarks();
+        try {
+          BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+           if (success) {
+              waitForAsyncUpdates(function () {
+                testImportedBookmarks();
 
-  try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+                // Cleanup.
+                test_bookmarks.unfiled.pop();
+                PlacesUtils.bookmarks.removeItem(id);
 
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+                try {
+                  exporter.exportHTMLToFile(gBookmarksFileNew);
+                } catch(ex) { do_throw("couldn't export to file: " + ex); }
 
-    // Cleanup.
-    test_bookmarks.unfiled.pop();
-    PlacesUtils.bookmarks.removeItem(id);
-
-    try {
-      importer.exportHTMLToFile(gBookmarksFileNew);
-    } catch(ex) { do_throw("couldn't export to file: " + ex); }
-
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+                waitForAsyncUpdates(function () {
+                  remove_all_bookmarks();
+                  run_next_test();
+                });
+              });
+            } else {
+              do_throw("couldn't import the exported file.");
+            }
+          });
+        } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+      } else {
+        do_throw("couldn't import the exported file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
 add_test(function test_import_ontop()
 {
   // Test importing the exported bookmarks.html file *on top of* the existing
   // bookmarks.
   // 1. empty bookmarks db
   // 2. import the exported bookmarks file
   // 3. export to file
   // 3. import the exported bookmarks file
   // 4. run the test-suite
 
   try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
-  try {
-    importer.exportHTMLToFile(gBookmarksFileNew);
-  } catch(ex) { do_throw("couldn't export to file: " + ex); }
-  try {
-    importer.importHTMLFromFile(gBookmarksFileNew, true);
-  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
+    BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+      if (success) {
+        try {
+          exporter.exportHTMLToFile(gBookmarksFileNew);
+        } catch(ex) { do_throw("couldn't export to file: " + ex); }
+        try {
+          BookmarkHTMLUtils.importFromFile(gBookmarksFileNew, true, function(success) {
+            if (success) {
+              waitForAsyncUpdates(function () {
+                testImportedBookmarks();
 
-  waitForAsyncUpdates(function () {
-    testImportedBookmarks();
+                waitForAsyncUpdates(function () {
+                  remove_all_bookmarks();
+                  run_next_test();
+                });
+              });
+            } else {
+              do_throw("couldn't import the exported file.");
+            }
+          });
+        } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 
-    waitForAsyncUpdates(function () {
-      remove_all_bookmarks();
-      run_next_test();
+      } else {
+        do_throw("couldn't import the exported file.");
+      }
     });
-  });
+  } catch(ex) { do_throw("couldn't import the exported file: " + ex); }
 });
 
 function testImportedBookmarks()
 {
   for (let group in test_bookmarks) {
     do_print("[testImportedBookmarks()] Checking group '" + group + "'");
 
     let root;
--- a/browser/components/places/tests/unit/test_browserGlue_migrate.js
+++ b/browser/components/places/tests/unit/test_browserGlue_migrate.js
@@ -24,24 +24,25 @@ function run_test() {
     do_check_false(db.exists());
   }
 
   // Initialize Places through the History Service and check that a new
   // database has been created.
   do_check_eq(PlacesUtils.history.databaseStatus,
               PlacesUtils.history.DATABASE_STATUS_CREATE);
 
-  // A migrator would run before nsBrowserGlue, so we mimic that behavior
-  // adding a bookmark.
+  //A migrator would run before nsBrowserGlue Places initialization, so mimic
+  //that behavior adding a bookmark and notifying the migration.
   PlacesUtils.bookmarks.insertBookmark(PlacesUtils.bookmarks.bookmarksMenuFolder, uri("http://mozilla.org/"),
                     PlacesUtils.bookmarks.DEFAULT_INDEX, "migrated");
 
   // Initialize nsBrowserGlue.
   let bg = Cc["@mozilla.org/browser/browserglue;1"].
-           getService(Ci.nsIBrowserGlue);
+           getService(Ci.nsIObserver);
+  bg.observe(null, "initial-migration", null)
 
   let bookmarksObserver = {
     onBeginUpdateBatch: function() {},
     onEndUpdateBatch: function() {
       // Check if the currently finished batch created the smart bookmarks.
       let itemId =
         PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
       do_check_neq(itemId, -1);
--- a/browser/components/places/tests/unit/test_browserGlue_prefs.js
+++ b/browser/components/places/tests/unit/test_browserGlue_prefs.js
@@ -12,16 +12,26 @@ const PREF_SMART_BOOKMARKS_VERSION = "br
 const PREF_AUTO_EXPORT_HTML = "browser.bookmarks.autoExportHTML";
 
 const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
 const TOPICDATA_FORCE_PLACES_INIT = "force-places-init";
 
 let bg = Cc["@mozilla.org/browser/browserglue;1"].
          getService(Ci.nsIBrowserGlue);
 
+function waitForImportAndSmartBookmarks(aCallback) {
+  Services.obs.addObserver(function waitImport() {
+    Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
+    // Delay to test eventual smart bookmarks creation.
+    do_execute_soon(function () {
+      waitForAsyncUpdates(aCallback);
+    });
+  }, "bookmarks-restore-success", false);
+}
+
 let gTests = [
 
   // This test must be the first one.
   function test_checkPreferences() {
     // Initialize Places through the History Service and check that a new
     // database has been created.
     do_check_eq(PlacesUtils.history.databaseStatus,
                 PlacesUtils.history.DATABASE_STATUS_CREATE);
@@ -61,29 +71,30 @@ let gTests = [
       PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces().
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, and a smart bookmark has been
+      // created.
+      itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                                    SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, and a smart bookmark has been
-    // created.
-    itemId = PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                                  SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-
-    run_next_test();
   },
 
   function test_import_noSmartBookmarks()
   {
     do_log_info("import from bookmarks.html, but don't create smart bookmarks \
                  if they are disabled");
 
     remove_all_bookmarks();
@@ -93,29 +104,30 @@ let gTests = [
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, -1);
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces().
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, but smart bookmarks have not
+      // been created.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, but smart bookmarks have not
-    // been created.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-
-    run_next_test();
   },
 
   function test_import_autoExport_updatedSmartBookmarks()
   {
     do_log_info("Import from bookmarks.html, but don't create smart bookmarks \
                  if autoExportHTML is true and they are at latest version");
 
     remove_all_bookmarks();
@@ -126,30 +138,31 @@ let gTests = [
 
     // Set preferences.
     Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 999);
     Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, but smart bookmarks have not
+      // been created.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+      // Check preferences have been reverted.
+      Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, but smart bookmarks have not
-    // been created.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-    // Check preferences have been reverted.
-    Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
-
-    run_next_test();
   },
 
   function test_import_autoExport_oldSmartBookmarks()
   {
     do_log_info("Import from bookmarks.html, and create smart bookmarks if \
                  autoExportHTML is true and they are not at latest version.");
 
     remove_all_bookmarks();
@@ -160,31 +173,32 @@ let gTests = [
 
     // Set preferences.
     Services.prefs.setIntPref(PREF_SMART_BOOKMARKS_VERSION, 0);
     Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, true);
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been imported, but smart bookmarks have not
+      // been created.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                             SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+      // Check preferences have been reverted.
+      Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been imported, but smart bookmarks have not
-    // been created.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                           SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_eq(PlacesUtils.bookmarks.getItemTitle(itemId), "example");
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-    // Check preferences have been reverted.
-    Services.prefs.setBoolPref(PREF_AUTO_EXPORT_HTML, false);
-
-    run_next_test();
   },
 
   function test_restore()
   {
     do_log_info("restore from default bookmarks.html if \
                  restore_default_bookmarks is true.");
 
     remove_all_bookmarks();
@@ -193,29 +207,31 @@ let gTests = [
       PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId, 0);
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been restored.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                             SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_true(itemId > 0);
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
 
-    // Check bookmarks.html has been restored.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                           SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_true(itemId > 0);
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
-
-    run_next_test();
   },
 
   function test_restore_import()
   {
     do_log_info("setting both importBookmarksHTML and \
                  restore_default_bookmarks should restore defaults.");
 
     remove_all_bookmarks();
@@ -225,30 +241,31 @@ let gTests = [
     do_check_eq(itemId, -1);
 
     // Set preferences.
     Services.prefs.setBoolPref(PREF_IMPORT_BOOKMARKS_HTML, true);
     Services.prefs.setBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS, true);
 
     // Force nsBrowserGlue::_initPlaces()
     print("Simulate Places init");
+    waitForImportAndSmartBookmarks(function () {
+      // Check bookmarks.html has been restored.
+      itemId =
+        PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
+                                             SMART_BOOKMARKS_ON_TOOLBAR);
+      do_check_true(itemId > 0);
+      // Check preferences have been reverted.
+      do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
+      do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
+
+      run_next_test();
+    });
     bg.QueryInterface(Ci.nsIObserver).observe(null,
                                               TOPIC_BROWSERGLUE_TEST,
                                               TOPICDATA_FORCE_PLACES_INIT);
-
-    // Check bookmarks.html has been restored.
-    itemId =
-      PlacesUtils.bookmarks.getIdForItemAt(PlacesUtils.toolbarFolderId,
-                                           SMART_BOOKMARKS_ON_TOOLBAR);
-    do_check_true(itemId > 0);
-    // Check preferences have been reverted.
-    do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
-    do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
-
-    run_next_test();
   }
 
 ];
 
 do_register_cleanup(function () {
   remove_all_bookmarks();
   remove_bookmarks_html();
   remove_all_JSON_backups();
--- a/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js
+++ b/browser/components/places/tests/unit/test_browserGlue_smartBookmarks.js
@@ -359,11 +359,20 @@ function run_test() {
   do_check_false(Services.prefs.getBoolPref(PREF_AUTO_EXPORT_HTML));
   do_check_false(Services.prefs.getBoolPref(PREF_RESTORE_DEFAULT_BOOKMARKS));
   try {
     do_check_false(Services.prefs.getBoolPref(PREF_IMPORT_BOOKMARKS_HTML));
     do_throw("importBookmarksHTML pref should not exist");
   }
   catch(ex) {}
 
-  // Kick-off tests.
-  next_test();
+  waitForImportAndSmartBookmarks(next_test);
 }
+
+function waitForImportAndSmartBookmarks(aCallback) {
+  Services.obs.addObserver(function waitImport() {
+    Services.obs.removeObserver(waitImport, "bookmarks-restore-success");
+    // Delay to test eventual smart bookmarks creation.
+    do_execute_soon(function () {
+      waitForAsyncUpdates(aCallback);
+    });
+  }, "bookmarks-restore-success", false);
+}
--- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -105,17 +105,19 @@ let notificationsObserver = {
 }
 
 let timeInMicroseconds = Date.now() * 1000;
 
 function run_test() {
   do_test_pending();
 
   print("Initialize browserglue before Places");
-  Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIBrowserGlue);
+  // Avoid default bookmarks import.
+  Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver)
+    .observe(null, "initial-migration", null);
 
   Services.prefs.setBoolPref("privacy.clearOnShutdown.cache", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.offlineApps", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.history", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.downloads", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.cookies", true);
   Services.prefs.setBoolPref("privacy.clearOnShutdown.formData", true);
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -94,17 +94,19 @@
 ; [Base Browser Files]
 #ifndef XP_UNIX
 @BINPATH@/@MOZ_APP_NAME@.exe
 #else
 @BINPATH@/@MOZ_APP_NAME@-bin
 @BINPATH@/@MOZ_APP_NAME@
 #endif
 @BINPATH@/application.ini
+#ifdef MOZ_UPDATER
 @BINPATH@/update-settings.ini
+#endif
 @BINPATH@/platform.ini
 #ifndef XP_OS2
 @BINPATH@/@DLL_PREFIX@mozsqlite3@DLL_SUFFIX@
 #else
 @BINPATH@/mozsqlt3@DLL_SUFFIX@
 #endif
 @BINPATH@/blocklist.xml
 #ifdef XP_UNIX
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -275,17 +275,17 @@ can reach it easily. -->
 <!ENTITY preferencesCmd2.label       "Options">
 <!ENTITY preferencesCmd2.accesskey     "O">
 <!ENTITY preferencesCmdUnix.label       "Preferences">
 <!ENTITY preferencesCmdUnix.accesskey     "n"> 
 
 <!ENTITY clearRecentHistory.label               "Clear Recent History…">
 <!ENTITY clearRecentHistory.accesskey           "H">
 
-<!-- LOCALIZATION NOTE : These two strings can share an access key beause they never appear together on the menu -->
+<!-- LOCALIZATION NOTE : These two strings can share an access key because they never appear together on the menu -->
 <!ENTITY privateBrowsingCmd.start.label         "Start Private Browsing">
 <!ENTITY privateBrowsingCmd.start.accesskey     "P">
 <!ENTITY privateBrowsingCmd.stop.label          "Stop Private Browsing">
 <!ENTITY privateBrowsingCmd.stop.accesskey      "P">
 <!ENTITY privateBrowsingCmd.commandkey          "P">
 
 <!ENTITY viewMenu.label         "View"> 
 <!ENTITY viewMenu.accesskey       "V"> 
--- a/browser/themes/winstripe/browser-aero.css
+++ b/browser/themes/winstripe/browser-aero.css
@@ -153,19 +153,19 @@
   #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child:not(:-moz-lwtheme) {
     background-color: transparent !important;
     color: black;
     text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
     border-left-style: none !important;
     border-right-style: none !important;
   }
 
-  #toolbar-menubar :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
+  #toolbar-menubar :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
   #TabsToolbar[tabsontop=true] :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
-  #navigator-toolbox[tabsontop=false] > #nav-bar :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
+  #navigator-toolbox[tabsontop=false] > #nav-bar :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme),
   #nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child :-moz-any(@primaryToolbarButtons@):not(:-moz-any(#alltabs-button,#tabview-button,#new-tab-button,#sync-button[status])) > .toolbarbutton-icon:not(:-moz-lwtheme) {
     list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
   }
 
   #toolbar-menubar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker:not(:-moz-lwtheme),
   #toolbar-menubar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:not(:-moz-lwtheme),
   #TabsToolbar[tabsontop=true] .toolbarbutton-1 > .toolbarbutton-menu-dropmarker:not(:-moz-lwtheme),
   #TabsToolbar[tabsontop=true] .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:not(:-moz-lwtheme),
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -998,16 +998,18 @@ toolbar[mode=full] .toolbarbutton-1 > .t
 #tabview-button,
 #menu_tabview {
   list-style-image: url(chrome://browser/skin/tabview/tabview.png);
 }
 
 %ifdef WINSTRIPE_AERO
 #TabsToolbar > #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
 #TabsToolbar > toolbarpaletteitem > #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+#navigator-toolbox[tabsontop=false] > #nav-bar #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+#toolbar-menubar #tabview-button:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
 %endif
 #tabview-button:-moz-lwtheme-brighttext {
   list-style-image: url(chrome://browser/skin/tabview/tabview-inverted.png);
 }
 
 #tabview-button {
   -moz-image-region: rect(0, 90px, 18px, 72px);
 }
@@ -2046,16 +2048,18 @@ richlistitem[type~="action"][actiontype=
 #alltabs-button[type="menu"] {
   list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow.png");
   -moz-image-region: auto;
 }
 
 %ifdef WINSTRIPE_AERO
 #TabsToolbar > #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
 #TabsToolbar > toolbarpaletteitem > #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+#navigator-toolbox[tabsontop=false] > #nav-bar #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
+#toolbar-menubar #alltabs-button[type="menu"]:-moz-system-metric(windows-compositor):not(:-moz-lwtheme),
 %endif
 #alltabs-button[type="menu"]:-moz-lwtheme-brighttext {
   list-style-image: url("chrome://browser/skin/toolbarbutton-dropdown-arrow-inverted.png");
 }
 
 #alltabs-button[type="menu"] > .toolbarbutton-icon {
   margin: 0 2px;
 }
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -48,34 +48,40 @@ MODULE = build
 ifeq (,$(filter WINNT OS2,$(OS_ARCH)))
 DIRS		= unix
 endif
 
 ifeq (WINNT,$(OS_ARCH))
 DIRS = win32
 endif
 
+ifdef STLPORT_SOURCES
+DIRS += stlport
+endif
+
 DIRS += pgo
 
 TEST_DIRS += autoconf/test
 ifeq (android,$(MOZ_WIDGET_TOOLKIT))
 TEST_DIRS += mobile/sutagent/android \
           mobile/sutagent/android/watcher \
           mobile/sutagent/android/ffxcp \
           mobile/sutagent/android/fencp \
           mobile/robocop \
           $(NULL)
 endif
 
 ifdef MOZ_APP_BASENAME
 DIST_FILES = application.ini
 
 ifneq (android,$(MOZ_WIDGET_TOOLKIT))
+ifdef MOZ_UPDATER
 DIST_FILES += update-settings.ini
 endif
+endif
 
 ifdef LIBXUL_SDK
 GRE_MILESTONE = $(shell $(PYTHON) $(topsrcdir)/config/printconfigsetting.py $(LIBXUL_DIST)/bin/platform.ini Build Milestone)
 APP_INI_DEPS = $(LIBXUL_DIST)/bin/platform.ini
 else
 GRE_MILESTONE = $(shell tail -n 1 $(topsrcdir)/config/milestone.txt 2>/dev/null || tail -1 $(topsrcdir)/config/milestone.txt)
 APP_INI_DEPS = $(topsrcdir)/config/milestone.txt
 endif
@@ -155,20 +161,22 @@ GARBAGE_DIRS += $(_LEAKTEST_DIR)
 		$(NULL)
 
 leaktest.py: leaktest.py.in
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $^ > $@
 	chmod +x $@
 GARBAGE += leaktest.py
 
 ifneq (android,$(MOZ_WIDGET_TOOLKIT))
+ifdef MOZ_UPDATER
 update-settings.ini: update-settings.ini.in $(APP_INI_DEPS)
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
 GARBAGE += update-settings.ini
 endif
+endif
 
 ifdef MOZ_APP_BASENAME
 application.ini: application.ini.in $(APP_INI_DEPS)
 	$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $< > $@
 GARBAGE += application.ini
 
 ifdef MOZ_APP_STATIC_INI
 application.ini.h: appini_header.py application.ini 
--- a/build/mobile/devicemanager.py
+++ b/build/mobile/devicemanager.py
@@ -230,22 +230,22 @@ class DeviceManager:
       procName = proc[1].split('/')[-1]
       if (procName == app):
         pid = proc[0]
         break
     return pid
 
 
   @abstractmethod
-  def killProcess(self, appname):
+  def killProcess(self, appname, forceKill=False):
     """
     external function
     returns:
-    success: output from testagent
-    failure: None
+    success: True
+    failure: False
     """
     
   @abstractmethod
   def catFile(self, remoteFile):
     """
     external function
     returns:
     success: filecontents
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -371,26 +371,31 @@ class DeviceManagerADB(DeviceManager):
       acmd.append("-d")
       acmd.append(''.join(['\'',uri, '\'']));
     print acmd
     self.checkCmd(acmd)
     return outputFile
 
   # external function
   # returns:
-  #  success: output from testagent
-  #  failure: None
-  def killProcess(self, appname):
+  #  success: True
+  #  failure: False
+  def killProcess(self, appname, forceKill=False):
     procs = self.getProcessList()
+    didKillProcess = False
     for (pid, name, user) in procs:
       if name == appname:
-        p = self.runCmdAs(["shell", "kill", pid])
-        return p.stdout.read()
+         args = ["shell", "kill"]
+         if forceKill:
+           args.append("-9")
+         args.append(pid)
+         p = self.runCmdAs(args)
+         didKillProcess = True
 
-    return None
+    return didKillProcess
 
   # external function
   # returns:
   #  success: filecontents
   #  failure: None
   def catFile(self, remoteFile):
     #p = self.runCmd(["shell", "cat", remoteFile])
     #return p.stdout.read()
--- a/build/mobile/devicemanagerSUT.py
+++ b/build/mobile/devicemanagerSUT.py
@@ -577,25 +577,27 @@ class DeviceManagerSUT(DeviceManager):
     cmdline = '%s %s' % (self.formatEnvString(env), cmdline)
 
     if self.fireProcess(cmdline, failIfRunning) is None:
       return None
     return outputFile
 
   # external function
   # returns:
-  #  success: output from testagent
-  #  failure: None
-  def killProcess(self, appname):
+  #  success: True
+  #  failure: False
+  def killProcess(self, appname, forceKill=False):
+    if forceKill:
+      print "WARNING: killProcess(): forceKill parameter unsupported on SUT"
     try:
       data = self.runCmds(['kill ' + appname])
     except AgentError:
-      return None
+      return False
 
-    return data
+    return True
 
   # external function
   # returns:
   #  success: tmpdir, string
   #  failure: None
   def getTempDir(self):
     try:
       data = self.runCmds(['tmpd'])
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -65,16 +65,18 @@ import android.opengl.GLSurfaceView;
 import android.view.View;
 import android.util.Log;
 
 import org.json.*;
 
 import com.jayway.android.robotium.solo.Solo;
 
 public class FennecNativeDriver implements Driver {
+    private static final int FRAME_TIME_THRESHOLD = 17;     // allow 17ms per frame (~60fps)
+
     // Map of IDs to element names.
     private HashMap mLocators = null;
     private Activity mActivity;
     private Solo mSolo;
 
     private static String mLogFile = null;
     private static LogLevel mLogLevel = LogLevel.LOG_LEVEL_INFO;
 
@@ -220,31 +222,25 @@ public class FennecNativeDriver implemen
         } catch (InvocationTargetException e) {
             e.printStackTrace();
         }
     }
 
     public int stopFrameRecording() {
         Class [] parameters = new Class[1];
         parameters[0] = null;
-        List frames;
 
         try {
             Object [] params = null;
-            frames = (List)_stopFrameRecording.invoke(null, params);
-            Object [] framearray = frames.toArray();
-            Long last = new Long(0);
-            Long threshold = new Long(17);
+            List<Long> frames = (List<Long>)_stopFrameRecording.invoke(null, params);
             int numDelays = 0;
-            for (int i=0; i < framearray.length; i++) {
-                Long val = (Long)framearray[i];
-                if ((val - last) > threshold) {
+            for (int i = 1; i < frames.size(); i++) {
+                if (frames.get(i) - frames.get(i-1) > FRAME_TIME_THRESHOLD) {
                     numDelays++;
                 }
-                last = val;
             }
             return numDelays;
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         } catch (InvocationTargetException e) {
             e.printStackTrace();
         }
 
@@ -260,28 +256,25 @@ public class FennecNativeDriver implemen
         } catch (InvocationTargetException e) {
             e.printStackTrace();
         }
     }
 
     public float stopCheckerboardRecording() {
         Class [] parameters = new Class[1];
         parameters[0] = null;
-        List checkerboard;
 
         try {
             Object [] params = null;
-            checkerboard = (List)_stopCheckerboardRecording.invoke(null, params);
-            Object [] amountarray = checkerboard.toArray();
-            double completeness = 0;
-            for (Object obj : amountarray) {
-                float val = (Float)obj;
-                completeness += (1.0 - (double)val) / (double)amountarray.length;
+            List<Float> checkerboard = (List<Float>)_stopCheckerboardRecording.invoke(null, params);
+            float completeness = 0;
+            for (float val : checkerboard) {
+                completeness += (1.0f - val);
             }
-            return (float)completeness;
+            return completeness / (float)checkerboard.size();
         } catch (IllegalAccessException e) {
             e.printStackTrace();
         } catch (InvocationTargetException e) {
             e.printStackTrace();
         }
 
         return 0.0f;
     }
--- a/build/stdc++compat.cpp
+++ b/build/stdc++compat.cpp
@@ -54,19 +54,17 @@
 namespace std {
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 9)
     /* Instantiate these templates to avoid GLIBCXX_3.4.9 symbol versions */
     template ostream& ostream::_M_insert(double);
     template ostream& ostream::_M_insert(long);
     template ostream& ostream::_M_insert(unsigned long);
     template ostream& ostream::_M_insert(long long);
     template ostream& ostream::_M_insert(unsigned long long);
-#ifdef DEBUG
     template ostream& ostream::_M_insert(const void*);
-#endif
     template ostream& __ostream_insert(ostream&, const char*, streamsize);
     template istream& istream::_M_extract(double&);
 #endif
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 14)
     /* Instantiate these templates to avoid GLIBCXX_3.4.14 symbol versions
      * depending on optimization level */
     template char *string::_S_construct_aux_2(size_type, char, allocator<char> const&);
 #ifdef _GLIBCXX_USE_WCHAR_T
new file mode 100644
--- /dev/null
+++ b/build/stlport/Makefile.in
@@ -0,0 +1,30 @@
+# 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/.
+
+DEPTH = ../..
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULES = stlport
+LIBRARY_NAME = stlport_static
+FORCE_STATIC_LIB = 1
+STL_FLAGS =
+
+# Force to build a static library, instead of a fake library, without
+# installing it in dist/lib.
+LIBRARY = $(LIB_PREFIX)$(LIBRARY_NAME).$(LIB_SUFFIX)
+
+VPATH += $(STLPORT_SOURCES)/src
+
+CPPSRCS = $(notdir $(wildcard $(STLPORT_SOURCES)/src/*.cpp))
+CSRCS = $(notdir $(wildcard $(STLPORT_SOURCES)/src/*.c))
+
+include $(topsrcdir)/config/rules.mk
+
+DEFINES += -D_GNU_SOURCE
+CXXFLAGS += -fuse-cxa-atexit
+INCLUDES += -I$(STLPORT_SOURCES)/stlport
new file mode 100644
--- /dev/null
+++ b/build/stlport/stl/config/_android.h.in
@@ -0,0 +1,23 @@
+/* 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_stl_config__android_h
+#define mozilla_stl_config__android_h
+
+#include "@STLPORT_SOURCES@/stlport/stl/config/_android.h"
+
+// No rtti support
+#undef _STLP_NO_RTTI
+#define _STLP_NO_RTTI 1
+
+// No throwing exceptions
+#undef _STLP_NO_EXCEPTIONS
+#define _STLP_NO_EXCEPTIONS 1
+
+#undef _STLP_NATIVE_CPP_C_HEADER
+#define _STLP_NATIVE_CPP_C_HEADER(header) <../../system/include/header>
+#undef _STLP_NATIVE_CPP_RUNTIME_HEADER
+#define _STLP_NATIVE_CPP_RUNTIME_HEADER(header) <../../system/include/header>
+
+#endif /* mozilla_stl_config__android_h */
--- a/build/unix/build-toolchain/build-gcc.py
+++ b/build/unix/build-toolchain/build-gcc.py
@@ -104,16 +104,17 @@ def build_gcc(stage_dir, is_stage_one):
     tool_inst_dir = stage_dir + '/inst'
     lib_inst_dir = stage_dir + '/libinst'
     gcc_configure_args = ["--prefix=%s" % tool_inst_dir,
                           "--enable-__cxa_atexit",
                           "--with-gmp=%s" % lib_inst_dir,
                           "--with-mpfr=%s" % lib_inst_dir,
                           "--with-mpc=%s" % lib_inst_dir,
                           "--enable-languages=c,c++",
+                          "--disable-lto",
                           "--disable-multilib",
                           "--disable-bootstrap"]
     if is_stage_one:
         # We build the stage1 gcc without shared libraries. Otherwise its
         # libgcc.so would depend on the system libc.so, which causes problems
         # when it tries to use that libgcc.so and the libc we are about to
         # build.
         gcc_configure_args.append("--disable-shared")
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -686,16 +686,17 @@ MOZ_APP_COMPONENT_LIBS = @MOZ_APP_COMPON
 MOZ_APP_EXTRA_LIBS = @MOZ_APP_EXTRA_LIBS@
 
 ANDROID_NDK       = @ANDROID_NDK@
 ANDROID_TOOLCHAIN = @ANDROID_TOOLCHAIN@
 ANDROID_PLATFORM  = @ANDROID_PLATFORM@
 ANDROID_SDK       = @ANDROID_SDK@
 ANDROID_PLATFORM_TOOLS = @ANDROID_PLATFORM_TOOLS@
 ANDROID_VERSION   = @ANDROID_VERSION@
+STLPORT_SOURCES   = @STLPORT_SOURCES@
 
 ANDROID_PACKAGE_NAME = @ANDROID_PACKAGE_NAME@
 
 JS_SHARED_LIBRARY = @JS_SHARED_LIBRARY@
 
 MOZ_INSTRUMENT_EVENT_LOOP = @MOZ_INSTRUMENT_EVENT_LOOP@
 
 MOZ_SYSTEM_PLY = @MOZ_SYSTEM_PLY@
--- a/configure.in
+++ b/configure.in
@@ -1611,33 +1611,36 @@ fi
 if test "$OS_TARGET" = "Android" -a -z "$gonkdir"; then
     if test -n "$MOZ_ANDROID_LIBSTDCXX" ; then
        if test ! -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libstdc++.a" ; then
           AC_MSG_ERROR([Cannot find path to libstdc++ (NDK version >= 5?)])
        fi
        STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include -D_GLIBCXX_PERMIT_BACKWARD_HASH"
        STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH"
        STLPORT_LIBS="-lstdc++"
-    elif test -e "$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then
-       STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport"
-       STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/"
+    elif test -e "$android_ndk/sources/cxx-stl/stlport/src/iostream.cpp" ; then
+       STLPORT_SOURCES="$android_ndk/sources/cxx-stl/stlport"
+       STLPORT_CPPFLAGS="-I$_objdir/build/stlport -I$android_ndk/sources/cxx-stl/stlport/stlport"
+       STLPORT_LDFLAGS="-L$_objdir/build/stlport -L$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/"
        STLPORT_LIBS="-lstlport_static"
     elif  test -e "$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then
        STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport"
        STLPORT_LDFLAGS="-L$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH"
        STLPORT_LIBS="-lstlport_static"
     elif test "$target" != "arm-android-eabi"; then
        dnl fail if we're not building with NDKr4
        AC_MSG_ERROR([Couldn't find path to stlport in the android ndk])
     fi
     CXXFLAGS="$CXXFLAGS $STLPORT_CPPFLAGS"
     LDFLAGS="$LDFLAGS $STLPORT_LDFLAGS"
     LIBS="$LIBS $STLPORT_LIBS"
 fi
 
+AC_SUBST([STLPORT_SOURCES])
+
 dnl ========================================================
 dnl Suppress Clang Argument Warnings
 dnl ========================================================
 if test -n "$CLANG_CC"; then
     _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}"
     CPPFLAGS="-Qunused-arguments ${CPPFLAGS}"
 fi
 if test -n "$CLANG_CXX"; then
@@ -7223,23 +7226,21 @@ AC_SUBST(MOZ_GLUE_PROGRAM_LDFLAGS)
 AC_SUBST(WIN32_CRT_LIBS)
 dnl Need to set this for make because NSS doesn't have configure
 AC_SUBST(DLLFLAGS)
 
 dnl We need to wrap dlopen and related functions on Android because we use
 dnl our own linker.
 if test "$OS_TARGET" = Android; then
     WRAP_LDFLAGS="${WRAP_LDFLAGS} -L$_objdir/dist/lib -lmozglue"
-    if test "$MOZ_WIDGET_TOOLKIT" = android; then
-        if test -n "$MOZ_OLD_LINKER"; then
-            WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=dlopen,--wrap=dlclose,--wrap=dlerror,--wrap=dlsym,--wrap=dladdr"
-        fi
-        WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror"
-        WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=fork,--wrap=pthread_atfork"
-    fi
+    if test -n "$MOZ_OLD_LINKER"; then
+        WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=dlopen,--wrap=dlclose,--wrap=dlerror,--wrap=dlsym,--wrap=dladdr"
+    fi
+    WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=getaddrinfo,--wrap=freeaddrinfo,--wrap=gai_strerror"
+    WRAP_LDFLAGS="${WRAP_LDFLAGS} -Wl,--wrap=fork,--wrap=pthread_atfork"
 fi
 
 dnl ========================================================
 dnl = Use malloc wrapper lib
 dnl ========================================================
 MOZ_ARG_ENABLE_BOOL(wrap-malloc,
 [  --enable-wrap-malloc    Wrap malloc calls (gnu linker only)],
     _WRAP_MALLOC=1,
@@ -9102,16 +9103,19 @@ if test "$MOZ_MEMORY"; then
 fi
 if test -n "$MOZ_GLUE_LDFLAGS"; then
    export MOZ_GLUE_LDFLAGS
 fi
 if test -n "$MOZ_GLUE_PROGRAM_LDFLAGS"; then
    export MOZ_GLUE_PROGRAM_LDFLAGS
 fi
 export MOZ_APP_NAME
+export STLPORT_CPPFLAGS
+export STLPORT_LDFLAGS
+export STLPORT_LIBS
 AC_OUTPUT_SUBDIRS(js/src)
 ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 
 fi # COMPILE_ENVIRONMENT && !LIBXUL_SDK_DIR
 
 dnl Prevent the regeneration of autoconf.mk forcing rebuilds of the world
 dnl Needs to be at the end to respect possible changes from NSPR configure
 if cmp -s config/autoconf.mk config/autoconf.mk.orig; then
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -1243,23 +1243,24 @@ nsresult
 nsObjectLoadingContent::LoadObject(nsIURI* aURI,
                                    bool aNotify,
                                    const nsCString& aTypeHint,
                                    bool aForceLoad)
 {
   // Only do a URI equality check for things that aren't stopped plugins.
   // This is because we still need to load again if the plugin has been stopped.
   if (mType == eType_Document || mType == eType_Image || mInstanceOwner) {
-    if (mURI && aURI && !aForceLoad) {
+    if (mURI && aURI) {
       bool equal;
       nsresult rv = mURI->Equals(aURI, &equal);
-      if (NS_SUCCEEDED(rv) && equal) {
+      if (NS_SUCCEEDED(rv) && equal && !aForceLoad) {
         // URI didn't change, do nothing
         return NS_OK;
       }
+      StopPluginInstance();
     }
   }
 
   // Need to revoke any potentially pending instantiate events
   if (mType == eType_Plugin && mPendingInstantiateEvent) {
     mPendingInstantiateEvent = nsnull;
   }
 
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -412,18 +412,17 @@ nsScriptLoader::ProcessScriptElement(nsI
   // For now though, if JS is disabled we assume every language is
   // disabled.
   // XXX is this different from the mDocument->IsScriptEnabled() call?
   nsIScriptGlobalObject *globalObject = mDocument->GetScriptGlobalObject();
   if (!globalObject) {
     return false;
   }
   
-  nsIScriptContext *context = globalObject->GetScriptContext(
-                                        nsIProgrammingLanguage::JAVASCRIPT);
+  nsIScriptContext *context = globalObject->GetScriptContext();
 
   // If scripts aren't enabled in the current context, there's no
   // point in going on.
   if (!context || !context->GetScriptsEnabled()) {
     return false;
   }
 
   // Default script language is whatever the root element specifies
@@ -885,24 +884,24 @@ nsScriptLoader::EvaluateScript(nsScriptL
   nsCOMPtr<nsIScriptGlobalObject> globalObject = do_QueryInterface(pwin);
   NS_ASSERTION(globalObject, "windows must be global objects");
 
   // Get the script-type to be used by this element.
   NS_ASSERTION(scriptContent, "no content - what is default script-type?");
   PRUint32 stid = scriptContent ? scriptContent->GetScriptTypeID() :
                                   nsIProgrammingLanguage::JAVASCRIPT;
   // and make sure we are setup for this type of script.
-  rv = globalObject->EnsureScriptEnvironment(stid);
+  rv = globalObject->EnsureScriptEnvironment();
   if (NS_FAILED(rv))
     return rv;
 
   // Make sure context is a strong reference since we access it after
   // we've executed a script, which may cause all other references to
   // the context to go away.
-  nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext(stid);
+  nsCOMPtr<nsIScriptContext> context = globalObject->GetScriptContext();
   if (!context) {
     return NS_ERROR_FAILURE;
   }
 
   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
   context->SetProcessingScriptTag(true);
 
   // Update our current script.
--- a/content/canvas/public/nsICanvasElementExternal.h
+++ b/content/canvas/public/nsICanvasElementExternal.h
@@ -62,24 +62,29 @@ struct _cairo_surface;
  * same functionality is needed in both places, two separate methods should be
  * used.
  */
 
 class nsICanvasElementExternal : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASELEMENTEXTERNAL_IID)
 
+  enum {
+    RenderFlagPremultAlpha = 0x1
+  };
+
   /**
    * Get the size in pixels of this canvas element
    */
   NS_IMETHOD_(nsIntSize) GetSizeExternal() = 0;
 
   /*
    * Ask the canvas element to tell the contexts to render themselves
    * to the given gfxContext at the origin of its coordinate space.
    */
   NS_IMETHOD RenderContextsExternal(gfxContext *ctx,
-                                    gfxPattern::GraphicsFilter aFilter) = 0;
+                                    gfxPattern::GraphicsFilter aFilter,
+                                    PRUint32 aFlags = RenderFlagPremultAlpha) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsICanvasElementExternal, NS_ICANVASELEMENTEXTERNAL_IID)
 
 #endif /* nsICanvasElementExternal_h___ */
--- a/content/canvas/public/nsICanvasRenderingContextInternal.h
+++ b/content/canvas/public/nsICanvasRenderingContextInternal.h
@@ -69,28 +69,34 @@ class SourceSurface;
 
 class nsICanvasRenderingContextInternal : public nsISupports {
 public:
   typedef mozilla::layers::CanvasLayer CanvasLayer;
   typedef mozilla::layers::LayerManager LayerManager;
 
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICANVASRENDERINGCONTEXTINTERNAL_IID)
 
+  enum {
+    RenderFlagPremultAlpha = 0x1
+  };
+
   // This method should NOT hold a ref to aParentCanvas; it will be called
   // with nsnull when the element is going away.
   NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas) = 0;
 
   // Sets the dimensions of the canvas, in pixels.  Called
   // whenever the size of the element changes.
   NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height) = 0;
 
   NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, PRInt32 width, PRInt32 height) = 0;
 
   // Render the canvas at the origin of the given gfxContext
-  NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter) = 0;
+  NS_IMETHOD Render(gfxContext *ctx,
+                    gfxPattern::GraphicsFilter aFilter,
+                    PRUint32 aFlags = RenderFlagPremultAlpha) = 0;
 
   // Gives you a stream containing the image represented by this context.
   // The format is given in aMimeTime, for example "image/png".
   //
   // If the image format does not support transparency or aIncludeTransparency
   // is false, alpha will be discarded and the result will be the image
   // composited on black.
   NS_IMETHOD GetInputStream(const char *aMimeType,
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -556,28 +556,36 @@ WebGLContext::SetDimensions(PRInt32 widt
 
     gl->ClearSafely();
 
     reporter.SetSuccessful();
     return NS_OK;
 }
 
 NS_IMETHODIMP
-WebGLContext::Render(gfxContext *ctx, gfxPattern::GraphicsFilter f)
+WebGLContext::Render(gfxContext *ctx, gfxPattern::GraphicsFilter f, PRUint32 aFlags)
 {
     if (!gl)
         return NS_OK;
 
     nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
                                                          gfxASurface::ImageFormatARGB32);
     if (surf->CairoStatus() != 0)
         return NS_ERROR_FAILURE;
 
     gl->ReadPixelsIntoImageSurface(0, 0, mWidth, mHeight, surf);
-    gfxUtils::PremultiplyImageSurface(surf);
+
+    bool srcPremultAlpha = mOptions.premultipliedAlpha;
+    bool dstPremultAlpha = aFlags & RenderFlagPremultAlpha;
+
+    if (!srcPremultAlpha && dstPremultAlpha) {
+        gfxUtils::PremultiplyImageSurface(surf);
+    } else if (srcPremultAlpha && !dstPremultAlpha) {
+        gfxUtils::UnpremultiplyImageSurface(surf);
+    }
 
     nsRefPtr<gfxPattern> pat = new gfxPattern(surf);
     pat->SetFilter(f);
 
     // Pixels from ReadPixels will be "upside down" compared to
     // what cairo wants, so draw with a y-flip and a translte to
     // flip them.
     gfxMatrix m;
@@ -603,38 +611,50 @@ WebGLContext::GetInputStream(const char*
 
     nsRefPtr<gfxImageSurface> surf = new gfxImageSurface(gfxIntSize(mWidth, mHeight),
                                                          gfxASurface::ImageFormatARGB32);
     if (surf->CairoStatus() != 0)
         return NS_ERROR_FAILURE;
 
     nsRefPtr<gfxContext> tmpcx = new gfxContext(surf);
     // Use Render() to make sure that appropriate y-flip gets applied
-    nsresult rv = Render(tmpcx, gfxPattern::FILTER_NEAREST);
+    PRUint32 flags = mOptions.premultipliedAlpha ? RenderFlagPremultAlpha : 0;
+    nsresult rv = Render(tmpcx, gfxPattern::FILTER_NEAREST, flags);
     if (NS_FAILED(rv))
         return rv;
 
     const char encoderPrefix[] = "@mozilla.org/image/encoder;2?type=";
     nsAutoArrayPtr<char> conid(new char[strlen(encoderPrefix) + strlen(aMimeType) + 1]);
 
     if (!conid)
         return NS_ERROR_OUT_OF_MEMORY;
 
     strcpy(conid, encoderPrefix);
     strcat(conid, aMimeType);
 
     nsCOMPtr<imgIEncoder> encoder = do_CreateInstance(conid);
     if (!encoder)
         return NS_ERROR_FAILURE;
 
+    int format = imgIEncoder::INPUT_FORMAT_HOSTARGB;
+    if (!mOptions.premultipliedAlpha) {
+        // We need to convert to INPUT_FORMAT_RGBA, otherwise
+        // we are automatically considered premult, and unpremult'd.
+        // Yes, it is THAT silly.
+        // Except for different lossy conversions by color,
+        // we could probably just change the label, and not change the data.
+        gfxUtils::ConvertBGRAtoRGBA(surf);
+        format = imgIEncoder::INPUT_FORMAT_RGBA;
+    }
+
     rv = encoder->InitFromData(surf->Data(),
                                mWidth * mHeight * 4,
                                mWidth, mHeight,
                                surf->Stride(),
-                               imgIEncoder::INPUT_FORMAT_HOSTARGB,
+                               format,
                                nsDependentString(aEncoderOptions));
     NS_ENSURE_SUCCESS(rv, rv);
 
     return CallQueryInterface(encoder, aStream);
 }
 
 NS_IMETHODIMP
 WebGLContext::GetThebesSurface(gfxASurface **surface)
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -537,17 +537,19 @@ public:
 
     // nsICanvasRenderingContextInternal
     NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
     NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
     NS_IMETHOD InitializeWithSurface(nsIDocShell *docShell, gfxASurface *surface, PRInt32 width, PRInt32 height)
         { return NS_ERROR_NOT_IMPLEMENTED; }
     NS_IMETHOD Reset()
         { /* (InitializeWithSurface) */ return NS_ERROR_NOT_IMPLEMENTED; }
-    NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter f);
+    NS_IMETHOD Render(gfxContext *ctx,
+                      gfxPattern::GraphicsFilter f,
+                      PRUint32 aFlags = RenderFlagPremultAlpha);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream);
     NS_IMETHOD GetThebesSurface(gfxASurface **surface);
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot()
         { return nsnull; }
 
     NS_IMETHOD SetIsOpaque(bool b) { return NS_OK; };
--- a/content/canvas/src/nsCanvasRenderingContext2D.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2D.cpp
@@ -346,17 +346,19 @@ public:
     nsresult Redraw();
 
     // nsICanvasRenderingContextInternal
     NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
     NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
     void Initialize(nsIDocShell *shell, PRInt32 width, PRInt32 height);
     NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height);
     bool EnsureSurface();
-    NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
+    NS_IMETHOD Render(gfxContext *ctx,
+                      gfxPattern::GraphicsFilter aFilter,
+                      PRUint32 aFlags = RenderFlagPremultAlpha);
     NS_IMETHOD GetInputStream(const char* aMimeType,
                               const PRUnichar* aEncoderOptions,
                               nsIInputStream **aStream);
     NS_IMETHOD GetThebesSurface(gfxASurface **surface);
     mozilla::TemporaryRef<mozilla::gfx::SourceSurface> GetSurfaceSnapshot()
         { return nsnull; }
 
     NS_IMETHOD SetIsOpaque(bool isOpaque);
@@ -1273,17 +1275,17 @@ nsCanvasRenderingContext2D::SetIsIPC(boo
          */
         return SetDimensions(mWidth, mHeight);
     }
 
     return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCanvasRenderingContext2D::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter)
+nsCanvasRenderingContext2D::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter, PRUint32 aFlags)
 {
     nsresult rv = NS_OK;
 
     if (!EnsureSurface())
         return NS_ERROR_FAILURE;
 
     nsRefPtr<gfxPattern> pat = new gfxPattern(mSurface);
 
@@ -1298,16 +1300,24 @@ nsCanvasRenderingContext2D::Render(gfxCo
     // pixel alignment for this stuff!
     ctx->NewPath();
     ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
     ctx->Fill();
 
     if (mOpaque)
         ctx->SetOperator(op);
 
+    if (!(aFlags & RenderFlagPremultAlpha)) {
+        nsRefPtr<gfxASurface> curSurface = ctx->CurrentSurface();
+        nsRefPtr<gfxImageSurface> gis = curSurface->GetAsImageSurface();
+        NS_ABORT_IF_FALSE(gis, "If non-premult alpha, must be able to get image surface!");
+
+        gfxUtils::UnpremultiplyImageSurface(gis);
+    }
+
     return rv;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2D::GetInputStream(const char *aMimeType,
                                            const PRUnichar *aEncoderOptions,
                                            nsIInputStream **aStream)
 {
--- a/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
+++ b/content/canvas/src/nsCanvasRenderingContext2DAzure.cpp
@@ -401,17 +401,19 @@ public:
   nsresult Redraw();
 
   // nsICanvasRenderingContextInternal
   NS_IMETHOD SetCanvasElement(nsHTMLCanvasElement* aParentCanvas);
   NS_IMETHOD SetDimensions(PRInt32 width, PRInt32 height);
   NS_IMETHOD InitializeWithSurface(nsIDocShell *shell, gfxASurface *surface, PRInt32 width, PRInt32 height)
   { return NS_ERROR_NOT_IMPLEMENTED; }
 
-  NS_IMETHOD Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter);
+  NS_IMETHOD Render(gfxContext *ctx,
+                    gfxPattern::GraphicsFilter aFilter,
+                    PRUint32 aFlags = RenderFlagPremultAlpha);
   NS_IMETHOD GetInputStream(const char* aMimeType,
                             const PRUnichar* aEncoderOptions,
                             nsIInputStream **aStream);
   NS_IMETHOD GetThebesSurface(gfxASurface **surface);
 
   TemporaryRef<SourceSurface> GetSurfaceSnapshot()
   { return mTarget ? mTarget->Snapshot() : nsnull; }
 
@@ -1377,17 +1379,17 @@ nsCanvasRenderingContext2DAzure::SetIsIP
       */
     return SetDimensions(mWidth, mHeight);
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsCanvasRenderingContext2DAzure::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter)
+nsCanvasRenderingContext2DAzure::Render(gfxContext *ctx, gfxPattern::GraphicsFilter aFilter, PRUint32 aFlags)
 {
   nsresult rv = NS_OK;
 
   if (!mValid || !mTarget) {
     return NS_ERROR_FAILURE;
   }
 
   nsRefPtr<gfxASurface> surface;
@@ -1409,16 +1411,24 @@ nsCanvasRenderingContext2DAzure::Render(
   // pixel alignment for this stuff!
   ctx->NewPath();
   ctx->PixelSnappedRectangleAndSetPattern(gfxRect(0, 0, mWidth, mHeight), pat);
   ctx->Fill();
 
   if (mOpaque)
       ctx->SetOperator(op);
 
+  if (!(aFlags & RenderFlagPremultAlpha)) {
+      nsRefPtr<gfxASurface> curSurface = ctx->CurrentSurface();
+      nsRefPtr<gfxImageSurface> gis = curSurface->GetAsImageSurface();
+      NS_ABORT_IF_FALSE(gis, "If non-premult alpha, must be able to get image surface!");
+
+      gfxUtils::UnpremultiplyImageSurface(gis);
+  }
+
   return rv;
 }
 
 NS_IMETHODIMP
 nsCanvasRenderingContext2DAzure::GetInputStream(const char *aMimeType,
                                                 const PRUnichar *aEncoderOptions,
                                                 nsIInputStream **aStream)
 {
--- a/content/canvas/test/webgl/failing_tests_linux.txt
+++ b/content/canvas/test/webgl/failing_tests_linux.txt
@@ -1,7 +1,6 @@
-conformance/context/premultiplyalpha-test.html
 conformance/misc/uninitialized-test.html
 conformance/programs/gl-get-active-attribute.html
 conformance/textures/texture-mips.html
 conformance/uniforms/gl-uniform-bool.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/renderbuffers/framebuffer-object-attachment.html
\ No newline at end of file
--- a/content/canvas/test/webgl/failing_tests_mac.txt
+++ b/content/canvas/test/webgl/failing_tests_mac.txt
@@ -1,6 +1,5 @@
-conformance/context/premultiplyalpha-test.html
 conformance/glsl/misc/glsl-function-nodes.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/programs/program-test.html
 conformance/textures/texture-mips.html
 conformance/textures/texture-npot.html
--- a/content/canvas/test/webgl/failing_tests_windows.txt
+++ b/content/canvas/test/webgl/failing_tests_windows.txt
@@ -1,6 +1,5 @@
-conformance/context/premultiplyalpha-test.html
 conformance/glsl/functions/glsl-function-atan.html
 conformance/glsl/functions/glsl-function-atan-xy.html
 conformance/more/conformance/quickCheckAPI-S_V.html
 conformance/glsl/misc/struct-nesting-under-maximum.html
 conformance/more/functions/uniformfArrayLen1.html
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -527,22 +527,22 @@ nsEventListenerManager::AddScriptEventLi
                                  nsnull);
         return NS_OK;
       }
     }
   }
 
   // This might be the first reference to this language in the global
   // We must init the language before we attempt to fetch its context.
-  if (NS_FAILED(global->EnsureScriptEnvironment(aLanguage))) {
+  if (NS_FAILED(global->EnsureScriptEnvironment())) {
     NS_WARNING("Failed to setup script environment for this language");
     // but fall through and let the inevitable failure below handle it.
   }
 
-  nsIScriptContext* context = global->GetScriptContext(aLanguage);
+  nsIScriptContext* context = global->GetScriptContext();
   NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
 
   JSObject* scope = global->GetGlobalJSObject();
 
   nsListenerStruct *ls;
   rv = SetJSEventListener(context, scope, aName, nsnull,
                           aPermitUntrustedEvents, &ls);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/html/content/public/nsHTMLCanvasElement.h
+++ b/content/html/content/public/nsHTMLCanvasElement.h
@@ -137,17 +137,19 @@ public:
    * across its entire area.
    */
   bool GetIsOpaque();
 
   /*
    * nsICanvasElementExternal -- for use outside of content/layout
    */
   NS_IMETHOD_(nsIntSize) GetSizeExternal();
-  NS_IMETHOD RenderContextsExternal(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter);
+  NS_IMETHOD RenderContextsExternal(gfxContext *aContext,
+                                    gfxPattern::GraphicsFilter aFilter,
+                                    PRUint32 aFlags = RenderFlagPremultAlpha);
 
   virtual bool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
   nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, PRInt32 aModType) const;
 
   // SetAttr override.  C++ is stupid, so have to override both
--- a/content/html/content/src/nsHTMLCanvasElement.cpp
+++ b/content/html/content/src/nsHTMLCanvasElement.cpp
@@ -789,22 +789,22 @@ nsHTMLCanvasElement::MarkContextClean()
 
 NS_IMETHODIMP_(nsIntSize)
 nsHTMLCanvasElement::GetSizeExternal()
 {
   return GetWidthHeight();
 }
 
 NS_IMETHODIMP
-nsHTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter)
+nsHTMLCanvasElement::RenderContextsExternal(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, PRUint32 aFlags)
 {
   if (!mCurrentContext)
     return NS_OK;
 
-  return mCurrentContext->Render(aContext, aFilter);
+  return mCurrentContext->Render(aContext, aFilter, aFlags);
 }
 
 nsresult NS_NewCanvasRenderingContext2DThebes(nsIDOMCanvasRenderingContext2D** aResult);
 nsresult NS_NewCanvasRenderingContext2DAzure(nsIDOMCanvasRenderingContext2D** aResult);
 
 nsresult
 NS_NewCanvasRenderingContext2D(nsIDOMCanvasRenderingContext2D** aResult)
 {
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -850,16 +850,20 @@ nsHTMLInputElement::AfterSetAttr(PRInt32
       UpdateTooLongValidityState();
     } else if (aName == nsGkAtoms::pattern) {
       UpdatePatternMismatchValidityState();
     } else if (aName == nsGkAtoms::multiple) {
       UpdateTypeMismatchValidityState();
     }
 
     UpdateEditableState(aNotify);
+    nsTextEditorState *state = GetEditorState();
+    if (state) {
+      state->UpdateEditableState(aNotify);
+    }
     UpdateState(aNotify);
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName,
                                                 aValue, aNotify);
 }
 
 // nsIDOMHTMLInputElement
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -1288,16 +1288,17 @@ nsHTMLTextAreaElement::AfterSetAttr(PRIn
         UpdateBarredFromConstraintValidation();
       }
     } else if (aName == nsGkAtoms::maxlength) {
       UpdateTooLongValidityState();
     }
 
     if (aName == nsGkAtoms::readonly) {
       UpdateEditableState(aNotify);
+      mState->UpdateEditableState(aNotify);
     }
     UpdateState(aNotify);
   }
 
   return nsGenericHTMLFormElement::AfterSetAttr(aNameSpaceID, aName, aValue,
                                                 aNotify);
 }
 
--- a/content/html/content/src/nsTextEditorState.h
+++ b/content/html/content/src/nsTextEditorState.h
@@ -39,16 +39,17 @@
 #ifndef nsTextEditorState_h__
 #define nsTextEditorState_h__
 
 #include "nsAutoPtr.h"
 #include "nsString.h"
 #include "nsITextControlElement.h"
 #include "nsITextControlFrame.h"
 #include "nsCycleCollectionParticipant.h"
+#include "nsIContent.h"
 
 class nsTextInputListener;
 class nsTextControlFrame;
 class nsTextInputSelectionImpl;
 class nsAnonDivObserver;
 class nsISelectionController;
 class nsFrameSelection;
 class nsIEditor;
@@ -233,16 +234,22 @@ public:
 
   bool IsSelectionCached() const { return mSelectionCached; }
   SelectionProperties& GetSelectionProperties() {
     return mSelectionProperties;
   }
   void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
   bool HasNeverInitializedBefore() const { return !mEverInited; }
 
+  void UpdateEditableState(bool aNotify) {
+    if (mRootNode) {
+      mRootNode->UpdateEditableState(aNotify);
+    }
+  }
+
 private:
   friend class RestoreSelectionState;
 
   // not copy constructible
   nsTextEditorState(const nsTextEditorState&);
   // not assignable
   void operator= (const nsTextEditorState&);
 
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2676,32 +2676,32 @@ nsHTMLDocument::EditingStateChanged()
     nsCOMPtr<nsIURI> uri;
     rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("resource://gre/res/contenteditable.css"));
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsRefPtr<nsCSSStyleSheet> sheet;
     rv = LoadChromeSheetSync(uri, true, getter_AddRefs(sheet));
     NS_ENSURE_TRUE(sheet, rv);
 
-    rv = agentSheets.AppendObject(sheet);
-    NS_ENSURE_SUCCESS(rv, rv);
+    bool result = agentSheets.AppendObject(sheet);
+    NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
 
     // Should we update the editable state of all the nodes in the document? We
     // need to do this when the designMode value changes, as that overrides
     // specific states on the elements.
     if (designMode) {
       // designMode is being turned on (overrides contentEditable).
       rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("resource://gre/res/designmode.css"));
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = LoadChromeSheetSync(uri, true, getter_AddRefs(sheet));
       NS_ENSURE_TRUE(sheet, rv);
 
-      rv = agentSheets.AppendObject(sheet);
-      NS_ENSURE_SUCCESS(rv, rv);
+      result = agentSheets.AppendObject(sheet);
+      NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
 
       // Disable scripting and plugins.
       rv = editSession->DisableJSAndPlugins(window);
       NS_ENSURE_SUCCESS(rv, rv);
 
       updateState = true;
       spellRecheckAll = oldState == eContentEditable;
     }
--- a/content/xbl/src/nsXBLDocumentInfo.cpp
+++ b/content/xbl/src/nsXBLDocumentInfo.cpp
@@ -73,18 +73,18 @@ class nsXBLDocGlobalObject : public nsIS
 {
 public:
   nsXBLDocGlobalObject(nsIScriptGlobalObjectOwner *aGlobalObjectOwner);
 
   // nsISupports interface
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   
   // nsIScriptGlobalObject methods
-  virtual nsresult EnsureScriptEnvironment(PRUint32 aLangID);
-  virtual nsresult SetScriptContext(PRUint32 lang_id, nsIScriptContext *aContext);
+  virtual nsresult EnsureScriptEnvironment();
+  virtual nsresult SetScriptContext(nsIScriptContext *aContext);
 
   virtual nsIScriptContext *GetContext();
   virtual JSObject *GetGlobalJSObject();
   virtual void OnFinalize(JSObject* aObject);
   virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
 
   // nsIScriptObjectPrincipal methods
   virtual nsIPrincipal* GetPrincipal();
@@ -98,20 +98,20 @@ public:
   void ClearGlobalObjectOwner();
 
   void UnmarkScriptContext();
 
 protected:
   virtual ~nsXBLDocGlobalObject();
 
   void SetContext(nsIScriptContext *aContext);
-  nsIScriptContext *GetScriptContext(PRUint32 language);
+  nsIScriptContext *GetScriptContext();
 
   nsCOMPtr<nsIScriptContext> mScriptContext;
-  JSObject *mJSObject;    // XXX JS language rabies bigotry badness
+  JSObject *mJSObject;
 
   nsIScriptGlobalObjectOwner* mGlobalObjectOwner; // weak reference
   static JSClass gSharedGlobalClass;
 };
 
 JSBool
 nsXBLDocGlobalObject::doCheckAccess(JSContext *cx, JSObject *obj, jsid id, PRUint32 accessType)
 {
@@ -277,50 +277,44 @@ nsXBLDocGlobalObject::SetContext(nsIScri
   NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Script Language's InitContext failed");
   aScriptContext->SetGCOnDestruction(false);
   aScriptContext->DidInitializeContext();
   // and we set up our global manually
   mScriptContext = aScriptContext;
 }
 
 nsresult
-nsXBLDocGlobalObject::SetScriptContext(PRUint32 lang_id, nsIScriptContext *aContext)
+nsXBLDocGlobalObject::SetScriptContext(nsIScriptContext *aContext)
 {
-  NS_ASSERTION(lang_id == nsIProgrammingLanguage::JAVASCRIPT, "Only JS allowed!");
   SetContext(aContext);
   return NS_OK;
 }
 
 nsIScriptContext *
-nsXBLDocGlobalObject::GetScriptContext(PRUint32 language)
+nsXBLDocGlobalObject::GetScriptContext()
 {
-  // This impl still assumes JS
-  NS_ENSURE_TRUE(language==nsIProgrammingLanguage::JAVASCRIPT, nsnull);
   return GetContext();
 }
 
 nsresult
-nsXBLDocGlobalObject::EnsureScriptEnvironment(PRUint32 aLangID)
+nsXBLDocGlobalObject::EnsureScriptEnvironment()
 {
-  if (aLangID != nsIProgrammingLanguage::JAVASCRIPT) {
-    NS_WARNING("XBL still JS only");
-    return NS_ERROR_INVALID_ARG;
-  }
   if (mScriptContext)
     return NS_OK; // already initialized for this lang
   nsCOMPtr<nsIDOMScriptObjectFactory> factory = do_GetService(kDOMScriptObjectFactoryCID);
   NS_ENSURE_TRUE(factory, NS_OK);
 
   nsresult rv;
 
   nsCOMPtr<nsIScriptRuntime> scriptRuntime;
-  rv = NS_GetScriptRuntimeByID(aLangID, getter_AddRefs(scriptRuntime));
+  rv = NS_GetScriptRuntimeByID(nsIProgrammingLanguage::JAVASCRIPT,
+                               getter_AddRefs(scriptRuntime));
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIScriptContext> newCtx = scriptRuntime->CreateContext();
-  rv = SetScriptContext(aLangID, newCtx);
+  rv = SetScriptContext(newCtx);
 
   JSContext *cx = mScriptContext->GetNativeContext();
   JSAutoRequest ar(cx);
 
   // nsJSEnvironment set the error reporter to NS_ScriptErrorReporter so
   // we must apparently override that with our own (although it isn't clear 
   // why - see bug 339647)
   JS_SetErrorReporter(cx, XBL_ProtoErrorReporter);
@@ -342,17 +336,17 @@ nsXBLDocGlobalObject::EnsureScriptEnviro
 }
 
 nsIScriptContext *
 nsXBLDocGlobalObject::GetContext()
 {
   // This whole fragile mess is predicated on the fact that
   // GetContext() will be called before GetScriptObject() is.
   if (! mScriptContext) {
-    nsresult rv = EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
+    nsresult rv = EnsureScriptEnvironment();
     // JS is builtin so we make noise if it fails to initialize.
     NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to setup JS!?");
     NS_ENSURE_SUCCESS(rv, nsnull);
     NS_ASSERTION(mScriptContext, "Failed to find a script context!?");
   }
   return mScriptContext;
 }
 
@@ -558,17 +552,17 @@ nsXBLDocumentInfo::nsXBLDocumentInfo(nsI
   }
 }
 
 nsXBLDocumentInfo::~nsXBLDocumentInfo()
 {
   /* destructor code */
   if (mGlobalObject) {
     // remove circular reference
-    mGlobalObject->SetScriptContext(nsIProgrammingLanguage::JAVASCRIPT, nsnull);
+    mGlobalObject->SetScriptContext(nsnull);
     mGlobalObject->ClearGlobalObjectOwner(); // just in case
   }
   if (mBindingTable) {
     NS_DROP_JS_OBJECTS(this, nsXBLDocumentInfo);
     delete mBindingTable;
   }
 }
 
--- a/content/xbl/src/nsXBLPrototypeHandler.cpp
+++ b/content/xbl/src/nsXBLPrototypeHandler.cpp
@@ -296,18 +296,17 @@ nsXBLPrototypeHandler::ExecuteHandler(ns
     }
 
     boundGlobal = boundDocument->GetScopeObject();
   }
 
   if (!boundGlobal)
     return NS_OK;
 
-  nsIScriptContext *boundContext =
-    boundGlobal->GetScriptContext(nsIProgrammingLanguage::JAVASCRIPT);
+  nsIScriptContext *boundContext = boundGlobal->GetScriptContext();
   if (!boundContext)
     return NS_OK;
 
   nsScriptObjectHolder<JSObject> handler(boundContext);
   nsISupports *scriptTarget;
 
   if (winRoot) {
     scriptTarget = boundGlobal;
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -766,17 +766,17 @@ nsScriptEventHandlerOwnerTearoff::Compil
         nsCOMPtr<nsIScriptGlobalObjectOwner> globalOwner;
         rv = xuldoc->GetScriptGlobalObjectOwner(getter_AddRefs(globalOwner));
         NS_ENSURE_SUCCESS(rv, rv);
         NS_ENSURE_TRUE(globalOwner, NS_ERROR_UNEXPECTED);
 
         nsIScriptGlobalObject* global = globalOwner->GetScriptGlobalObject();
         NS_ENSURE_TRUE(global, NS_ERROR_UNEXPECTED);
 
-        context = global->GetScriptContext(aContext->GetScriptTypeID());
+        context = global->GetScriptContext();
         // It could be possible the language has been setup on aContext but
         // not on the global - we don't demand-create language contexts on the
         // nsGlobalWindow
         NS_ASSERTION(context,
                      "Failed to get a language context from the global!?");
         NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
     }
     else {
@@ -2938,18 +2938,17 @@ nsXULPrototypeScript::~nsXULPrototypeScr
     UnlinkJSObjects();
 }
 
 nsresult
 nsXULPrototypeScript::Serialize(nsIObjectOutputStream* aStream,
                                 nsIScriptGlobalObject* aGlobal,
                                 const nsCOMArray<nsINodeInfo> *aNodeInfos)
 {
-    nsIScriptContext *context = aGlobal->GetScriptContext(
-                                        mScriptObject.mLangID);
+    nsIScriptContext *context = aGlobal->GetScriptContext();
     NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nsnull ||
                  !mScriptObject.mObject,
                  "script source still loading when serializing?!");
     if (!mScriptObject.mObject)
         return NS_ERROR_FAILURE;
 
     // Write basic prototype data
     nsresult rv;
@@ -3017,18 +3016,17 @@ nsXULPrototypeScript::Deserialize(nsIObj
     NS_ASSERTION(!mSrcLoading || mSrcLoadWaiters != nsnull ||
                  !mScriptObject.mObject,
                  "prototype script not well-initialized when deserializing?!");
 
     // Read basic prototype data
     aStream->Read32(&mLineNo);
     aStream->Read32(&mLangVersion);
 
-    nsIScriptContext *context = aGlobal->GetScriptContext(
-                                            mScriptObject.mLangID);
+    nsIScriptContext *context = aGlobal->GetScriptContext();
     NS_ASSERTION(context != nsnull, "Have no context for deserialization");
     NS_ENSURE_TRUE(context, NS_ERROR_UNEXPECTED);
     nsScriptObjectHolder<JSScript> newScriptObject(context);
     rv = context->Deserialize(aStream, newScriptObject);
     if (NS_FAILED(rv)) {
         NS_WARNING("Language deseralization failed");
         return rv;
     }
@@ -3146,17 +3144,17 @@ nsXULPrototypeScript::Compile(const PRUn
     nsIScriptContext *context;
 
     {
         nsIScriptGlobalObject* global = aGlobalOwner->GetScriptGlobalObject();
         NS_ASSERTION(global != nsnull, "prototype doc has no script global");
         if (! global)
             return NS_ERROR_UNEXPECTED;
 
-        context = global->GetScriptContext(mScriptObject.mLangID);
+        context = global->GetScriptContext();
         NS_ASSERTION(context != nsnull, "no context for script global");
         if (! context)
             return NS_ERROR_UNEXPECTED;
     }
 
     nsCAutoString urlspec;
     nsContentUtils::GetWrapperSafeScriptFilename(aDocument, aURI, urlspec);
 
--- a/content/xul/document/src/nsXULDocument.cpp
+++ b/content/xul/document/src/nsXULDocument.cpp
@@ -3548,19 +3548,18 @@ nsXULDocument::OnStreamComplete(nsIStrea
                 // Ignore the return value, as we don't need to propagate
                 // a failure to write to the FastLoad file, because this
                 // method aborts that whole process on error.
                 nsIScriptGlobalObject* global =
                     mCurrentPrototype->GetScriptGlobalObject();
 
                 NS_ASSERTION(global != nsnull, "master prototype w/o global?!");
                 if (global) {
-                    PRUint32 stid = scriptProto->mScriptObject.mLangID;
                     nsIScriptContext *scriptContext = \
-                          global->GetScriptContext(stid);
+                          global->GetScriptContext();
                     NS_ASSERTION(scriptContext != nsnull,
                                  "Failed to get script context for language");
                     if (scriptContext)
                         scriptProto->SerializeOutOfLine(nsnull, global);
                 }
             }
         }
         // ignore any evaluation errors
@@ -3614,24 +3613,23 @@ nsXULDocument::ExecuteScript(nsIScriptCo
 }
 
 nsresult
 nsXULDocument::ExecuteScript(nsXULPrototypeScript *aScript)
 {
     NS_PRECONDITION(aScript != nsnull, "null ptr");
     NS_ENSURE_TRUE(aScript, NS_ERROR_NULL_POINTER);
     NS_ENSURE_TRUE(mScriptGlobalObject, NS_ERROR_NOT_INITIALIZED);
-    PRUint32 stid = aScript->mScriptObject.mLangID;
 
     nsresult rv;
-    rv = mScriptGlobalObject->EnsureScriptEnvironment(stid);
+    rv = mScriptGlobalObject->EnsureScriptEnvironment();
     NS_ENSURE_SUCCESS(rv, rv);
 
     nsCOMPtr<nsIScriptContext> context =
-      mScriptGlobalObject->GetScriptContext(stid);
+      mScriptGlobalObject->GetScriptContext();
     // failure getting a script context is fatal.
     NS_ENSURE_TRUE(context != nsnull, NS_ERROR_UNEXPECTED);
 
     if (aScript->mScriptObject.mObject)
         rv = ExecuteScript(context, aScript->mScriptObject.mObject);
     else
         rv = NS_ERROR_UNEXPECTED;
     return rv;
--- a/content/xul/document/src/nsXULPrototypeDocument.cpp
+++ b/content/xul/document/src/nsXULPrototypeDocument.cpp
@@ -80,20 +80,20 @@ public:
     // nsISupports interface
     NS_DECL_CYCLE_COLLECTING_ISUPPORTS
 
     // nsIScriptGlobalObject methods
     virtual void OnFinalize(JSObject* aObject);
     virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
 
     virtual JSObject* GetGlobalJSObject();
-    virtual nsresult EnsureScriptEnvironment(PRUint32 aLangID);
+    virtual nsresult EnsureScriptEnvironment();
 
-    virtual nsIScriptContext *GetScriptContext(PRUint32 lang);
-    virtual nsresult SetScriptContext(PRUint32 language, nsIScriptContext *ctx);
+    virtual nsIScriptContext *GetScriptContext();
+    virtual nsresult SetScriptContext(nsIScriptContext *ctx);
 
     // nsIScriptObjectPrincipal methods
     virtual nsIPrincipal* GetPrincipal();
 
     NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsXULPDGlobalObject,
                                              nsIScriptGlobalObject)
 
     void ClearGlobalObjectOwner();
@@ -678,20 +678,18 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(nsXULPDG
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsXULPDGlobalObject)
 
 //----------------------------------------------------------------------
 //
 // nsIScriptGlobalObject methods
 //
 
 nsresult
-nsXULPDGlobalObject::SetScriptContext(PRUint32 lang_id, nsIScriptContext *aScriptContext)
+nsXULPDGlobalObject::SetScriptContext(nsIScriptContext *aScriptContext)
 {
-  NS_ABORT_IF_FALSE(lang_id == nsIProgrammingLanguage::JAVASCRIPT,
-                    "We don't support this language ID");
   // almost a clone of nsGlobalWindow
   if (!aScriptContext) {
     NS_WARNING("Possibly early removal of script object, see bug #41608");
   } else {
     // should probably assert the context is clean???
     aScriptContext->WillInitializeContext();
     nsresult rv = aScriptContext->InitContext();
     NS_ENSURE_SUCCESS(rv, rv);
@@ -708,20 +706,18 @@ nsXULPDGlobalObject::SetScriptContext(PR
     NS_ASSERTION(global, "GetNativeGlobal returned NULL!");
   }
   mContext = aScriptContext;
   mJSObject = global;
   return NS_OK;
 }
 
 nsresult
-nsXULPDGlobalObject::EnsureScriptEnvironment(PRUint32 lang_id)
+nsXULPDGlobalObject::EnsureScriptEnvironment()
 {
-  NS_ABORT_IF_FALSE(lang_id == nsIProgrammingLanguage::JAVASCRIPT,
-                    "We don't support this language ID");
   if (mContext) {
     return NS_OK;
   }
   NS_ASSERTION(!mJSObject, "Have global without context?");
 
   nsCOMPtr<nsIScriptRuntime> languageRuntime;
   nsresult rv = NS_GetScriptRuntimeByID(nsIProgrammingLanguage::JAVASCRIPT,
                                         getter_AddRefs(languageRuntime));
@@ -747,33 +743,31 @@ nsXULPDGlobalObject::EnsureScriptEnviron
 
     // Add an owning reference from JS back to us. This'll be
     // released when the JSObject is finalized.
     ::JS_SetPrivate(newGlob, this);
     NS_ADDREF(this);
   }
 
   NS_ENSURE_SUCCESS(rv, NS_OK);
-  rv = SetScriptContext(lang_id, ctxNew);
+  rv = SetScriptContext(ctxNew);
   NS_ENSURE_SUCCESS(rv, NS_OK);
   return NS_OK;
 }
 
 nsIScriptContext*
-nsXULPDGlobalObject::GetScriptContext(PRUint32 lang_id)
+nsXULPDGlobalObject::GetScriptContext()
 {
-  NS_ABORT_IF_FALSE(lang_id == nsIProgrammingLanguage::JAVASCRIPT,
-                    "We don't support this language ID");
   // This global object creates a context on demand - do that now.
-  nsresult rv = EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
+  nsresult rv = EnsureScriptEnvironment();
   if (NS_FAILED(rv)) {
     NS_ERROR("Failed to setup script language");
     return NULL;
   }
-  // Note that EnsureScriptEnvironment has validated lang_id
+
   return mContext;
 }
 
 JSObject*
 nsXULPDGlobalObject::GetGlobalJSObject()
 {
   return mJSObject;
 }
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -10845,21 +10845,19 @@ nsDocShell::EnsureScriptEnvironment()
     factory->NewScriptGlobalObject(mItemType == typeChrome,
                                    isModalContentWindow,
                                    getter_AddRefs(mScriptGlobal));
     NS_ENSURE_TRUE(mScriptGlobal, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(mScriptGlobal));
     win->SetDocShell(static_cast<nsIDocShell *>(this));
 
-    // Ensure the script object is set to run javascript - other languages
-    // setup on demand.
-    // XXXmarkh - should this be setup to run the default language for this doc?
+    // Ensure the script object is set up to run script.
     nsresult rv;
-    rv = mScriptGlobal->EnsureScriptEnvironment(nsIProgrammingLanguage::JAVASCRIPT);
+    rv = mScriptGlobal->EnsureScriptEnvironment();
     NS_ENSURE_SUCCESS(rv, rv);
 
     return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsDocShell::EnsureEditorData()
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -521,16 +521,17 @@ using mozilla::dom::indexedDB::IDBWrappe
 #ifdef MOZ_B2G_RIL
 #include "Telephony.h"
 #include "TelephonyCall.h"
 #include "CallEvent.h"
 #endif
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothAdapter.h"
+#include "BluetoothDevice.h"
 #endif
 
 #include "DOMError.h"
 #include "DOMRequest.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -1616,16 +1617,18 @@ static nsDOMClassInfoData sClassInfoData
                            EVENTTARGET_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(CallEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
 #ifdef MOZ_B2G_BT
   NS_DEFINE_CLASSINFO_DATA(BluetoothAdapter, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(BluetoothDevice, nsEventTargetSH,
+                           EVENTTARGET_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(DOMRequest, nsEventTargetSH,
                            EVENTTARGET_SCRIPTABLE_FLAGS)
 };
@@ -4369,16 +4372,20 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCallEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEvent)
   DOM_CLASSINFO_MAP_END
 #endif
 
 #ifdef MOZ_B2G_BT
   DOM_CLASSINFO_MAP_BEGIN(BluetoothAdapter, nsIDOMBluetoothAdapter)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothAdapter)
+  DOM_CLASSINFO_MAP_END  
+  
+  DOM_CLASSINFO_MAP_BEGIN(BluetoothDevice, nsIDOMBluetoothDevice)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMBluetoothDevice)
   DOM_CLASSINFO_MAP_END
 #endif
 
   DOM_CLASSINFO_MAP_BEGIN(DOMError, nsIDOMDOMError)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(DOMRequest, nsIDOMDOMRequest)
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -536,12 +536,13 @@ DOMCI_CLASS(CustomEvent)
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(Telephony)
 DOMCI_CLASS(TelephonyCall)
 DOMCI_CLASS(CallEvent)
 #endif
 
 #ifdef MOZ_B2G_BT
 DOMCI_CLASS(BluetoothAdapter)
+DOMCI_CLASS(BluetoothDevice)
 #endif
 
 DOMCI_CLASS(DOMError)
 DOMCI_CLASS(DOMRequest)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1567,20 +1567,18 @@ nsGlobalWindow::UnmarkGrayTimers()
   }
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptGlobalObject
 //*****************************************************************************
 
 nsresult
-nsGlobalWindow::SetScriptContext(PRUint32 lang_id, nsIScriptContext *aScriptContext)
-{
-  NS_ASSERTION(lang_id == nsIProgrammingLanguage::JAVASCRIPT,
-               "We don't support this language ID");
+nsGlobalWindow::SetScriptContext(nsIScriptContext *aScriptContext)
+{
   NS_ASSERTION(IsOuterWindow(), "Uh, SetScriptContext() called on inner window!");
 
   NS_ASSERTION(!aScriptContext || !mContext, "Bad call to SetContext()!");
 
   if (aScriptContext) {
     // should probably assert the context is clean???
     aScriptContext->WillInitializeContext();
 
@@ -1596,53 +1594,49 @@ nsGlobalWindow::SetScriptContext(PRUint3
     }
   }
 
   mContext = aScriptContext;
   return NS_OK;
 }
 
 nsresult
-nsGlobalWindow::EnsureScriptEnvironment(PRUint32 aLangID)
-{
-  NS_ASSERTION(aLangID == nsIProgrammingLanguage::JAVASCRIPT,
-               "We don't support this language ID");
-  FORWARD_TO_OUTER(EnsureScriptEnvironment, (aLangID), NS_ERROR_NOT_INITIALIZED);
+nsGlobalWindow::EnsureScriptEnvironment()
+{
+  FORWARD_TO_OUTER(EnsureScriptEnvironment, (), NS_ERROR_NOT_INITIALIZED);
 
   if (mJSObject)
       return NS_OK;
 
   NS_ASSERTION(!GetCurrentInnerWindowInternal(),
                "mJSObject is null, but we have an inner window?");
 
   nsCOMPtr<nsIScriptRuntime> scriptRuntime;
-  nsresult rv = NS_GetScriptRuntimeByID(aLangID, getter_AddRefs(scriptRuntime));
+  nsresult rv = NS_GetScriptRuntimeByID(nsIProgrammingLanguage::JAVASCRIPT,
+                                        getter_AddRefs(scriptRuntime));
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIScriptContext> context = scriptRuntime->CreateContext();
-  return SetScriptContext(aLangID, context);
+  return SetScriptContext(context);
 }
 
 nsIScriptContext *
-nsGlobalWindow::GetScriptContext(PRUint32 lang)
-{
-  NS_ASSERTION(lang == nsIProgrammingLanguage::JAVASCRIPT,
-               "We don't support this language ID");
-
-  FORWARD_TO_OUTER(GetScriptContext, (lang), nsnull);
+nsGlobalWindow::GetScriptContext()
+{
+  FORWARD_TO_OUTER(GetScriptContext, (), nsnull);
   return mContext;
 }
 
 nsIScriptContext *
 nsGlobalWindow::GetContext()
 {
   FORWARD_TO_OUTER(GetContext, (), nsnull);
 
   // check GetContext is indeed identical to GetScriptContext()
-  NS_ASSERTION(mContext == GetScriptContext(nsIProgrammingLanguage::JAVASCRIPT),
+  NS_ASSERTION(mContext == GetScriptContext(),
                "GetContext confused?");
   return mContext;
 }
 
 JSObject *
 nsGlobalWindow::GetGlobalJSObject()
 {
   return FastGetGlobalJSObject();
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -312,23 +312,23 @@ public:
   // nsIScriptGlobalObject
   virtual nsIScriptContext *GetContext();
   virtual JSObject *GetGlobalJSObject();
   JSObject *FastGetGlobalJSObject()
   {
     return mJSObject;
   }
 
-  virtual nsresult EnsureScriptEnvironment(PRUint32 aLangID);
+  virtual nsresult EnsureScriptEnvironment();
 
-  virtual nsIScriptContext *GetScriptContext(PRUint32 lang);
+  virtual nsIScriptContext *GetScriptContext();
 
   // Set a new script language context for this global.  The native global
   // for the context is created by the context's GetNativeGlobal() method.
-  virtual nsresult SetScriptContext(PRUint32 lang, nsIScriptContext *aContext);
+  virtual nsresult SetScriptContext(nsIScriptContext *aContext);
   
   virtual void OnFinalize(JSObject* aObject);
   virtual void SetScriptsEnabled(bool aEnabled, bool aFireTimeouts);
 
   virtual bool IsBlackForCC();
 
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal();
--- a/dom/base/nsIScriptGlobalObject.h
+++ b/dom/base/nsIScriptGlobalObject.h
@@ -117,34 +117,34 @@ public:
    * Ensure that the script global object is initialized for working with the
    * specified script language ID.  This will set up the nsIScriptContext
    * and 'script global' for that language, allowing these to be fetched
    * and manipulated.
    * @return NS_OK if successful; error conditions include that the language
    * has not been registered, as well as 'normal' errors, such as
    * out-of-memory
    */
-  virtual nsresult EnsureScriptEnvironment(PRUint32 aLangID) = 0;
+  virtual nsresult EnsureScriptEnvironment() = 0;
   /**
    * Get a script context (WITHOUT added reference) for the specified language.
    */
-  virtual nsIScriptContext *GetScriptContext(PRUint32 lang) = 0;
+  virtual nsIScriptContext *GetScriptContext() = 0;
   
   virtual JSObject* GetGlobalJSObject() = 0;
 
   virtual nsIScriptContext *GetContext() {
-        return GetScriptContext(nsIProgrammingLanguage::JAVASCRIPT);
+        return GetScriptContext();
   }
 
   /**
    * Set a new language context for this global.  The native global for the
    * context is created by the context's GetNativeGlobal() method.
    */
 
-  virtual nsresult SetScriptContext(PRUint32 lang, nsIScriptContext *aContext) = 0;
+  virtual nsresult SetScriptContext(nsIScriptContext *aContext) = 0;
 
   /**
    * Called when the global script for a language is finalized, typically as
    * part of its GC process.  By the time this call is made, the
    * nsIScriptContext for the language has probably already been removed.
    * After this call, the passed object is dead - which should generally be the
    * same object the global is using for a global for that language.
    */
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -138,17 +138,17 @@ nsJSUtils::GetStaticScriptGlobal(JSConte
 
 nsIScriptContext *
 nsJSUtils::GetStaticScriptContext(JSContext* aContext, JSObject* aObj)
 {
   nsIScriptGlobalObject *nativeGlobal = GetStaticScriptGlobal(aContext, aObj);
   if (!nativeGlobal)
     return nsnull;
 
-  return nativeGlobal->GetScriptContext(nsIProgrammingLanguage::JAVASCRIPT);
+  return nativeGlobal->GetScriptContext();
 }
 
 nsIScriptGlobalObject *
 nsJSUtils::GetDynamicScriptGlobal(JSContext* aContext)
 {
   nsIScriptContext *scriptCX = GetDynamicScriptContext(aContext);
   if (!scriptCX)
     return nsnull;
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -0,0 +1,154 @@
+/* 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 "BluetoothDevice.h"
+#include "nsDOMClassInfo.h"
+
+USING_BLUETOOTH_NAMESPACE
+
+DOMCI_DATA(BluetoothDevice, BluetoothDevice)
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(BluetoothDevice)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BluetoothDevice,
+                                                  nsDOMEventTargetHelper)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(propertychanged)
+  NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(disconnectrequested)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BluetoothDevice,
+                                                nsDOMEventTargetHelper)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(propertychanged)
+  NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(disconnectrequested)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BluetoothDevice)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMBluetoothDevice)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMBluetoothDevice)
+  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(BluetoothDevice)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(BluetoothDevice, nsDOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(BluetoothDevice, nsDOMEventTargetHelper)
+
+BluetoothDevice::BluetoothDevice()
+{
+}
+
+nsresult
+BluetoothDevice::GetAdapter(nsAString& aAdapter)
+{
+  aAdapter = mAdapter;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetAddress(nsAString& aAddress)
+{
+  aAddress = mAddress;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetName(nsAString& aName)
+{
+  aName = mName;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetClass(PRUint32* aClass)
+{
+  *aClass = mClass;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetConnected(bool* aConnected)
+{
+  *aConnected = mConnected;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetPaired(bool* aPaired)
+{
+  *aPaired = mPaired;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetLegacyPairing(bool* aLegacyPairing)
+{
+  *aLegacyPairing = mLegacyPairing;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetTrusted(bool* aTrusted)
+{
+  *aTrusted = mTrusted;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::SetTrusted(bool aTrusted)
+{
+  mTrusted = aTrusted;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetAlias(nsAString& aAlias)
+{
+  aAlias = mAlias;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::SetAlias(const nsAString& aAlias)
+{
+  mAlias = aAlias;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetUuids(jsval* aUuids)
+{
+  //TODO: convert mUuids to jsval and assign to aUuids;
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::Disconnect()
+{
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::GetProperties(jsval* aProperties)
+{
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::SetProperty(const nsAString& aName, const nsAString& aValue)
+{
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::DiscoverServices(const nsAString& aPattern, jsval* aServices)
+{
+  return NS_OK;
+}
+
+nsresult
+BluetoothDevice::CancelDiscovery()
+{
+  return NS_OK;
+}
+
+NS_IMPL_EVENT_HANDLER(BluetoothDevice, propertychanged)
+NS_IMPL_EVENT_HANDLER(BluetoothDevice, disconnectrequested)
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/BluetoothDevice.h
@@ -0,0 +1,48 @@
+/* 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_bluetooth_bluetoothdevice_h__
+#define mozilla_dom_bluetooth_bluetoothdevice_h__
+
+#include "BluetoothCommon.h"
+#include "nsDOMEventTargetHelper.h"
+#include "nsIDOMBluetoothDevice.h"
+#include "nsString.h"
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+class BluetoothDevice : public nsIDOMBluetoothDevice
+                      , public nsDOMEventTargetHelper
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIDOMBLUETOOTHDEVICE
+
+  NS_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper::)
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BluetoothDevice,
+                                           nsDOMEventTargetHelper)
+
+  BluetoothDevice();
+
+protected:
+  nsString mAdapter;
+  nsString mAddress;
+  PRUint32 mClass;
+  bool mConnected;
+  bool mLegacyPairing;
+  nsString mName;
+  bool mPaired;
+  nsTArray<nsString> mUuids;
+
+  bool mTrusted;
+  nsString mAlias;
+
+  NS_DECL_EVENT_HANDLER(propertychanged)
+  NS_DECL_EVENT_HANDLER(disconnectrequested)
+};
+
+END_BLUETOOTH_NAMESPACE
+
+#endif
--- a/dom/bluetooth/Makefile.in
+++ b/dom/bluetooth/Makefile.in
@@ -14,17 +14,19 @@ LIBRARY_NAME     = dombluetooth_s
 XPIDL_MODULE     = dom_bluetooth
 LIBXUL_LIBRARY   = 1
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/dom/dom-config.mk
 
 CPPSRCS = \
   BluetoothAdapter.cpp \
+  BluetoothDevice.cpp \
   $(NULL)
 
 XPIDLSRCS = \
   nsIDOMNavigatorBluetooth.idl \
   nsIDOMBluetoothAdapter.idl \
+  nsIDOMBluetoothDevice.idl \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/dom/bluetooth/nsIDOMBluetoothDevice.idl
@@ -0,0 +1,30 @@
+/* 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 "nsIDOMEventTarget.idl"
+
+[scriptable, builtinclass, uuid(2da61f89-a7d2-4f7d-9f4c-fb8a99d9add2)]
+interface nsIDOMBluetoothDevice : nsIDOMEventTarget
+{
+  readonly attribute DOMString adapter;
+  readonly attribute DOMString address;
+  readonly attribute unsigned long class;
+  readonly attribute boolean connected;
+  readonly attribute boolean legacyPairing;
+  readonly attribute DOMString name;
+  readonly attribute boolean paired;
+  readonly attribute jsval uuids;
+
+  attribute boolean trusted;
+  attribute DOMString alias;
+
+  attribute nsIDOMEventListener onpropertychanged;
+  attribute nsIDOMEventListener ondisconnectrequested;
+
+  void disconnect();
+  jsval getProperties();
+  void setProperty(in DOMString name, in DOMString value);
+  jsval discoverServices(in DOMString pattern);
+  void cancelDiscovery();
+};
--- a/dom/plugins/test/mochitest/Makefile.in
+++ b/dom/plugins/test/mochitest/Makefile.in
@@ -106,16 +106,17 @@ include $(topsrcdir)/config/rules.mk
   test_npruntime_npnsetexception.html \
   test_NPNVdocumentOrigin.html \
   test_instance_re-parent.html \
   test_instance_unparent1.html \
   test_instance_unparent2.html \
   test_instance_unparent3.html \
   test_pluginstream_referer.html \
   plugin-stream-referer.sjs \
+  test_src_url_change.html \
   $(NULL)
 
 #  test_plugin_scroll_painting.html \ bug 596491
 
 ifeq ($(OS_ARCH),WINNT)
 _MOCHITEST_FILES += \
   test_windowed_invalidate.html \
   $(NULL)
--- a/dom/plugins/test/mochitest/test_NPNVdocumentOrigin.html
+++ b/dom/plugins/test/mochitest/test_NPNVdocumentOrigin.html
@@ -11,19 +11,27 @@
 
     function runTest() {
       var p1 = document.getElementById("plugin1");
       var realOrigin = "http://mochi.test:8888";
 
       // Test with no modifications
       is(p1.getNPNVdocumentOrigin(), realOrigin, "Checking for expected origin.");
 
-      // Mess with window.location.toString
-      window.location.toString = function() { return 'http://victim.rckc.at/'; }
-      is(p1.getNPNVdocumentOrigin(), realOrigin, "Checking for expected origin afer modifying window.location.toString.");
+      // This used to test that shadowing window.location.toString didn't confuse
+      // getNPNVdocumentOrigin. But now we explicitly throw when that happens. So
+      // just verify that we throw. There's no reason why getNPNVdocumentOrigin _would_
+      // be confused in this case anyway.
+      try {
+        window.location.toString = function() { return 'http://victim.rckc.at/'; }
+        ok(false, "Should throw when shadowing window.location.toString");
+      }
+      catch (e) {
+        ok(true, "Should throw when shadowing window.location.toString");
+      }
 
       // Create a plugin in a new window with about:blank
       var newWindow = window.open("about:blank");
       newWindow.document.writeln('<embed id="plugin2" type="application/x-test" width="200" height="200"></embed>');
       var p2 = newWindow.document.getElementById("plugin2");
       is(p2.getNPNVdocumentOrigin(), realOrigin, "Checking for expected origin of plugin in new about:blank window.");
       newWindow.close();
 
new file mode 100644
--- /dev/null
+++ b/dom/plugins/test/mochitest/test_src_url_change.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+  <title>Test changing src attribute</title>
+  <script type="text/javascript" src="/MochiKit/packed.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body onload="runTests()">
+  <p id="display"></p>
+
+  <embed id="plugin1" src="about:blank" type="application/x-test" width="200" height="200"></embed>
+
+  <script type="application/javascript;version=1.8">
+  SimpleTest.waitForExplicitFinish();
+
+  var p = document.getElementById('plugin1');
+
+  var destroyed = false;
+  function onDestroy() {
+    destroyed = true;
+  }
+
+  function runTests() {
+    p.startWatchingInstanceCount();
+    p.callOnDestroy(onDestroy);
+
+    p.setAttribute("src", "loremipsum.txt");
+
+    is(destroyed, true, "Instance should have been destroyed.");
+    is(p.getInstanceCount(), 1, "One new instance should have been created.");
+
+    p.stopWatchingInstanceCount();
+
+    SimpleTest.finish();
+  }
+  </script>
+</body>
+</html>
+
--- a/dom/tests/mochitest/bugs/test_bug541530.html
+++ b/dom/tests/mochitest/bugs/test_bug541530.html
@@ -20,58 +20,83 @@ var orig = window;
 window = {};
 
 var origLocation = location;
 
 ok(window === orig, "can't override window");
 ok(window.location === location, "properties are properly aliased");
 ok(document.location === location, "properties are properly aliased");
 
+var canDefine = false;
+try {
+    var foo;
+    __defineGetter__.call(foo, 'bar', function() {});
+    __defineSetter__.call(foo, 'bar', function() {});
+    canDefine = true;
+} catch (e) {}
+ok(canDefine, "Should have access to __defineGetter__ and __defineSetter__");
+
 try {
     __defineGetter__('window', function() {});
     ok(false, "should not be able to defineGetter(window)");
 } catch (e) {
 }
 
 try {
-    window.__defineGetter__('location', function(){});
+    __defineGetter__.call(window, 'location', function(){});
     ok(false, "should not be able to defineGetter(window.location)");
 } catch (e) {
 }
 
 try {
-    window.location.__defineGetter__('href', function(){});
+    __defineGetter__.call(window.location, 'href', function(){});
     ok(false, "shouldn't be able to override location.href");
 } catch (e) {
+    ok(/shadow/.exec(e.message), "Should be caught by the anti-shadow mechanism.");
 }
 
+// Try deleting the property.
+delete window.location.href;
+ok(typeof window.location.href !== 'undefined',
+   "shouldn't be able to delete the inherited property");
+delete Object.getPrototypeOf(window.location).href;
+ok(typeof window.location.href !== 'undefined',
+   "shouldn't be able to delete the property off of the prototype");
+
 try {
-    window.location.__proto__.__defineGetter__('href', function(){});
+    __defineGetter__.call(Object.getPrototypeOf(window.location), 'href', function(){});
     ok(false, "shouldn't be able to use the prototype");
 } catch (e) {
 }
 
 try {
-    window.location.__defineSetter__('href', function(){});
+    __defineSetter__.call(window.location, 'href', function(){});
     ok(false, "overrode a setter for location.href?");
 } catch (e) {
+    ok(/shadow/.exec(e.message), "Should be caught by the anti-shadow mechanism.");
 }
 
 try {
-    document.__defineGetter__('location', function(){});
+    __defineGetter__.call(document, 'location', function(){});
     ok(false, "shouldn't be able to override document.location");
 } catch (e) {
 }
 
-is(location.watch, undefined, "watch doesn't exist on location objects");
+// Make sure watch works correctly.
+var watchTest = {a: 42};
+var watchHandler = function(prop, oldval, newval) { return 22; }
+Object.prototype.watch.call(watchTest, 'a', watchHandler);
+watchTest.a = 1;
+is(watchTest.a, 22, "Watch works correctly");
+
+// Now make sure we can't watch location.
 try {
-    Object.prototype.watch.call(location, function() {});
+    Object.prototype.watch.call(location, 'href', function(prop, oldval, newval) {});
     ok(false, "shouldn't be able to set a watchpoint on location");
-} catch (e) {
-}
+} catch (e) {}
 
 ok(window === orig, "can't override window");
 ok(window.location === origLocation, "properties are properly aliased");
 ok(document.location === origLocation, "properties are properly aliased");
 
 location.href = 'javascript:ok(true, "was able to set location.href through a watchpoint")';
 
 </script>
--- a/dom/tests/mochitest/chrome/Makefile.in
+++ b/dom/tests/mochitest/chrome/Makefile.in
@@ -68,16 +68,17 @@ include $(topsrcdir)/config/rules.mk
 		test_cyclecollector.xul \
 		test_resize_move_windows.xul \
 		test_popup_blocker_chrome.xul \
 		test_moving_xhr.xul \
 		test_nodesFromRect.html \
 		489127.html \
 		test_focus_docnav.xul \
 		window_focus_docnav.xul \
+		test_clonewrapper.xul \
 		$(NULL)
 
 ifeq (WINNT,$(OS_ARCH))
 _TEST_FILES += \
 		test_sizemode_attribute.xul \
 		sizemode_attribute.xul \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/chrome/test_clonewrapper.xul
@@ -0,0 +1,126 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=667388
+-->
+<window title="Mozilla Bug 667388"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+
+  // Setup.
+  SimpleTest.waitForExplicitFinish();
+  window.testObject = { myNumber: 42,
+                        myDomain: window.location.domain };
+
+
+  // Wait for both frames to load before proceeding.
+  var framesLoaded = [null, false, false, false];
+  function onFrameLoaded(id) {
+
+    // Mark this frame as loaded.
+    framesLoaded[id] = true;
+
+    // Allow it to call |is|.
+    window.frames[id].wrappedJSObject.is = is;
+
+    // If all the frames are loaded, start the test.
+    if (framesLoaded[1] && framesLoaded[2] && framesLoaded[3])
+      startTest();
+  }
+
+  function startTest() {
+
+    // Test interaction between chrome and content.
+    runChromeContentTest(window.frames[1]);
+
+    // Try same origin.
+    runContentContentTest(window.frames[1], window.frames[2],
+                          true, "Should be able to clone same-origin");
+
+    // Try cross-origin.
+    runContentContentTest(window.frames[2], window.frames[3],
+                          false, "Should not be able to clone cross-origin");
+
+    // Colaborate with document.domain, then try again.
+    frames[2].document.domain = 'example.org';
+    frames[3].document.domain = 'example.org';
+    runContentContentTest(window.frames[2], window.frames[3],
+                          true, "Should be able to clone cross-origin with document.domain")
+
+    SimpleTest.finish();
+  }
+
+  // Tests cloning between chrome and content.
+  function runChromeContentTest(contentWin) {
+
+    // We should be able to clone a content object.
+    tryToClone(contentWin.wrappedJSObject.testObject,
+               true,
+               "Chrome should be able to clone content object");
+
+    // Content should not be able to clone a chrome object.
+    //
+    // Note that because we're using |wrappedJSObject| here, the subject
+    // principal is clamed to the principal of the iframe, which is what we want.
+    contentWin.wrappedJSObject.tryToClone(window.testObject, false,
+                                          "Content should not be able to clone chrome object");
+  }
+
+  // Test cloning between content and content.
+  //
+  // Note - the way we do this is kind of sketchy. Because we're grabbing the
+  // test object from win1 by waiving Xray (via .wrappedJSObject), the object
+  // we're passing from win1 to win2 is actually the waived object (which has
+  // a distinct identity in the compartment of win2). So this means that we're
+  // actually giving win2 Xray waivers to win1! This doesn't affect things as
+  // long as the security wrappers check documentDomainMakesSameOrigin directly
+  // for the puncture case rather than checking IsTransparent(), but it still
+  // gives rise to a situation that wouldn't really happen in practice.
+  //
+  // A more serious issues is that we don't/can't test the Xray wrapper path,
+  // because the only native objects that we successfully send through
+  // structured clone don't force a parent in PreCreate, and so we _can't_
+  // have security wrappers around them (we get a new XPCWN in each
+  // compartment). Since the case can't happen, we can't write tests for it.
+  // But when it can happen (ie, we fix PreCreate for File, FileList, Blob, etc),
+  // we want tests right away, because this stuff is very security-sensitive.
+  // So we set this test up to fail when things are fixed, so that the someone
+  // will notice and properly augment this test to cover native objects.
+  function runContentContentTest(win1, win2, shouldSucceed, msg) {
+    win1.wrappedJSObject.tryToClone(win2.wrappedJSObject.testObject,
+                                    shouldSucceed, msg);
+
+    var fixMsg = "If this fails, you fixed PreCreate. That's a good thing, ";
+    fixMsg += "but now we need some test coverage. See the above comment.";
+    win1.wrappedJSObject.tryToClone(win2.wrappedJSObject.blob, true, fixMsg);
+    win1.wrappedJSObject.tryToClone(win2.wrappedJSObject.filelist, true, fixMsg);
+  }
+
+  function tryToClone(obj, shouldSucceed, message) {
+    var success = false;
+    var sink = window.frames[0];
+    try { sink.postMessage(obj, '*'); success = true; }
+    catch (e) { message = message + ' (threw: ' + e.message + ')'; }
+    is(success, shouldSucceed, message);
+  }
+
+  ]]>
+  </script>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=667388"
+     target="_blank">Mozilla Bug 667388</a>
+  <iframe id="sink" />
+  <!-- The first two are same-origin, the third is not. -->
+  <iframe id="frame1" onload="onFrameLoaded(1);" src="http://test1.example.org/tests/dom/tests/mochitest/general/file_clonewrapper.html" />
+  <iframe id="frame2" onload="onFrameLoaded(2);" src="http://test1.example.org/tests/dom/tests/mochitest/general/file_clonewrapper.html" />
+  <iframe id="frame3" onload="onFrameLoaded(3);" src="http://test2.example.org/tests/dom/tests/mochitest/general/file_clonewrapper.html" />
+  </body>
+
+</window>
--- a/dom/tests/mochitest/general/Makefile.in
+++ b/dom/tests/mochitest/general/Makefile.in
@@ -77,16 +77,17 @@ include $(topsrcdir)/config/rules.mk
 		test_browserFrame2.html \
 		test_browserFrame3.html \
 		test_browserFrame4.html \
 		test_browserFrame5.html \
 		test_browserFrame6.html \
 		test_browserFrame7.html \
 		test_for_of.html \
 		test_focus_legend_noparent.html \
+		file_clonewrapper.html \
 		$(NULL)
 
 _CHROME_FILES = \
 		test_innerScreen.xul \
 		test_offsets.xul \
 		test_offsets.js \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/file_clonewrapper.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<html>
+<head>
+<script type="application/javascript">
+
+  // Set up the objects for cloning.
+  function setup() {
+    window.testObject = { myNumber: 42,
+                          myDomain: window.location.domain };
+    window.blob = (new MozBlobBuilder()).getBlob('text/plain');
+    window.fileList = document.getElementById('fileinput').files;
+  }
+
+  // Called by the chrome parent window.
+  function tryToClone(obj, shouldSucceed, message) {
+    var success = false;
+    var sink = window.frames[0];
+    try { sink.postMessage(obj, '*'); success = true; }
+    catch (e) { message = message + ' (threw: ' + e.message + ')'; }
+    is(success, shouldSucceed, message);
+  }
+
+</script>
+</head>
+<body onload="setup()">
+<input id="fileinput" type="file"></input>
+<iframe id="sink">
+</body>
+</html>
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -671,21 +671,17 @@ nsEditorEventListener::DragOver(nsIDOMDr
   if (defaultPrevented) {
     return NS_OK;
   }
 
   aDragEvent->GetRangeParent(getter_AddRefs(parent));
   nsCOMPtr<nsIContent> dropParent = do_QueryInterface(parent);
   NS_ENSURE_TRUE(dropParent, NS_ERROR_FAILURE);
 
-  if (!dropParent->IsEditable()) {
-    return NS_OK;
-  }
-
-  if (CanDrop(aDragEvent)) {
+  if (dropParent->IsEditable() && CanDrop(aDragEvent)) {
     aDragEvent->PreventDefault(); // consumed
 
     if (mCaret) {
       PRInt32 offset = 0;
       nsresult rv = aDragEvent->GetRangeOffset(&offset);
       NS_ENSURE_SUCCESS(rv, rv);
 
       // to avoid flicker, we could track the node and offset to see if we moved
@@ -749,21 +745,17 @@ nsEditorEventListener::Drop(nsIDOMDragEv
     return NS_OK;
   }
 
   nsCOMPtr<nsIDOMNode> parent;
   aMouseEvent->GetRangeParent(getter_AddRefs(parent));
   nsCOMPtr<nsIContent> dropParent = do_QueryInterface(parent);
   NS_ENSURE_TRUE(dropParent, NS_ERROR_FAILURE);
 
-  if (!dropParent->IsEditable()) {
-    return NS_OK;
-  }
-
-  if (!CanDrop(aMouseEvent)) {
+  if (!dropParent->IsEditable() || !CanDrop(aMouseEvent)) {
     // was it because we're read-only?
     if (mEditor->IsReadonly() || mEditor->IsDisabled())
     {
       // it was decided to "eat" the event as this is the "least surprise"
       // since someone else handling it might be unintentional and the 
       // user could probably re-drag to be not over the disabled/readonly 
       // editfields if that is what is desired.
       return aMouseEvent->StopPropagation();
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -861,16 +861,18 @@ BasicTextureImage::Resize(const nsIntSiz
 
 TiledTextureImage::TiledTextureImage(GLContext* aGL,
                                      nsIntSize aSize,
                                      TextureImage::ContentType aContentType,
                                      bool aUseNearestFilter)
     : TextureImage(aSize, LOCAL_GL_CLAMP_TO_EDGE, aContentType, aUseNearestFilter)
     , mCurrentImage(0)
     , mInUpdate(false)
+    , mRows(0)
+    , mColumns(0)
     , mGL(aGL)
     , mUseNearestFilter(aUseNearestFilter)
     , mTextureState(Created)
     , mIterationCallback(nsnull)
 {
     mTileSize = mGL->WantsSmallTiles() ? 256 : mGL->GetMaxTextureSize();
     if (aSize != nsIntSize(0,0)) {
         Resize(aSize);
@@ -1109,40 +1111,108 @@ TiledTextureImage::BindTexture(GLenum aT
 
 void
 TiledTextureImage::ApplyFilter()
 {
    mGL->ApplyFilterToBoundTexture(mFilter);
 }
 
 /*
- * simple resize, just discards everything. we can be more clever just
- * adding or discarding tiles, but do we want this?
+ * Resize, trying to reuse tiles. The reuse strategy is to decide on reuse per
+ * column. A tile on a column is reused if it hasn't changed size, otherwise it
+ * is discarded/replaced. Extra tiles on a column are pruned after iterating
+ * each column, and extra rows are pruned after iteration over the entire image
+ * finishes.
  */
 void TiledTextureImage::Resize(const nsIntSize& aSize)
 {
     if (mSize == aSize && mTextureState != Created) {
         return;
     }
-    mSize = aSize;
-    mImages.Clear();
+
     // calculate rows and columns, rounding up
-    mColumns = (aSize.width  + mTileSize - 1) / mTileSize;
-    mRows    = (aSize.height + mTileSize - 1) / mTileSize;
-
-    for (unsigned int row = 0; row < mRows; row++) {
-      for (unsigned int col = 0; col < mColumns; col++) {
-          nsIntSize size( // use tilesize first, then the remainder
-                  (col+1) * mTileSize > (unsigned int)aSize.width  ? aSize.width  % mTileSize : mTileSize,
-                  (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
-          nsRefPtr<TextureImage> teximg =
-                  mGL->TileGenFunc(size, mContentType, mUseNearestFilter);
-          mImages.AppendElement(teximg.forget());
-      }
+    unsigned int columns = (aSize.width  + mTileSize - 1) / mTileSize;
+    unsigned int rows = (aSize.height + mTileSize - 1) / mTileSize;
+
+    // Iterate over old tile-store and insert/remove tiles as necessary
+    int row;
+    unsigned int i = 0;
+    for (row = 0; row < (int)rows; row++) {
+        // If we've gone beyond how many rows there were before, set mColumns to
+        // zero so that we only create new tiles.
+        if (row >= (int)mRows)
+            mColumns = 0;
+
+        // Similarly, if we're on the last row of old tiles and the height has
+        // changed, discard all tiles in that row.
+        // This will cause the pruning of columns not to work, but we don't need
+        // to worry about that, as no more tiles will be reused past this point
+        // anyway.
+        if ((row == (int)mRows - 1) && (aSize.height != mSize.height))
+            mColumns = 0;
+
+        int col;
+        for (col = 0; col < (int)columns; col++) {
+            nsIntSize size( // use tilesize first, then the remainder
+                    (col+1) * mTileSize > (unsigned int)aSize.width  ? aSize.width  % mTileSize : mTileSize,
+                    (row+1) * mTileSize > (unsigned int)aSize.height ? aSize.height % mTileSize : mTileSize);
+
+            bool replace = false;
+
+            // Check if we can re-use old tiles.
+            if (col < (int)mColumns) {
+                // Reuse an existing tile. If the tile is an end-tile and the
+                // width differs, replace it instead.
+                if (mSize.width != aSize.width) {
+                    if (col == (int)mColumns - 1) {
+                        // Tile at the end of the old column, replace it with
+                        // a new one.
+                        replace = true;
+                    } else if (col == (int)columns - 1) {
+                        // Tile at the end of the new column, create a new one.
+                    } else {
+                        // Before the last column on both the old and new sizes,
+                        // reuse existing tile.
+                        i++;
+                        continue;
+                    }
+                } else {
+                    // Width hasn't changed, reuse existing tile.
+                    i++;
+                    continue;
+                }
+            }
+
+            // Create a new tile.
+            nsRefPtr<TextureImage> teximg =
+                    mGL->TileGenFunc(size, mContentType, mUseNearestFilter);
+            if (replace)
+                mImages.ReplaceElementAt(i, teximg.forget());
+            else
+                mImages.InsertElementAt(i, teximg.forget());
+            i++;
+        }
+
+        // Prune any unused tiles on the end of the column.
+        if (row < (int)mRows) {
+            for (col = (int)mColumns - col; col > 0; col--) {
+                mImages.RemoveElementAt(i);
+            }
+        }
     }
+
+    // Prune any unused tiles at the end of the store.
+    unsigned int length = mImages.Length();
+    for (; i < length; i++)
+      mImages.RemoveElementAt(mImages.Length()-1);
+
+    // Reset tile-store properties.
+    mRows = rows;
+    mColumns = columns;
+    mSize = aSize;
     mTextureState = Allocated;
 }
 
 PRUint32 TiledTextureImage::GetTileCount()
 {
     return mImages.Length();
 }
 
--- a/gfx/thebes/gfxColor.h
+++ b/gfx/thebes/gfxColor.h
@@ -179,16 +179,18 @@ struct THEBES_API gfxRGBA {
      */
     gfxRGBA(gfxFloat _r, gfxFloat _g, gfxFloat _b, gfxFloat _a=1.0) : r(_r), g(_g), b(_b), a(_a) {}
 
     /**
      * Initialize this color from a packed 32-bit color.
      * The color value is interpreted based on colorType;
      * all values use the native platform endianness.
      *
+     * Resulting gfxRGBA stores non-premultiplied data.
+     *
      * @see gfxRGBA::Packed
      */
     gfxRGBA(PRUint32 c, PackedColorType colorType = PACKED_ABGR) {
         if (colorType == PACKED_ABGR ||
             colorType == PACKED_ABGR_PREMULTIPLIED)
         {
             r = ((c >> 0) & 0xff) * (1.0 / 255.0);
             g = ((c >> 8) & 0xff) * (1.0 / 255.0);
--- a/gfx/thebes/gfxFont.cpp
+++ b/gfx/thebes/gfxFont.cpp
@@ -1930,45 +1930,50 @@ gfxFont::GetShapedWord(gfxContext *aCont
 {
     // if there's a cached entry for this word, just return it
     CacheHashKey key(aText, aLength, aHash,
                      aRunScript,
                      aAppUnitsPerDevUnit,
                      aFlags);
 
     CacheHashEntry *entry = mWordCache.PutEntry(key);
+    if (!entry) {
+        NS_WARNING("failed to create word cache entry - expect missing text");
+        return nsnull;
+    }
     gfxShapedWord *sw = entry->mShapedWord;
     Telemetry::Accumulate(Telemetry::WORD_CACHE_LOOKUP_LEN, aLength);
     Telemetry::Accumulate(Telemetry::WORD_CACHE_LOOKUP_SCRIPT, aRunScript);
 
     if (sw) {
         sw->ResetAge();
         Telemetry::Accumulate(Telemetry::WORD_CACHE_HIT_LEN, aLength);
         Telemetry::Accumulate(Telemetry::WORD_CACHE_HIT_SCRIPT, aRunScript);
         return sw;
     }
 
     sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
                                                     aRunScript,
                                                     aAppUnitsPerDevUnit,
                                                     aFlags);
-    NS_ASSERTION(sw != nsnull,
-                 "failed to create gfxShapedWord - expect missing text");
     if (!sw) {
+        NS_WARNING("failed to create gfxShapedWord - expect missing text");
         return nsnull;
     }
 
-    bool ok;
+    bool ok = false;
     if (sizeof(T) == sizeof(PRUnichar)) {
         ok = ShapeWord(aContext, sw, (const PRUnichar*)aText);
     } else {
         nsAutoString utf16;
         AppendASCIItoUTF16(nsDependentCSubstring((const char*)aText, aLength),
                            utf16);
-        ok = ShapeWord(aContext, sw, utf16.BeginReading());
+        if (utf16.Length() == aLength) {
+            ok = ShapeWord(aContext, sw, utf16.BeginReading());
+        }
     }
     NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
 
     for (PRUint32 i = 0; i < aLength; ++i) {
         if (aText[i] == ' ') {
             sw->SetIsSpace(i);
         } else if (i > 0 &&
                    NS_IS_HIGH_SURROGATE(aText[i - 1]) &&
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -205,16 +205,61 @@ gfxUtils::UnpremultiplyImageSurface(gfxI
         *dst++ = a;
         *dst++ = UnpremultiplyValue(a, r);
         *dst++ = UnpremultiplyValue(a, g);
         *dst++ = UnpremultiplyValue(a, b);
 #endif
     }
 }
 
+void
+gfxUtils::ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,
+                            gfxImageSurface *aDestSurface) {
+    if (!aDestSurface)
+        aDestSurface = aSourceSurface;
+
+    NS_ABORT_IF_FALSE(aSourceSurface->Format() == aDestSurface->Format() &&
+                      aSourceSurface->Width() == aDestSurface->Width() &&
+                      aSourceSurface->Height() == aDestSurface->Height() &&
+                      aSourceSurface->Stride() == aDestSurface->Stride(),
+                      "Source and destination surfaces don't have identical characteristics");
+
+    NS_ABORT_IF_FALSE(aSourceSurface->Stride() == aSourceSurface->Width() * 4,
+                      "Source surface stride isn't tightly packed");
+
+    NS_ABORT_IF_FALSE(aSourceSurface->Format() == gfxASurface::ImageFormatARGB32,
+                      "Surfaces must be ARGB32");
+
+    PRUint8 *src = aSourceSurface->Data();
+    PRUint8 *dst = aDestSurface->Data();
+
+    PRUint32 dim = aSourceSurface->Width() * aSourceSurface->Height();
+    PRUint8 *srcEnd = src + 4*dim;
+
+    if (src == dst) {
+        PRUint8 buffer[4];
+        for (; src != srcEnd; src += 4) {
+            buffer[0] = src[2];
+            buffer[1] = src[1];
+            buffer[2] = src[0];
+
+            src[0] = buffer[0];
+            src[1] = buffer[1];
+            src[2] = buffer[2];
+        }
+    } else {
+        for (; src != srcEnd; src += 4, dst += 4) {
+            dst[0] = src[2];
+            dst[1] = src[1];
+            dst[2] = src[0];
+            dst[3] = src[3];
+        }
+    }
+}
+
 static bool
 IsSafeImageTransformComponent(gfxFloat aValue)
 {
   return aValue >= -32768 && aValue <= 32767;
 }
 
 /**
  * This returns the fastest operator to use for solid surfaces which have no
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -60,16 +60,19 @@ public:
      * If the source is not ImageFormatARGB32, no operation is performed.  If
      * aDestSurface is given, the data is copied over.
      */
     static void PremultiplyImageSurface(gfxImageSurface *aSourceSurface,
                                         gfxImageSurface *aDestSurface = nsnull);
     static void UnpremultiplyImageSurface(gfxImageSurface *aSurface,
                                           gfxImageSurface *aDestSurface = nsnull);
 
+    static void ConvertBGRAtoRGBA(gfxImageSurface *aSourceSurface,
+                                  gfxImageSurface *aDestSurface = nsnull);
+
     /**
      * Draw something drawable while working around limitations like bad support
      * for EXTEND_PAD, lack of source-clipping, or cairo / pixman bugs with
      * extreme user-space-to-image-space transforms.
      *
      * The input parameters here usually come from the output of our image
      * snapping algorithm in nsLayoutUtils.cpp.
      * This method is split from nsLayoutUtils::DrawPixelSnapped to allow for
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -204,16 +204,19 @@ void nsPNGDecoder::EndImageFrame()
   numFrames = mImage.GetNumFrames();
 
   // We can't use mPNG->num_frames_read as it may be one ahead.
   if (numFrames > 1) {
     // Tell the image renderer that the frame is complete
     if (mFrameHasNoAlpha)
       mImage.SetFrameHasNoAlpha(numFrames - 1);
 
+    // PNG is always non-premult
+    mImage.SetFrameAsNonPremult(numFrames - 1, true);
+
     PostInvalidation(mFrameRect);
   }
 #endif
 
   PostFrameStop();
 }
 
 void
--- a/image/src/Decoder.cpp
+++ b/image/src/Decoder.cpp
@@ -295,16 +295,23 @@ Decoder::PostInvalidation(nsIntRect& aRe
 void
 Decoder::PostDecodeDone()
 {
   NS_ABORT_IF_FALSE(!IsSizeDecode(), "Can't be done with decoding with size decode!");
   NS_ABORT_IF_FALSE(!mInFrame, "Can't be done decoding if we're mid-frame!");
   NS_ABORT_IF_FALSE(!mDecodeDone, "Decode already done!");
   mDecodeDone = true;
 
+  // Set premult before DecodingComplete(), since DecodingComplete() calls Optimize()
+  int frames = GetFrameCount();
+  bool isNonPremult = GetDecodeFlags() & DECODER_NO_PREMULTIPLY_ALPHA;
+  for (int i = 0; i < frames; i++) {
+    mImage.SetFrameAsNonPremult(i, isNonPremult);
+  }
+
   // Notify
   mImage.DecodingComplete();
   if (mObserver) {
     mObserver->OnStopContainer(nsnull, &mImage);
     mObserver->OnStopDecode(nsnull, NS_OK, nsnull);
   }
 }
 
--- a/image/src/Decoder.h
+++ b/image/src/Decoder.h
@@ -145,18 +145,18 @@ public:
   bool GetDecodeDone() const {
     return mDecodeDone;
   }
 
   // flags.  Keep these in sync with imgIContainer.idl.
   // SetDecodeFlags must be called before Init(), otherwise
   // default flags are assumed.
   enum {
-    DECODER_NO_PREMULTIPLY_ALPHA = 0x2,
-    DECODER_NO_COLORSPACE_CONVERSION = 0x4
+    DECODER_NO_PREMULTIPLY_ALPHA = 0x2,     // imgIContainer::FLAG_DECODE_NO_PREMULTIPLY_ALPHA
+    DECODER_NO_COLORSPACE_CONVERSION = 0x4  // imgIContainer::FLAG_DECODE_NO_COLORSPACE_CONVERSION
   };
   void SetDecodeFlags(PRUint32 aFlags) { mDecodeFlags = aFlags; }
   PRUint32 GetDecodeFlags() { return mDecodeFlags; }
 
   // Use HistogramCount as an invalid Histogram ID
   virtual Telemetry::ID SpeedHistogram() { return Telemetry::HistogramCount; }
 
 protected:
--- a/image/src/RasterImage.cpp
+++ b/image/src/RasterImage.cpp
@@ -1320,16 +1320,35 @@ RasterImage::SetFrameHasNoAlpha(PRUint32
   NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
 
   frame->SetHasNoAlpha();
 
   return NS_OK;
 }
 
 nsresult
+RasterImage::SetFrameAsNonPremult(PRUint32 aFrameNum, bool aIsNonPremult)
+{
+  if (mError)
+    return NS_ERROR_FAILURE;
+
+  NS_ABORT_IF_FALSE(aFrameNum < mFrames.Length(), "Invalid frame index!");
+  if (aFrameNum >= mFrames.Length())
+    return NS_ERROR_INVALID_ARG;
+
+  imgFrame* frame = GetImgFrame(aFrameNum);
+  NS_ABORT_IF_FALSE(frame, "Calling SetFrameAsNonPremult on frame that doesn't exist!");
+  NS_ENSURE_TRUE(frame, NS_ERROR_FAILURE);
+
+  frame->SetAsNonPremult(aIsNonPremult);
+
+  return NS_OK;
+}
+
+nsresult
 RasterImage::DecodingComplete()
 {
   if (mError)
     return NS_ERROR_FAILURE;
 
   // Flag that we're done decoding.
   // XXX - these should probably be combined when we fix animated image
   // discarding with bug 500402.
--- a/image/src/RasterImage.h
+++ b/image/src/RasterImage.h
@@ -239,16 +239,17 @@ public:
   void ForceDiscard() { Discard(/* force = */ true); }
 
   /* Callbacks for decoders */
   nsresult SetFrameDisposalMethod(PRUint32 aFrameNum,
                                   PRInt32 aDisposalMethod);
   nsresult SetFrameTimeout(PRUint32 aFrameNum, PRInt32 aTimeout);
   nsresult SetFrameBlendMethod(PRUint32 aFrameNum, PRInt32 aBlendMethod);
   nsresult SetFrameHasNoAlpha(PRUint32 aFrameNum);
+  nsresult SetFrameAsNonPremult(PRUint32 aFrameNum, bool aIsNonPremult);
 
   /**
    * Sets the size of the container. This should only be called by the
    * decoder. This function may be called multiple times, but will throw an
    * error if subsequent calls do not match the first.
    */
   nsresult SetSize(PRInt32 aWidth, PRInt32 aHeight);
 
--- a/image/src/imgFrame.cpp
+++ b/image/src/imgFrame.cpp
@@ -140,22 +140,23 @@ imgFrame::imgFrame() :
   mPalettedImageData(nsnull),
   mSinglePixelColor(0),
   mTimeout(100),
   mDisposalMethod(0), /* imgIContainer::kDisposeNotSpecified */
   mBlendMethod(1), /* imgIContainer::kBlendOver */
   mSinglePixel(false),
   mNeverUseDeviceSurface(false),
   mFormatChanged(false),
-  mCompositingFailed(false)
+  mCompositingFailed(false),
+  mNonPremult(false),
 #ifdef USE_WIN_SURFACE
-  , mIsDDBSurface(false)
+  mIsDDBSurface(false),
 #endif
-  , mLocked(false)
-  , mInformedDiscardTracker(false)
+  mLocked(false),
+  mInformedDiscardTracker(false)
 {
   static bool hasCheckedOptimize = false;
   if (!hasCheckedOptimize) {
     if (PR_GetEnv("MOZ_DISABLE_IMAGE_OPTIMIZE")) {
       gDisableOptimize = true;
     }
     hasCheckedOptimize = true;
   }
@@ -249,37 +250,43 @@ nsresult imgFrame::Init(PRInt32 aX, PRIn
 nsresult imgFrame::Optimize()
 {
   if (gDisableOptimize)
     return NS_OK;
 
   if (mPalettedImageData || mOptSurface || mSinglePixel)
     return NS_OK;
 
+  // Don't do single-color opts on non-premult data.
+  // Cairo doesn't support non-premult single-colors.
+  if (mNonPremult)
+    return NS_OK;
+
   /* Figure out if the entire image is a constant color */
 
   // this should always be true
   if (mImageSurface->Stride() == mSize.width * 4) {
     PRUint32 *imgData = (PRUint32*) mImageSurface->Data();
     PRUint32 firstPixel = * (PRUint32*) imgData;
     PRUint32 pixelCount = mSize.width * mSize.height + 1;
 
     while (--pixelCount && *imgData++ == firstPixel)
       ;
 
     if (pixelCount == 0) {
       // all pixels were the same
       if (mFormat == gfxASurface::ImageFormatARGB32 ||
           mFormat == gfxASurface::ImageFormatRGB24)
       {
-        mSinglePixelColor = gfxRGBA
-          (firstPixel,
-           (mFormat == gfxImageSurface::ImageFormatRGB24 ?
-            gfxRGBA::PACKED_XRGB :
-            gfxRGBA::PACKED_ARGB_PREMULTIPLIED));
+        // Should already be premult if desired.
+        gfxRGBA::PackedColorType inputType = gfxRGBA::PACKED_XRGB;
+        if (mFormat == gfxASurface::ImageFormatARGB32)
+          inputType = gfxRGBA::PACKED_ARGB_PREMULTIPLIED;
+
+        mSinglePixelColor = gfxRGBA(firstPixel, inputType);
 
         mSinglePixel = true;
 
         // blow away the older surfaces (if they exist), to release their memory
         mImageSurface = nsnull;
         mOptSurface = nsnull;
 #ifdef USE_WIN_SURFACE
         mWinSurface = nsnull;
@@ -429,16 +436,17 @@ imgFrame::SurfaceForDrawing(bool        
     tmpCtx.SetOperator(gfxContext::OPERATOR_SOURCE);
     if (mSinglePixel) {
       tmpCtx.SetDeviceColor(mSinglePixelColor);
     } else {
       tmpCtx.SetSource(ThebesSurface(), gfxPoint(aPadding.left, aPadding.top));
     }
     tmpCtx.Rectangle(available);
     tmpCtx.Fill();
+
     return SurfaceWithFormat(new gfxSurfaceDrawable(surface, size), format);
   }
 
   // Not tiling, and we have a surface, so we can account for
   // padding and/or a partial decode just by twiddling parameters.
   // First, update our user-space fill rect.
   aSourceRect = aSourceRect.Intersect(available);
   gfxMatrix imageSpaceToUserSpace = aUserSpaceToImageSpace;
@@ -512,16 +520,18 @@ nsresult imgFrame::Extract(const nsIntRe
   // surface for the sub-image; this ensures that the correct
   // (albeit slower) Cairo fallback scaler will be used.
   subImage->mNeverUseDeviceSurface = true;
 
   nsresult rv = subImage->Init(0, 0, aRegion.width, aRegion.height, 
                                mFormat, mPaletteDepth);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  subImage->SetAsNonPremult(mNonPremult);
+
   // scope to destroy ctx
   {
     gfxContext ctx(subImage->ThebesSurface());
     ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
     if (mSinglePixel) {
       ctx.SetDeviceColor(mSinglePixelColor);
     } else {
       // SetSource() places point (0,0) of its first argument at
@@ -767,16 +777,21 @@ bool imgFrame::ImageComplete() const
 void imgFrame::SetHasNoAlpha()
 {
   if (mFormat == gfxASurface::ImageFormatARGB32) {
       mFormat = gfxASurface::ImageFormatRGB24;
       mFormatChanged = true;
   }
 }
 
+void imgFrame::SetAsNonPremult(bool aIsNonPremult)
+{
+  mNonPremult = aIsNonPremult;
+}
+
 bool imgFrame::GetCompositingFailed() const
 {
   return mCompositingFailed;
 }
 
 void imgFrame::SetCompositingFailed(bool val)
 {
   mCompositingFailed = val;
--- a/image/src/imgFrame.h
+++ b/image/src/imgFrame.h
@@ -87,16 +87,17 @@ public:
 
   PRInt32 GetFrameDisposalMethod() const;
   void SetFrameDisposalMethod(PRInt32 aFrameDisposalMethod);
   PRInt32 GetBlendMethod() const;
   void SetBlendMethod(PRInt32 aBlendMethod);
   bool ImageComplete() const;
 
   void SetHasNoAlpha();
+  void SetAsNonPremult(bool aIsNonPremult);
 
   bool GetCompositingFailed() const;
   void SetCompositingFailed(bool val);
 
   nsresult LockImageData();
   nsresult UnlockImageData();
 
   nsresult GetSurface(gfxASurface **aSurface) const
@@ -175,28 +176,30 @@ private: // data
   nsIntRect    mDecoded;
 
   // The palette and image data for images that are paletted, since Cairo
   // doesn't support these images.
   // The paletted data comes first, then the image data itself.
   // Total length is PaletteDataLength() + GetImageDataLength().
   PRUint8*     mPalettedImageData;
 
+  // Note that the data stored in gfxRGBA is *non-alpha-premultiplied*.
   gfxRGBA      mSinglePixelColor;
 
   PRInt32      mTimeout; // -1 means display forever
   PRInt32      mDisposalMethod;
 
   gfxASurface::gfxImageFormat mFormat;
   PRUint8      mPaletteDepth;
   PRInt8       mBlendMethod;
   bool mSinglePixel;
   bool mNeverUseDeviceSurface;
   bool mFormatChanged;
   bool mCompositingFailed;
+  bool mNonPremult;
   /** Indicates if the image data is currently locked */
   bool mLocked;
 
   /** Have we called DiscardTracker::InformAllocation()? */
   bool mInformedDiscardTracker;
 
 #ifdef XP_WIN
   bool mIsDDBSurface;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1525,34 +1525,36 @@ if test "$OS_TARGET" = "Android"; then
       ;;
     x86-*)
       ANDROID_CPU_ARCH=x86
       ;;
     esac
 fi
 
 if test "$OS_TARGET" = "Android" -a -z "$gonkdir"; then
-    if test -n "$MOZ_ANDROID_LIBSTDCXX" ; then
-       if test ! -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libstdc++.a" ; then
-          AC_MSG_ERROR([Cannot find path to libstdc++ (NDK version >= 5?)])
-       fi
-       STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include -D_GLIBCXX_PERMIT_BACKWARD_HASH"
-       STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH"
-       STLPORT_LIBS="-lstdc++"
-    elif test -e "$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then
-       STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport"
-       STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/"
-       STLPORT_LIBS="-lstlport_static"
-    elif  test -e "$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then
-       STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport"
-       STLPORT_LDFLAGS="-L$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH"
-       STLPORT_LIBS="-lstlport_static"
-    elif test "$target" != "arm-android-eabi"; then
-       dnl fail if we're not building with NDKr4
-       AC_MSG_ERROR([Couldn't find path to stlport in the android ndk])
+    if test -z "$STLPORT_CPPFLAGS$STLPORT_LDFLAGS$STLPORT_LIBS"; then
+        if test -n "$MOZ_ANDROID_LIBSTDCXX" ; then
+            if test ! -e "$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/libstdc++.a" ; then
+                AC_MSG_ERROR([Cannot find path to libstdc++ (NDK version >= 5?)])
+            fi
+            STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/gnu-libstdc++/include -I$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH/include -D_GLIBCXX_PERMIT_BACKWARD_HASH"
+            STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/gnu-libstdc++/libs/$ANDROID_CPU_ARCH"
+            STLPORT_LIBS="-lstdc++"
+        elif test -e "$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then
+            STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport"
+            STLPORT_LDFLAGS="-L$android_ndk/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/"
+            STLPORT_LIBS="-lstlport_static"
+        elif  test -e "$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH/libstlport_static.a" ; then
+            STLPORT_CPPFLAGS="-I$android_ndk/sources/cxx-stl/stlport/stlport"
+            STLPORT_LDFLAGS="-L$android_ndk/tmp/ndk-digit/build/install/sources/cxx-stl/stlport/libs/$ANDROID_CPU_ARCH"
+            STLPORT_LIBS="-lstlport_static"
+        elif test "$target" != "arm-android-eabi"; then
+            dnl fail if we're not building with NDKr4
+            AC_MSG_ERROR([Couldn't find path to stlport in the android ndk])
+        fi
     fi
     CXXFLAGS="$CXXFLAGS $STLPORT_CPPFLAGS"
     LDFLAGS="$LDFLAGS $STLPORT_LDFLAGS"
     LIBS="$LIBS $STLPORT_LIBS"
 
     # save these for libffi's subconfigure,
     # which doesn't know how to figure this stuff out on its own
     ANDROID_CFLAGS="$CFLAGS"
--- a/js/src/jsclone.cpp
+++ b/js/src/jsclone.cpp
@@ -508,28 +508,43 @@ JSStructuredCloneWriter::startObject(JSO
 
     /* Write the header for obj. */
     return out.writePair(obj->isArray() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
 }
 
 bool
 JSStructuredCloneWriter::startWrite(const js::Value &v)
 {
+    assertSameCompartment(context(), v);
+
     if (v.isString()) {
         return writeString(SCTAG_STRING, v.toString());
     } else if (v.isNumber()) {
         return out.writeDouble(v.toNumber());
     } else if (v.isBoolean()) {
         return out.writePair(SCTAG_BOOLEAN, v.toBoolean());
     } else if (v.isNull()) {
         return out.writePair(SCTAG_NULL, 0);
     } else if (v.isUndefined()) {
         return out.writePair(SCTAG_UNDEFINED, 0);
     } else if (v.isObject()) {
         JSObject *obj = &v.toObject();
+
+        // The object might be a security wrapper. See if we can clone what's
+        // behind it. If we can, unwrap the object.
+        obj = UnwrapObjectChecked(context(), obj);
+        if (!obj)
+            return false;
+
+        // If we unwrapped above, we'll need to enter the underlying compartment.
+        // Let the AutoEnterCompartment do the right thing for us.
+        JSAutoEnterCompartment ac;
+        if (!ac.enter(context(), obj))
+            return false;
+
         if (obj->isRegExp()) {
             RegExpObject &reobj = obj->asRegExp();
             return out.writePair(SCTAG_REGEXP_OBJECT, reobj.getFlags()) &&
                    writeString(SCTAG_STRING, reobj.getSource());
         } else if (obj->isDate()) {
             double d = js_DateGetMsecSinceEpoch(context(), obj);
             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(d);
         } else if (obj->isObject() || obj->isArray()) {
@@ -559,16 +574,22 @@ JSStructuredCloneWriter::startWrite(cons
 bool
 JSStructuredCloneWriter::write(const Value &v)
 {
     if (!startWrite(v))
         return false;
 
     while (!counts.empty()) {
         JSObject *obj = &objs.back().toObject();
+
+        // The objects in |obj| can live in other compartments.
+        JSAutoEnterCompartment ac;
+        if (!ac.enter(context(), obj))
+            return false;
+
         if (counts.back()) {
             counts.back()--;
             jsid id = ids.back();
             ids.popBack();
             checkStack();
             if (JSID_IS_STRING(id) || JSID_IS_INT(id)) {
                 /*
                  * If obj still has an own property named id, write it out.
--- a/js/src/jsclone.h
+++ b/js/src/jsclone.h
@@ -168,16 +168,19 @@ struct JSStructuredCloneWriter {
     bool startObject(JSObject *obj);
     bool startWrite(const js::Value &v);
 
     inline void checkStack();
 
     js::SCOutput &out;
 
     // Vector of objects with properties remaining to be written.
+    //
+    // NB: These can span multiple compartments, so the compartment must be
+    // entered before any manipulation is performed.
     js::AutoValueVector objs;
 
     // counts[i] is the number of properties of objs[i] remaining to be written.
     // counts.length() == objs.length() and sum(counts) == ids.length().
     js::Vector<size_t> counts;
 
     // Ids of properties remaining to be written.
     js::AutoIdVector ids;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -861,21 +861,16 @@ struct JSObject : public js::ObjectImpl
     inline jsval getNamespaceDeclared() const;
     inline void setNamespaceDeclared(jsval decl);
 
     inline JSAtom *getQNameLocalName() const;
     inline jsval getQNameLocalNameVal() const;
     inline void setQNameLocalName(JSAtom *name);
 
     /*
-     * Proxy-specific getters and setters.
-     */
-    inline js::Wrapper *getWrapperHandler() const;
-
-    /*
      * Back to generic stuff.
      */
     inline bool isCallable();
 
     inline void finish(JSContext *cx);
     JS_ALWAYS_INLINE void finalize(JSContext *cx, bool background);
 
     inline bool hasProperty(JSContext *cx, jsid id, bool *foundp, unsigned flags = 0);
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -103,16 +103,17 @@ PropertyTable::init(JSRuntime *rt, Shape
     }
     return true;
 }
 
 bool
 Shape::makeOwnBaseShape(JSContext *cx)
 {
     JS_ASSERT(!base()->isOwned());
+    assertSameCompartment(cx, compartment());
 
     RootedVarShape self(cx, this);
 
     BaseShape *nbase = js_NewGCBaseShape(cx);
     if (!nbase)
         return false;
 
     new (nbase) BaseShape(*self->base());
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -81,16 +81,35 @@ js::UnwrapObject(JSObject *wrapped, bool
         if (stopAtOuter && wrapped->getClass()->ext.innerObject)
             break;
     }
     if (flagsp)
         *flagsp = flags;
     return wrapped;
 }
 
+JS_FRIEND_API(JSObject *)
+js::UnwrapObjectChecked(JSContext *cx, JSObject *obj)
+{
+    while (obj->isWrapper()) {
+        JSObject *wrapper = obj;
+        Wrapper *handler = Wrapper::wrapperHandler(obj);
+        bool rvOnFailure;
+        if (!handler->enter(cx, wrapper, JSID_VOID,
+                            Wrapper::PUNCTURE, &rvOnFailure))
+            return rvOnFailure ? obj : NULL;
+        obj = Wrapper::wrappedObject(obj);
+        JS_ASSERT(obj);
+        handler->leave(cx, wrapper);
+        if (obj->getClass()->ext.innerObject)
+            break;
+    }
+    return obj;
+}
+
 bool
 js::IsCrossCompartmentWrapper(const JSObject *wrapper)
 {
     return wrapper->isWrapper() &&
            !!(Wrapper::wrapperHandler(wrapper)->flags() & Wrapper::CROSS_COMPARTMENT);
 }
 
 Wrapper::Wrapper(unsigned flags) : ProxyHandler(&sWrapperFamily), mFlags(flags)
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -95,18 +95,45 @@ class JS_FRIEND_API(Wrapper) : public Pr
     virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
     virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, unsigned indent) MOZ_OVERRIDE;
     virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE;
     virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE;
     virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) MOZ_OVERRIDE;
 
     virtual void trace(JSTracer *trc, JSObject *wrapper) MOZ_OVERRIDE;
 
-    /* Policy enforcement traps. */
-    enum Action { GET, SET, CALL };
+    /* Policy enforcement traps.
+     *
+     * enter() allows the policy to specify whether the caller may perform |act|
+     * on the underlying object's |id| property. In the case when |act| is CALL,
+     * |id| is generally JSID_VOID.
+     *
+     * leave() allows the policy to undo various scoped state changes taken in
+     * enter(). If enter() succeeds, leave() must be called upon completion of
+     * the approved action.
+     *
+     * The |act| parameter to enter() specifies the action being performed. GET,
+     * SET, and CALL are self-explanatory, but PUNCTURE requires more explanation:
+     *
+     * GET and SET allow for a very fine-grained security membrane, through
+     * which access can be granted or denied on a per-property, per-object, and
+     * per-action basis. Sometimes though, we just want to asks if we can access
+     * _everything_ behind the wrapper barrier. For example, when the structured
+     * clone algorithm runs up against a cross-compartment wrapper, it needs to
+     * know whether it can enter the compartment and keep cloning, or whether it
+     * should throw. This is the role of PUNCTURE.
+     *
+     * PUNCTURE allows the policy to specify whether the wrapper barrier may
+     * be lifted - that is to say, whether the caller is allowed to access
+     * anything that the wrapped object could access. This is a very powerful
+     * permission, and thus should generally be denied for security wrappers
+     * except under very special circumstances. When |act| is PUNCTURE, |id|
+     * should be JSID_VOID.
+     * */
+    enum Action { GET, SET, CALL, PUNCTURE };
     virtual bool enter(JSContext *cx, JSObject *wrapper, jsid id, Action act, bool *bp);
     virtual void leave(JSContext *cx, JSObject *wrapper);
 
     static Wrapper singleton;
 
     static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
                          Wrapper *handler);
 
@@ -221,13 +248,19 @@ IsWrapper(const JSObject *obj)
 
 // Given a JSObject, returns that object stripped of wrappers. If
 // stopAtOuter is true, then this returns the outer window if it was
 // previously wrapped. Otherwise, this returns the first object for
 // which JSObject::isWrapper returns false.
 JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, bool stopAtOuter = true,
                                        unsigned *flagsp = NULL);
 
+// Given a JSObject, returns that object stripped of wrappers. At each stage,
+// the security wrapper has the opportunity to veto the unwrap. Since checked
+// code should never be unwrapping outer window wrappers, we always stop at
+// outer windows.
+JS_FRIEND_API(JSObject *) UnwrapObjectChecked(JSContext *cx, JSObject *obj);
+
 bool IsCrossCompartmentWrapper(const JSObject *obj);
 
 } /* namespace js */
 
 #endif
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -44,17 +44,16 @@
 #include "nsJSPrincipals.h"
 #include "nsIDOMWindow.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsContentUtils.h"
 
 #include "XPCWrapper.h"
 #include "XrayWrapper.h"
 #include "FilteringWrapper.h"
-#include "WrapperFactory.h"
 
 #include "jsfriendapi.h"
 
 using namespace mozilla;
 using namespace js;
 
 namespace xpc {
 
@@ -84,16 +83,19 @@ AccessCheck::isSameOrigin(JSCompartment 
     }
 
     return equals;
 }
 
 bool
 AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper)
 {
+    // The caller must ensure that the given wrapper wraps a Location object.
+    MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
+
     // Location objects are parented to the outer window for which they
     // were created. This gives us an easy way to determine whether our
     // object is same origin with the current inner window:
 
     // Grab the outer window...
     JSObject *obj = js::GetObjectParent(js::UnwrapObject(wrapper));
     if (!js::GetObjectClass(obj)->ext.innerObject) {
         // ...which might be wrapped in a security wrapper.
@@ -299,34 +301,48 @@ AccessCheck::isCrossOriginAccessPermitte
     if (!XPCWrapper::GetSecurityManager())
         return true;
 
     if (act == Wrapper::CALL)
         return true;
 
     JSObject *obj = Wrapper::wrappedObject(wrapper);
 
+    // LocationPolicy checks PUNCTURE first, so we should never get here for
+    // Location wrappers. For all other wrappers interested in cross-origin
+    // semantics, we want to allow puncturing only for the same-origin
+    // document.domain case.
+    if (act == Wrapper::PUNCTURE) {
+        MOZ_ASSERT(!WrapperFactory::IsLocationObject(obj));
+        return documentDomainMakesSameOrigin(cx, obj);
+    }
+
     const char *name;
     js::Class *clasp = js::GetObjectClass(obj);
     NS_ASSERTION(Jsvalify(clasp) != &XrayUtils::HolderClass, "shouldn't have a holder here");
     if (clasp->ext.innerObject)
         name = "Window";
     else
         name = clasp->name;
 
     if (JSID_IS_STRING(id)) {
         if (IsPermitted(name, JSID_TO_FLAT_STRING(id), act == Wrapper::SET))
             return true;
     }
 
     if (IsWindow(name) && IsFrameId(cx, obj, id))
         return true;
 
-    // We only reach this point for cross origin location objects (see
-    // SameOriginOrCrossOriginAccessiblePropertiesOnly::check).
+    // Do the dynamic document.domain check.
+    //
+    // Location also needs a dynamic access check, but it's a different one, and
+    // we do it in LocationPolicy::check. Before LocationPolicy::check does that
+    // though, it first calls this function to check whether the property is
+    // accessible to anyone regardless of origin. So make sure not to do the
+    // document.domain check in that case.
     if (!IsLocation(name) && documentDomainMakesSameOrigin(cx, obj))
         return true;
 
     return (act == Wrapper::SET)
            ? nsContentUtils::IsCallerTrustedForWrite()
            : nsContentUtils::IsCallerTrustedForRead();
 }
 
@@ -486,16 +502,20 @@ ExposedPropertiesOnly::check(JSContext *
                              Permission &perm)
 {
     JSObject *wrappedObject = Wrapper::wrappedObject(wrapper);
 
     if (act == Wrapper::CALL) {
         perm = PermitObjectAccess;
         return true;
     }
+    if (act == Wrapper::PUNCTURE) {
+        perm = DenyAccess;
+        return false;
+    }
 
     perm = DenyAccess;
 
     jsid exposedPropsId = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EXPOSEDPROPS);
 
     JSBool found = false;
     JSAutoEnterCompartment ac;
     if (!ac.enter(cx, wrappedObject) ||
--- a/js/xpconnect/wrappers/AccessCheck.h
+++ b/js/xpconnect/wrappers/AccessCheck.h
@@ -34,16 +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 "jsapi.h"
 #include "jswrapper.h"
+#include "WrapperFactory.h"
 
 class nsIPrincipal;
 
 namespace xpc {
 
 nsIPrincipal *
 GetCompartmentPrincipal(JSCompartment *compartment);
 
@@ -100,40 +101,73 @@ struct OnlyIfSubjectIsSystem : public Po
     }
 };
 
 // This policy only permits access to properties that are safe to be used
 // across origins.
 struct CrossOriginAccessiblePropertiesOnly : public Policy {
     static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
                       Permission &perm) {
+        // Location objects should always use LocationPolicy.
+        MOZ_ASSERT(!WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
+
         if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act)) {
             perm = PermitPropertyAccess;
             return true;
         }
         perm = DenyAccess;
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, wrapper))
             return false;
         AccessCheck::deny(cx, id);
         return false;
     }
 };
 
-// This policy only permits access to properties that are safe to be used
-// across origins.
-struct SameOriginOrCrossOriginAccessiblePropertiesOnly : public Policy {
+// We need a special security policy for Location objects.
+//
+// Location objects are special because their effective principal is that of
+// the outer window, not the inner window. So while the security characteristics
+// of most objects can be inferred from their compartments, those of the Location
+// object cannot. This has two implications:
+//
+// 1 - Same-compartment access of Location objects is not necessarily allowed.
+//     This means that objects must see a security wrapper around Location objects
+//     in their own compartment.
+// 2 - Cross-origin access of Location objects is not necessarily forbidden.
+//     Since the security decision depends on the current state of the outer window,
+//     we can't make it at wrap time. Instead, we need to make it at the time of
+//     access.
+//
+// So for any Location object access, be it same-compartment or cross-compartment,
+// we need to do a dynamic security check to determine whether the outer window is
+// same-origin with the caller.
+//
+// So this policy first checks whether the access is something that any code,
+// same-origin or not, is allowed to make. If it isn't, it _also_ checks the
+// state of the outer window to determine whether we happen to be same-origin
+// at the moment.
+struct LocationPolicy : public Policy {
     static bool check(JSContext *cx, JSObject *wrapper, jsid id, js::Wrapper::Action act,
                       Permission &perm) {
+        // We should only be dealing with Location objects here.
+        MOZ_ASSERT(WrapperFactory::IsLocationObject(js::UnwrapObject(wrapper)));
+
+        // Default to deny.
+        perm = DenyAccess;
+
+        // Location object security is complicated enough. Don't allow punctures.
+        if (act == js::Wrapper::PUNCTURE)
+            return false;
+
         if (AccessCheck::isCrossOriginAccessPermitted(cx, wrapper, id, act) ||
             AccessCheck::isLocationObjectSameOrigin(cx, wrapper)) {
             perm = PermitPropertyAccess;
             return true;
         }
-        perm = DenyAccess;
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, wrapper))
             return false;
         AccessCheck::deny(cx, id);
         return false;
     }
 };
 
--- a/js/xpconnect/wrappers/FilteringWrapper.cpp
+++ b/js/xpconnect/wrappers/FilteringWrapper.cpp
@@ -141,33 +141,33 @@ FilteringWrapper<Base, Policy>::enter(JS
 #define COW FilteringWrapper<CrossCompartmentSecurityWrapper, ExposedPropertiesOnly>
 #define XOW FilteringWrapper<XrayWrapper<CrossCompartmentSecurityWrapper>, \
                              CrossOriginAccessiblePropertiesOnly>
 #define PXOW   FilteringWrapper<XrayProxy, \
                                 CrossOriginAccessiblePropertiesOnly>
 #define NNXOW FilteringWrapper<CrossCompartmentSecurityWrapper, \
                                CrossOriginAccessiblePropertiesOnly>
 #define LW    FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>, \
-                               SameOriginOrCrossOriginAccessiblePropertiesOnly>
+                               LocationPolicy>
 #define XLW   FilteringWrapper<XrayWrapper<CrossCompartmentSecurityWrapper>, \
-                               SameOriginOrCrossOriginAccessiblePropertiesOnly>
+                               LocationPolicy>
 
 template<> SOW SOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::SOW_FLAG);
 template<> SCSOW SCSOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                   WrapperFactory::SOW_FLAG);
 template<> COW COW::singleton(0);
 template<> XOW XOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                               WrapperFactory::PARTIALLY_TRANSPARENT);
 template<> PXOW PXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                 WrapperFactory::PARTIALLY_TRANSPARENT);
 template<> NNXOW NNXOW::singleton(WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG |
                                   WrapperFactory::PARTIALLY_TRANSPARENT);
-template<> LW  LW::singleton(0);
-template<> XLW XLW::singleton(0);
+template<> LW  LW::singleton(WrapperFactory::SHADOWING_FORBIDDEN);
+template<> XLW XLW::singleton(WrapperFactory::SHADOWING_FORBIDDEN);
 
 template class SOW;
 template class COW;
 template class XOW;
 template class PXOW;
 template class NNXOW;
 template class LW;
 template class XLW;
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -32,17 +32,16 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
-#include "WrapperFactory.h"
 #include "CrossOriginWrapper.h"
 #include "FilteringWrapper.h"
 #include "XrayWrapper.h"
 #include "AccessCheck.h"
 #include "XPCWrapper.h"
 
 #include "xpcprivate.h"
 #include "dombindings.h"
@@ -282,17 +281,17 @@ WrapperFactory::Rewrap(JSContext *cx, JS
     NS_ASSERTION(!IsWrapper(obj) ||
                  GetProxyHandler(obj) == &WaiveXrayWrapperWrapper ||
                  js::GetObjectClass(obj)->ext.innerObject,
                  "wrapped object passed to rewrap");
     NS_ASSERTION(JS_GetClass(obj) != &XrayUtils::HolderClass, "trying to wrap a holder");
 
     JSCompartment *origin = js::GetObjectCompartment(obj);
     JSCompartment *target = js::GetContextCompartment(cx);
-    JSObject *xrayHolder = nsnull;
+    bool usingXray = false;
 
     Wrapper *wrapper;
     CompartmentPrivate *targetdata =
         static_cast<CompartmentPrivate *>(JS_GetCompartmentPrivate(target));
     if (AccessCheck::isChrome(target)) {
         if (AccessCheck::isChrome(origin)) {
             wrapper = &CrossCompartmentWrapper::singleton;
         } else {
@@ -314,21 +313,19 @@ WrapperFactory::Rewrap(JSContext *cx, JS
                 wrapper = &CrossOriginWrapper::singleton;
             } else {
                 // Native objects must be wrapped into an X-ray wrapper.
                 bool proxy;
                 if (CanXray(obj, &proxy)) {
                     if (proxy) {
                         wrapper = &XrayProxy::singleton;
                     } else {
-                        typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
+                        typedef XrayWrapper<CrossCompartmentWrapper> Xray;
+                        usingXray = true;
                         wrapper = &Xray::singleton;
-                        xrayHolder = Xray::createHolder(cx, obj, parent);
-                        if (!xrayHolder)
-                            return nsnull;
                     }
                 } else {
                     wrapper = &NoWaiverWrapper::singleton;
                 }
             }
         }
     } else if (AccessCheck::isChrome(origin)) {
         JSFunction *fun = JS_GetObjectFunction(obj);
@@ -339,41 +336,50 @@ WrapperFactory::Rewrap(JSContext *cx, JS
             }
         }
 
         XPCWrappedNative *wn;
         if (targetdata &&
             (wn = GetWrappedNative(cx, obj)) &&
             wn->HasProto() && wn->GetProto()->ClassIsDOMObject()) {
             typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
-            wrapper = &FilteringWrapper<Xray,
-                                        CrossOriginAccessiblePropertiesOnly>::singleton;
-            xrayHolder = Xray::createHolder(cx, obj, parent);
-            if (!xrayHolder)
-                return nsnull;
+            usingXray = true;
+            if (IsLocationObject(obj))
+                wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
+            else
+                wrapper = &FilteringWrapper<Xray, CrossOriginAccessiblePropertiesOnly>::singleton;
         } else {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         ExposedPropertiesOnly>::singleton;
         }
     } else if (AccessCheck::isSameOrigin(origin, target)) {
-        // Same origin we use a transparent wrapper, unless the compartment asks
-        // for an Xray or the wrapper needs a SOW.
+        // For the same-origin case we use a transparent wrapper, unless one
+        // of the following is true:
+        // * The wrapper is a Location object.
+        // * The wrapper is flagged as needing a SOW.
+        // * The context compartment specifically requested Xray vision into
+        //   same-origin compartments.
+        //
+        // The first two cases always require a security wrapper for non-chrome
+        // access, regardless of the origin of the object.
         bool proxy;
         if (AccessCheck::needsSystemOnlyWrapper(obj)) {
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         OnlyIfSubjectIsSystem>::singleton;
+        } else if (IsLocationObject(obj)) {
+            typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
+            usingXray = true;
+            wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
         } else if (targetdata && targetdata->wantXrays && CanXray(obj, &proxy)) {
             if (proxy) {
                 wrapper = &XrayProxy::singleton;
             } else {
                 typedef XrayWrapper<CrossCompartmentWrapper> Xray;
+                usingXray = true;
                 wrapper = &Xray::singleton;
-                xrayHolder = Xray::createHolder(cx, obj, parent);
-                if (!xrayHolder)
-                    return nsnull;
             }
         } else {
             wrapper = &CrossCompartmentWrapper::singleton;
         }
     } else {
         NS_ASSERTION(!AccessCheck::needsSystemOnlyWrapper(obj),
                      "bad object exposed across origins");
 
@@ -386,56 +392,54 @@ WrapperFactory::Rewrap(JSContext *cx, JS
             wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper,
                                         CrossOriginAccessiblePropertiesOnly>::singleton;
         } else {
             if (proxy) {
                 wrapper = &FilteringWrapper<XrayProxy,
                                             CrossOriginAccessiblePropertiesOnly>::singleton;
             } else {
                 typedef XrayWrapper<CrossCompartmentSecurityWrapper> Xray;
+                usingXray = true;
 
                 // Location objects can become same origin after navigation, so we might
                 // have to grant transparent access later on.
                 if (IsLocationObject(obj)) {
-                    wrapper = &FilteringWrapper<Xray,
-                        SameOriginOrCrossOriginAccessiblePropertiesOnly>::singleton;
+                    wrapper = &FilteringWrapper<Xray, LocationPolicy>::singleton;
                 } else {
                     wrapper = &FilteringWrapper<Xray,
                         CrossOriginAccessiblePropertiesOnly>::singleton;
                 }
-
-                xrayHolder = Xray::createHolder(cx, obj, parent);
-                if (!xrayHolder)
-                    return nsnull;
             }
         }
     }
 
     JSObject *wrapperObj = Wrapper::New(cx, obj, wrappedProto, parent, wrapper);
-    if (!wrapperObj || !xrayHolder)
+    if (!wrapperObj || !usingXray)
         return wrapperObj;
 
+    JSObject *xrayHolder = XrayUtils::createHolder(cx, obj, parent);
+    if (!xrayHolder)
+        return nsnull;
     js::SetProxyExtra(wrapperObj, 0, js::ObjectValue(*xrayHolder));
     return wrapperObj;
 }
 
-typedef FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>,
-                         SameOriginOrCrossOriginAccessiblePropertiesOnly> LW;
+typedef FilteringWrapper<XrayWrapper<SameCompartmentSecurityWrapper>, LocationPolicy> LW;
 
 bool
 WrapperFactory::IsLocationObject(JSObject *obj)
 {
     const char *name = js::GetObjectClass(obj)->name;
     return name[0] == 'L' && !strcmp(name, "Location");
 }
 
 JSObject *
 WrapperFactory::WrapLocationObject(JSContext *cx, JSObject *obj)
 {
-    JSObject *xrayHolder = LW::createHolder(cx, obj, js::GetObjectParent(obj));
+    JSObject *xrayHolder = XrayUtils::createHolder(cx, obj, js::GetObjectParent(obj));
     if (!xrayHolder)
         return nsnull;
     JSObject *wrapperObj = Wrapper::New(cx, obj, js::GetObjectProto(obj), js::GetObjectParent(obj),
                                         &LW::singleton);
     if (!wrapperObj)
         return nsnull;
     js::SetProxyExtra(wrapperObj, 0, js::ObjectValue(*xrayHolder));
     return wrapperObj;
--- a/js/xpconnect/wrappers/WrapperFactory.h
+++ b/js/xpconnect/wrappers/WrapperFactory.h
@@ -32,28 +32,37 @@
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
+#ifndef _xpc_WRAPPERFACTORY_H
+#define _xpc_WRAPPERFACTORY_H
+
 #include "jsapi.h"
 #include "jswrapper.h"
 
 namespace xpc {
 
 class WrapperFactory {
   public:
     enum { WAIVE_XRAY_WRAPPER_FLAG = js::Wrapper::LAST_USED_FLAG << 1,
            IS_XRAY_WRAPPER_FLAG    = WAIVE_XRAY_WRAPPER_FLAG << 1,
            SCRIPT_ACCESS_ONLY_FLAG = IS_XRAY_WRAPPER_FLAG << 1,
            PARTIALLY_TRANSPARENT   = SCRIPT_ACCESS_ONLY_FLAG << 1,
-           SOW_FLAG                = PARTIALLY_TRANSPARENT << 1 };
+           SOW_FLAG                = PARTIALLY_TRANSPARENT << 1,
+
+           // Prevent scripts from shadowing native properties.
+           // NB: Applies only to Xray wrappers.
+           // NB: This will prevent scriptable helpers from defining special
+           //     handlers for properties defined in IDL. Use with caution.
+           SHADOWING_FORBIDDEN     = SOW_FLAG << 1 };
 
     // Return true if any of any of the nested wrappers have the flag set.
     static bool HasWrapperFlag(JSObject *wrapper, unsigned flag) {
         unsigned flags = 0;
         js::UnwrapObject(wrapper, true, &flags);
         return !!(flags & flag);
     }
 
@@ -64,16 +73,20 @@ class WrapperFactory {
     static bool IsPartiallyTransparent(JSObject *wrapper) {
         return HasWrapperFlag(wrapper, PARTIALLY_TRANSPARENT);
     }
 
     static bool HasWaiveXrayFlag(JSObject *wrapper) {
         return HasWrapperFlag(wrapper, WAIVE_XRAY_WRAPPER_FLAG);
     }
 
+    static bool IsShadowingForbidden(JSObject *wrapper) {
+        return HasWrapperFlag(wrapper, SHADOWING_FORBIDDEN);
+    }
+
     static JSObject *WaiveXray(JSContext *cx, JSObject *obj);
 
     static JSObject *DoubleWrap(JSContext *cx, JSObject *obj, unsigned flags);
 
     // Prepare a given object for wrapping in a new compartment.
     static JSObject *PrepareForWrapping(JSContext *cx,
                                         JSObject *scope,
                                         JSObject *obj,
@@ -97,8 +110,10 @@ class WrapperFactory {
 
     // Wrap a (same compartment) object in a SOW.
     static JSObject *WrapSOWObject(JSContext *cx, JSObject *obj);
 };
 
 extern js::Wrapper WaiveXrayWrapperWrapper;
 
 }
+
+#endif /* _xpc_WRAPPERFACTORY_H */
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -98,25 +98,57 @@ IsResolving(JSObject *holder, jsid id)
 }
 
 static JSBool
 holder_get(JSContext *cx, JSObject *holder, jsid id, jsval *vp);
 
 static JSBool
 holder_set(JSContext *cx, JSObject *holder, jsid id, JSBool strict, jsval *vp);
 
+
+static XPCWrappedNative *GetWrappedNative(JSObject *obj);
+
 namespace XrayUtils {
 
 JSClass HolderClass = {
     "NativePropertyHolder",
     JSCLASS_HAS_RESERVED_SLOTS(3),
     JS_PropertyStub,        JS_PropertyStub, holder_get,      holder_set,
     JS_EnumerateStub,       JS_ResolveStub,  JS_ConvertStub
 };
 
+JSObject *
+createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent)
+{
+    JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, nsnull, parent);
+    if (!holder)
+        return nsnull;
+
+    CompartmentPrivate *priv =
+        (CompartmentPrivate *)JS_GetCompartmentPrivate(js::GetObjectCompartment(holder));
+    JSObject *inner = JS_ObjectToInnerObject(cx, wrappedNative);
+    XPCWrappedNative *wn = GetWrappedNative(inner);
+    Value expando = ObjectOrNullValue(priv->LookupExpandoObject(wn));
+
+    // A note about ownership: the holder has a direct pointer to the wrapped
+    // native that we're wrapping. Normally, we'd have to AddRef the pointer
+    // so that it doesn't have to be collected, but then we'd have to tell the
+    // cycle collector. Fortunately for us, we know that the Xray wrapper
+    // itself has a reference to the flat JS object which will hold the
+    // wrapped native alive. Furthermore, the reachability of that object and
+    // the associated holder are exactly the same, so we can use that for our
+    // strong reference.
+    JS_ASSERT(IS_WN_WRAPPER(wrappedNative) ||
+              js::GetObjectClass(wrappedNative)->ext.innerObject);
+    js::SetReservedSlot(holder, JSSLOT_WN, PrivateValue(wn));
+    js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(NULL));
+    js::SetReservedSlot(holder, JSSLOT_EXPANDO, expando);
+    return holder;
+}
+
 }
 
 using namespace XrayUtils;
 
 static JSObject *
 GetHolder(JSObject *obj)
 {
     return &js::GetProxyExtra(obj, 0).toObject();
@@ -784,16 +816,28 @@ XrayWrapper<Base>::getOwnPropertyDescrip
 template <typename Base>
 bool
 XrayWrapper<Base>::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                                   js::PropertyDescriptor *desc)
 {
     JSObject *holder = GetHolder(wrapper);
     JSPropertyDescriptor *jsdesc = desc;
 
+    // If shadowing is forbidden, see if the id corresponds to an underlying
+    // native property.
+    if (WrapperFactory::IsShadowingForbidden(wrapper)) {
+        js::PropertyDescriptor nativeProp;
+        if (!ResolveNativeProperty(cx, wrapper, holder, id, false, &nativeProp))
+            return false;
+        if (nativeProp.obj) {
+            JS_ReportError(cx, "Permission denied to shadow native property");
+            return false;
+        }
+    }
+
     // Redirect access straight to the wrapper if we should be transparent.
     if (XrayUtils::IsTransparent(cx, wrapper)) {
         JSObject *wnObject = GetWrappedNativeObjectFromHolder(holder);
 
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, wnObject))
             return false;
 
@@ -1037,45 +1081,16 @@ XrayWrapper<Base>::construct(JSContext *
                 XPCThrower::Throw(rv, cx);
             return false;
         }
     }
 
     return true;
 }
 
-template <typename Base>
-JSObject *
-XrayWrapper<Base>::createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent)
-{
-    JSObject *holder = JS_NewObjectWithGivenProto(cx, &HolderClass, nsnull, parent);
-    if (!holder)
-        return nsnull;
-
-    CompartmentPrivate *priv =
-        (CompartmentPrivate *)JS_GetCompartmentPrivate(js::GetObjectCompartment(holder));
-    JSObject *inner = JS_ObjectToInnerObject(cx, wrappedNative);
-    XPCWrappedNative *wn = GetWrappedNative(inner);
-    Value expando = ObjectOrNullValue(priv->LookupExpandoObject(wn));
-
-    // A note about ownership: the holder has a direct pointer to the wrapped
-    // native that we're wrapping. Normally, we'd have to AddRef the pointer
-    // so that it doesn't have to be collected, but then we'd have to tell the
-    // cycle collector. Fortunately for us, we know that the Xray wrapper
-    // itself has a reference to the flat JS object which will hold the
-    // wrapped native alive. Furthermore, the reachability of that object and
-    // the associated holder are exactly the same, so we can use that for our
-    // strong reference.
-    JS_ASSERT(IS_WN_WRAPPER(wrappedNative) ||
-              js::GetObjectClass(wrappedNative)->ext.innerObject);
-    js::SetReservedSlot(holder, JSSLOT_WN, PrivateValue(wn));
-    js::SetReservedSlot(holder, JSSLOT_RESOLVING, PrivateValue(NULL));
-    js::SetReservedSlot(holder, JSSLOT_EXPANDO, expando);
-    return holder;
-}
 
 XrayProxy::XrayProxy(unsigned flags)
   : XrayWrapper<CrossCompartmentWrapper>(flags)
 {
 }
 
 XrayProxy::~XrayProxy()
 {
@@ -1225,16 +1240,19 @@ XrayProxy::getOwnPropertyDescriptor(JSCo
     // Own properties don't get cached on the holder. Just return.
     return true;
 }
 
 bool
 XrayProxy::defineProperty(JSContext *cx, JSObject *wrapper, jsid id,
                           js::PropertyDescriptor *desc)
 {
+    // This wouldn't be hard to support, but we don't need it right now.
+    MOZ_ASSERT(!WrapperFactory::IsShadowingForbidden(wrapper));
+
     JSObject *holder = GetHolderObject(cx, wrapper);
     if (!holder)
         return false;
 
     JSObject *obj = &js::GetProxyPrivate(wrapper).toObject();
     if (XrayUtils::IsTransparent(cx, wrapper)) {
         JSAutoEnterCompartment ac;
         if (!ac.enter(cx, obj) || !JS_WrapPropertyDescriptor(cx, desc))
--- a/js/xpconnect/wrappers/XrayWrapper.h
+++ b/js/xpconnect/wrappers/XrayWrapper.h
@@ -47,16 +47,18 @@
 // little world around them.
 
 namespace xpc {
 
 namespace XrayUtils {
 
 extern JSClass HolderClass;
 
+JSObject *createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent);
+
 bool
 IsTransparent(JSContext *cx, JSObject *wrapper);
 
 JSObject *
 GetNativePropertiesObject(JSContext *cx, JSObject *wrapper);
 
 }
 
@@ -89,18 +91,16 @@ class XrayWrapper : public Base {
     virtual bool hasOwn(JSContext *cx, JSObject *wrapper, jsid id, bool *bp);
     virtual bool keys(JSContext *cx, JSObject *wrapper, js::AutoIdVector &props);
     virtual bool iterate(JSContext *cx, JSObject *wrapper, unsigned flags, js::Value *vp);
 
     virtual bool call(JSContext *cx, JSObject *wrapper, unsigned argc, js::Value *vp);
     virtual bool construct(JSContext *cx, JSObject *wrapper,
                            unsigned argc, js::Value *argv, js::Value *rval);
 
-    static JSObject *createHolder(JSContext *cx, JSObject *wrappedNative, JSObject *parent);
-
     static XrayWrapper singleton;
 
   private:
     bool resolveOwnProperty(JSContext *cx, JSObject *wrapper, jsid id, bool set,
                             js::PropertyDescriptor *desc);
 };
 
 class XrayProxy : public XrayWrapper<js::CrossCompartmentWrapper> {
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -4045,18 +4045,19 @@ nsLayoutUtils::SurfaceFromElementResult
 nsLayoutUtils::SurfaceFromElement(dom::Element* aElement,
                                   PRUint32 aSurfaceFlags)
 {
   SurfaceFromElementResult result;
   nsresult rv;
 
   bool forceCopy = (aSurfaceFlags & SFE_WANT_NEW_SURFACE) != 0;
   bool wantImageSurface = (aSurfaceFlags & SFE_WANT_IMAGE_SURFACE) != 0;
-
-  if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) {
+  bool premultAlpha = (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) == 0;
+
+  if (!premultAlpha) {
     forceCopy = true;
     wantImageSurface = true;
   }
 
   // If it's a <canvas>, we may be able to just grab its internal surface
   if (nsHTMLCanvasElement* canvas = nsHTMLCanvasElement::FromContent(aElement)) {
     gfxIntSize size = canvas->GetSize();
 
@@ -4077,31 +4078,26 @@ nsLayoutUtils::SurfaceFromElement(dom::E
       if (wantImageSurface) {
         surf = new gfxImageSurface(size, gfxASurface::ImageFormatARGB32);
       } else {
         surf = gfxPlatform::GetPlatform()->CreateOffscreenSurface(size, gfxASurface::CONTENT_COLOR_ALPHA);
       }
 
       nsRefPtr<gfxContext> ctx = new gfxContext(surf);
       // XXX shouldn't use the external interface, but maybe we can layerify this
-      rv = canvas->RenderContextsExternal(ctx, gfxPattern::FILTER_NEAREST);
+      PRUint32 flags = premultAlpha ? nsHTMLCanvasElement::RenderFlagPremultAlpha : 0;
+      rv = canvas->RenderContextsExternal(ctx, gfxPattern::FILTER_NEAREST, flags);
       if (NS_FAILED(rv))
         return result;
     }
 
     // Ensure that any future changes to the canvas trigger proper invalidation,
     // in case this is being used by -moz-element()
     canvas->MarkContextClean();
 
-    if (aSurfaceFlags & SFE_NO_PREMULTIPLY_ALPHA) {
-      // we can modify this surface since we force a copy above when
-      // when NO_PREMULTIPLY_ALPHA is set
-      gfxUtils::UnpremultiplyImageSurface(static_cast<gfxImageSurface*>(surf.get()));
-    }
-
     result.mSurface = surf;
     result.mSize = size;
     result.mPrincipal = aElement->NodePrincipal();
     result.mIsWriteOnly = canvas->IsWriteOnly();
 
     return result;
   }
 
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -2482,17 +2482,17 @@ RecoverPluginGeometry(nsDisplayListBuild
       // Ignore plugins which aren't supposed to be affected by this
       // operation --- their bounds will not have been included in the
       // display list computations so the visible region computed for them
       // would be incorrect
       nsPtrHashKey<nsObjectFrame>* entry =
         aClosure->mAffectedPlugins.GetEntry(f);
       // Windowed plugins in transforms are always ignored, we don't
       // create configurations for them
-      if (entry && (!aInTransform || !f->GetWidget())) {
+      if (entry && (!aInTransform || f->PaintedByGecko())) {
         displayPlugin->GetWidgetConfiguration(aBuilder,
                                               aClosure->mOutputConfigurations);
         // we've dealt with this plugin now
         aClosure->mAffectedPlugins.RawRemoveEntry(entry);
       }
       break;
     }
     case nsDisplayItem::TYPE_TRANSFORM: {
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -369,55 +369,16 @@ nsFrameList::GetLength() const
   nsIFrame* frame = mFirstChild;
   while (frame) {
     count++;
     frame = frame->GetNextSibling();
   }
   return count;
 }
 
-static int CompareByContentOrder(const nsIFrame* aF1, const nsIFrame* aF2)
-{
-  if (aF1->GetContent() != aF2->GetContent()) {
-    return nsLayoutUtils::CompareTreePosition(aF1->GetContent(), aF2->GetContent());
-  }
-
-  if (aF1 == aF2) {
-    return 0;
-  }
-
-  const nsIFrame* f;
-  for (f = aF2; f; f = f->GetPrevInFlow()) {
-    if (f == aF1) {
-      // f1 comes before f2 in the flow
-      return -1;
-    }
-  }
-  for (f = aF1; f; f = f->GetPrevInFlow()) {
-    if (f == aF2) {
-      // f1 comes after f2 in the flow
-      return 1;
-    }
-  }
-
-  NS_ASSERTION(false, "Frames for same content but not in relative flow order");
-  return 0;
-}
-
-class CompareByContentOrderComparator
-{
-  public:
-  bool Equals(const nsIFrame* aA, const nsIFrame* aB) const {
-    return aA == aB;
-  }
-  bool LessThan(const nsIFrame* aA, const nsIFrame* aB) const {
-    return CompareByContentOrder(aA, aB) < 0;
-  }
-};
-
 void
 nsFrameList::ApplySetParent(nsIFrame* aParent) const
 {
   NS_ASSERTION(aParent, "null ptr");
 
   for (nsIFrame* f = FirstChild(); f; f = f->GetNextSibling()) {
     f->SetParent(aParent);
   }
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -2160,9 +2160,19 @@ nsObjectFrame::EndSwapDocShells(nsIConte
 }
 
 nsIFrame*
 NS_NewObjectFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsObjectFrame(aContext);
 }
 
+bool
+nsObjectFrame::PaintedByGecko()
+{
+#ifdef XP_MACOSX
+  return true;
+#else
+  return !mWidget;
+#endif
+}
+
 NS_IMPL_FRAMEARENA_HELPERS(nsObjectFrame)
--- a/layout/generic/nsObjectFrame.h
+++ b/layout/generic/nsObjectFrame.h
@@ -187,16 +187,18 @@ public:
    */
   static void BeginSwapDocShells(nsIContent* aContent, void*);
   /**
    * If aContent has a nsObjectFrame, then set it up after a DocShell swap.
    * @see nsSubDocumentFrame::EndSwapDocShells.
    */
   static void EndSwapDocShells(nsIContent* aContent, void*);
 
+  bool PaintedByGecko();
+
   nsIWidget* GetWidget() { return mWidget; }
 
   /**
    * Adjust the plugin's idea of its size, using aSize as its new size.
    * (aSize must be in twips)
    */
   void FixupWindow(const nsSize& aSize);
 
--- a/layout/generic/nsTextRunTransformations.cpp
+++ b/layout/generic/nsTextRunTransformations.cpp
@@ -402,17 +402,17 @@ nsCaseTransformTextRunFactory::RebuildTe
     }
 
     if (IS_IN_BMP(ch)) {
       convertedString.Append(ch);
     } else {
       convertedString.Append(H_SURROGATE(ch));
       convertedString.Append(L_SURROGATE(ch));
       i++;
-      charsToMergeArray.AppendElement(true);
+      charsToMergeArray.AppendElement(false);
       styleArray.AppendElement(styles[i]);
       canBreakBeforeArray.AppendElement(false);
     }
 
     if (extraChar) {
       ++extraCharsCount;
       charsToMergeArray.AppendElement(true);
       styleArray.AppendElement(styles[i]);
--- a/layout/reftests/editor/reftest.list
+++ b/layout/reftests/editor/reftest.list
@@ -39,16 +39,17 @@ fails-if(Android) != spellcheck-input-at
 == spellcheck-input-property-dynamic-override.html spellcheck-input-nofocus-ref.html
 fails-if(Android) != spellcheck-input-property-dynamic-override.html spellcheck-input-ref.html
 == spellcheck-input-property-dynamic-override-inherit.html spellcheck-input-nofocus-ref.html
 fails-if(Android) != spellcheck-input-property-dynamic-override-inherit.html spellcheck-input-ref.html
 == spellcheck-textarea-attr.html spellcheck-textarea-nofocus-ref.html
 fails-if(Android) != spellcheck-textarea-attr.html spellcheck-textarea-ref.html
 needs-focus == spellcheck-textarea-focused.html spellcheck-textarea-ref.html
 needs-focus == spellcheck-textarea-focused-reframe.html spellcheck-textarea-ref.html
+needs-focus == spellcheck-textarea-focused-notreadonly.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-nofocus.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-disabled.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-attr-inherit.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-attr-dynamic.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-attr-dynamic-inherit.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-property-dynamic.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-property-dynamic-inherit.html spellcheck-textarea-ref.html
 fails-if(Android) != spellcheck-textarea-attr-dynamic-override.html spellcheck-textarea-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/editor/spellcheck-textarea-focused-notreadonly.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<body>
+
+  <textarea id="testBox" readonly></textarea>
+  <script type="text/javascript">
+    //Adding focus to the textbox should trigger a spellcheck
+    var textbox = document.getElementById("testBox");
+    addEventListener("load", function() {
+      textbox.readOnly = false;
+      textbox.focus();
+      textbox.value = "blahblahblah";
+      textbox.selectionStart = textbox.selectionEnd = 0;
+    }, false);
+  </script>
+
+</body>
+</html>
--- a/mobile/android/base/App.java.in
+++ b/mobile/android/base/App.java.in
@@ -35,16 +35,19 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #filter substitution
 package @ANDROID_PACKAGE_NAME@;
 
 import org.mozilla.gecko.GeckoApp;
 import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoEvent;
+import org.mozilla.gecko.R;
+import android.view.MenuItem;
 
 public class App extends GeckoApp {
     public String getPackageName() {
 	return "@ANDROID_PACKAGE_NAME@";
     }
 
     public String getContentProcessName() {
         return "@MOZ_CHILD_PROCESS_NAME@";
@@ -60,10 +63,21 @@ public class App extends GeckoApp {
     public String getUAStringForHost(String host) {
         // With our standard UA String, we get a 200 response code and 
         // client-side redirect from t.co. This bot-like UA gives us a 
         // 301 response code
         if ("t.co".equals(host))
             return "Redirector/@MOZ_APP_VERSION@ (Android; rv:@MOZ_APP_VERSION@)";
         return getDefaultUAString();
     }
+
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+#ifdef MOZ_PROFILING
+        if (item.getItemId() == R.id.toggle_profiling) {
+            GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("ToggleProfiling", null));
+            return true;
+        }
+#endif
+        return super.onOptionsItemSelected(item);
+    }
 };
 
--- a/mobile/android/base/AwesomeBar.java
+++ b/mobile/android/base/AwesomeBar.java
@@ -56,16 +56,17 @@ import android.os.Bundle;
 import android.text.Editable;
 import android.text.Spanned;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.ContextMenu;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.KeyEvent;
+import android.view.LayoutInflater;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.view.inputmethod.EditorInfo;
 import android.widget.AdapterView;
 import android.widget.Button;
 import android.widget.EditText;
@@ -104,16 +105,17 @@ public class AwesomeBar extends Activity
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         Log.d(LOGTAG, "creating awesomebar");
 
         mResolver = Tabs.getInstance().getContentResolver();
+        LayoutInflater.from(this).setFactory(GeckoViewsFactory.getInstance());
 
         setContentView(R.layout.awesomebar);
 
         mGoButton = (ImageButton) findViewById(R.id.awesomebar_button);
         mText = (AwesomeBarEditText) findViewById(R.id.awesomebar_text);
 
         TabWidget tabWidget = (TabWidget) findViewById(android.R.id.tabs);
         tabWidget.setDividerDrawable(null);
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -1565,17 +1565,18 @@ abstract public class GeckoApp
         for (View view : tab.getPluginViews()) {
             PluginLayoutParams lp = (PluginLayoutParams)view.getLayoutParams();
             lp.reposition(targetViewport);
 
             if (setVisible) {
                 view.setVisibility(View.VISIBLE);
             }
 
-            mPluginContainer.updateViewLayout(view, lp);
+            if (mPluginContainer.indexOfChild(view) >= 0)
+                mPluginContainer.updateViewLayout(view, lp);
         }
     }
 
     public void setFullScreen(final boolean fullscreen) {
         mMainHandler.post(new Runnable() { 
             public void run() {
                 // Hide/show the system notification bar
                 Window window = getWindow();
@@ -1618,16 +1619,18 @@ abstract public class GeckoApp
         Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - onCreate");
         if (savedInstanceState != null) {
             mLastTitle = savedInstanceState.getString(SAVED_STATE_TITLE);
             mLastViewport = savedInstanceState.getString(SAVED_STATE_VIEWPORT);
             mLastScreen = savedInstanceState.getByteArray(SAVED_STATE_SCREEN);
             mRestoreSession = savedInstanceState.getBoolean(SAVED_STATE_SESSION);
         }
 
+        LayoutInflater.from(this).setFactory(GeckoViewsFactory.getInstance());
+
         super.onCreate(savedInstanceState);
 
         mOrientation = getResources().getConfiguration().orientation;
 
         setContentView(R.layout.gecko_app);
 
         LinearLayout actionBar;
         if (Build.VERSION.SDK_INT >= 11) {
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/GeckoViewsFactory.java
@@ -0,0 +1,59 @@
+/* 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 org.mozilla.gecko;
+
+import java.util.HashMap;
+
+import android.content.Context;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+
+public final class GeckoViewsFactory implements LayoutInflater.Factory {
+    private static final String LOGTAG = "GeckoViewsFactory";
+
+    private static final String GECKO_VIEW_IDENTIFIER = "org.mozilla.gecko.";
+    private static final int GECKO_VIEW_IDENTIFIER_LENGTH = GECKO_VIEW_IDENTIFIER.length();
+
+    // List of custom views used
+    private static final int ABOUT_HOME_SECTION = 1;
+    private static final int AWESOME_BAR_TABS = 2;
+    private static final int FORM_ASSIST_POPUP = 3;
+    private static final int LINK_TEXT_VIEW = 4;
+
+    private GeckoViewsFactory() { }
+
+    // Making this a singleton class.
+    private static final GeckoViewsFactory INSTANCE = new GeckoViewsFactory();
+
+    public static GeckoViewsFactory getInstance() {
+        return INSTANCE;
+    }
+
+    @Override
+    public View onCreateView(String name, Context context, AttributeSet attrs) {
+        if (!TextUtils.isEmpty(name) && name.startsWith(GECKO_VIEW_IDENTIFIER)) {
+            String viewName = name.substring(GECKO_VIEW_IDENTIFIER_LENGTH);
+
+            if (TextUtils.isEmpty(viewName))
+                return null;
+        
+            Log.i(LOGTAG, "Creating custom Gecko view: " + viewName);
+
+            if (TextUtils.equals(viewName, "AboutHomeSection"))
+                return new AboutHomeSection(context, attrs);
+            else if (TextUtils.equals(viewName, "AwesomeBarTabs"))
+                return new AwesomeBarTabs(context, attrs);
+            else if (TextUtils.equals(viewName, "FormAssistPopup"))
+                return new FormAssistPopup(context, attrs);
+            else if (TextUtils.equals(viewName, "LinkTextView"))
+                return new LinkTextView(context, attrs);
+        }
+
+        return null;
+    }
+}
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -92,16 +92,17 @@ FENNEC_JAVA_FILES = \
   GeckoInputConnection.java \
   GeckoJarReader.java \
 	GeckoMessageReceiver.java \
   GeckoPreferences.java \
   GeckoProfile.java \
   GeckoStateListDrawable.java \
   GeckoThread.java \
   GlobalHistory.java \
+  GeckoViewsFactory.java \
   LinkPreference.java \
   LinkTextView.java \
 	NSSBridge.java \
   ProfileMigrator.java \
   PromptService.java \
   sqlite/ByteBufferInputStream.java \
   sqlite/MatrixBlobCursor.java \
   sqlite/SQLiteBridge.java \
@@ -169,16 +170,18 @@ FENNEC_PP_JAVA_FILES = \
   db/FormHistoryProvider.java \
   db/TabsProvider.java \
   db/GeckoProvider.java \
   SmsManager.java \
   $(NULL)
 
 FENNEC_PP_XML_FILES = \
   res/layout/abouthome_content.xml \
+  res/menu/gecko_menu.xml \
+  res/menu-v11/gecko_menu.xml \
   $(NULL)
 
 
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
@@ -525,21 +528,16 @@ RES_DRAWABLE_LAND_XHDPI_V14 = \
   res/drawable-land-xhdpi-v14/tabs_button_tail.9.png \
   $(NULL)
 
 RES_COLOR = \
   res/color/awesomebar_tab_text.xml
 
 RES_MENU = \
   res/menu/awesomebar_contextmenu.xml \
-  res/menu/gecko_menu.xml \
-  $(NULL)
-
-RES_MENU_V11 = \
-  res/menu-v11/gecko_menu.xml \
   $(NULL)
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
 ifdef MOZ_CRASHREPORTER
 FENNEC_PP_JAVA_FILES += CrashReporter.java
 MOZ_ANDROID_DRAWABLES += mobile/android/base/resources/drawable/crash_reporter.png
 RES_LAYOUT += res/layout/crash_reporter.xml
@@ -592,17 +590,17 @@ MOZ_ANDROID_DRAWABLES += \
   mobile/android/base/resources/drawable/tabs_tray_close_button.xml             \
   mobile/android/base/resources/drawable/tabs_tray_list_divider.xml             \
   mobile/android/base/resources/drawable/tabs_tray_list_selector.xml            \
   mobile/android/base/resources/drawable/shadow.png                             \
   $(NULL)
 
 MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' ';  fi)
 
-RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_V11) $(RES_LAYOUT_LAND_V14) $(RES_VALUES) $(RES_VALUES_V11) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_NODPI) $(RES_DRAWABLE_BASE) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_V14) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_COLOR) $(RES_MENU) $(RES_MENU_V11)
+RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_V11) $(RES_LAYOUT_LAND_V14) $(RES_VALUES) $(RES_VALUES_V11) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_NODPI) $(RES_DRAWABLE_BASE) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_V14) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_COLOR) $(RES_MENU)
 
 RES_DIRS= \
   res/layout              \
   res/layout-v11          \
   res/layout-land-v14     \
   res/values              \
   res/values-v11          \
   res/xml                 \
@@ -618,16 +616,17 @@ RES_DIRS= \
   res/drawable-hdpi-v11   \
   res/drawable-xhdpi-v11  \
   res/drawable-land-v14   \
   res/drawable-land-mdpi-v14  \
   res/drawable-land-hdpi-v14  \
   res/drawable-land-xhdpi-v14 \
   res/color                   \
   res/menu                    \
+  res/menu-v11                \
   $(NULL)
 
 
 include $(topsrcdir)/config/rules.mk
 
 # Override the Java settings with some specific android settings
 include $(topsrcdir)/config/android-common.mk
 
--- a/mobile/android/base/TabsTray.java
+++ b/mobile/android/base/TabsTray.java
@@ -48,16 +48,18 @@ public class TabsTray extends Activity i
     private static final int TABS_ADD_TAB_HEIGHT = 50;
 
     private static final String ABOUT_HOME = "about:home";
 
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        LayoutInflater.from(this).setFactory(GeckoViewsFactory.getInstance());
+
         setContentView(R.layout.tabs_tray);
 
         mWaitingForClose = false;
 
         mList = (ListView) findViewById(R.id.list);
         mListContainer = (TabsListContainer) findViewById(R.id.list_container);
 
         ImageButton addTab = (ImageButton) findViewById(R.id.add_tab);
--- a/mobile/android/base/db/BrowserContract.java.in
+++ b/mobile/android/base/db/BrowserContract.java.in
@@ -77,16 +77,21 @@ public class BrowserContract {
         public static final String TITLE = "title";
     }
 
     public interface ImageColumns {
         public static final String FAVICON = "favicon";
         public static final String THUMBNAIL = "thumbnail";
     }
 
+    public interface HistoryColumns {
+        public static final String DATE_LAST_VISITED = "date";
+        public static final String VISITS = "visits";
+    }
+
     public interface DeletedColumns {
         public static final String ID = "id";
         public static final String GUID = "guid";
         public static final String TIME_DELETED = "timeDeleted";
     }
 
     public static final class Images implements CommonColumns, ImageColumns, SyncColumns {
         private Images() {}
@@ -124,24 +129,30 @@ public class BrowserContract {
         public static final String TYPE = "type";
         public static final String PARENT = "parent";
         public static final String POSITION = "position";
         public static final String TAGS = "tags";
         public static final String DESCRIPTION = "description";
         public static final String KEYWORD = "keyword";
     }
 
-    public static final class History implements CommonColumns, URLColumns, ImageColumns, SyncColumns {
+    public static final class History implements CommonColumns, URLColumns, HistoryColumns, ImageColumns, SyncColumns {
         private History() {}
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "history");
         public static final String CONTENT_TYPE = "vnd.android.cursor.dir/browser-history";
         public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/browser-history";
+    }
 
-        public static final String DATE_LAST_VISITED = "date";
-        public static final String VISITS = "visits";
+    // Combined bookmarks and history
+    public static final class Combined implements CommonColumns, URLColumns, HistoryColumns, ImageColumns  {
+        private Combined() {}
+        public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "combined");
+
+        public static final String BOOKMARK_ID = "bookmark_id";
+        public static final String HISTORY_ID = "history_id";
     }
 
     public static final class Schema {
         private Schema() {}
         public static final Uri CONTENT_URI = Uri.withAppendedPath(AUTHORITY_URI, "schema");
 
         public static final String VERSION = "version";
     }
--- a/mobile/android/base/db/BrowserProvider.java.in
+++ b/mobile/android/base/db/BrowserProvider.java.in
@@ -14,16 +14,17 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Random;
 
 import org.mozilla.gecko.GeckoBackgroundThread;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
+import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.CommonColumns;
 import org.mozilla.gecko.db.BrowserContract.History;
 import org.mozilla.gecko.db.BrowserContract.Images;
 import org.mozilla.gecko.db.BrowserContract.Schema;
 import org.mozilla.gecko.db.BrowserContract.SyncColumns;
 import org.mozilla.gecko.db.BrowserContract.URLColumns;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.db.DBUtils;
@@ -50,32 +51,33 @@ import android.text.TextUtils;
 import android.util.Log;
 
 public class BrowserProvider extends ContentProvider {
     private static final String LOGTAG = "GeckoBrowserProvider";
     private Context mContext;
 
     static final String DATABASE_NAME = "browser.db";
 
-    static final int DATABASE_VERSION = 4;
+    static final int DATABASE_VERSION = 5;
 
     // Maximum age of deleted records to be cleaned up (20 days in ms)
     static final long MAX_AGE_OF_DELETED_RECORDS = 86400000 * 20;
 
     // Number of records marked as deleted to be removed
     static final long DELETED_RECORDS_PURGE_LIMIT = 5;
 
     static final String TABLE_BOOKMARKS = "bookmarks";
     static final String TABLE_HISTORY = "history";
     static final String TABLE_IMAGES = "images";
 
     static final String TABLE_BOOKMARKS_TMP = TABLE_BOOKMARKS + "_tmp";
 
     static final String VIEW_BOOKMARKS_WITH_IMAGES = "bookmarks_with_images";
     static final String VIEW_HISTORY_WITH_IMAGES = "history_with_images";
+    static final String VIEW_COMBINED_WITH_IMAGES = "combined_with_images";
 
     // Bookmark matches
     static final int BOOKMARKS = 100;
     static final int BOOKMARKS_ID = 101;
     static final int BOOKMARKS_FOLDER_ID = 102;
     static final int BOOKMARKS_PARENT = 103;
     static final int BOOKMARKS_POSITIONS = 104;
 
@@ -85,16 +87,19 @@ public class BrowserProvider extends Con
 
     // Image matches
     static final int IMAGES = 300;
     static final int IMAGES_ID = 301;
 
     // Schema matches
     static final int SCHEMA = 400;
 
+    // Combined bookmarks and history matches
+    static final int COMBINED = 500;
+
     static final String DEFAULT_BOOKMARKS_SORT_ORDER = Bookmarks.TYPE
             + " ASC, " + Bookmarks.POSITION + " ASC, " + Bookmarks._ID
             + " ASC";
 
     static final String DEFAULT_HISTORY_SORT_ORDER = History.DATE_LAST_VISITED + " DESC";
 
     static final String TABLE_BOOKMARKS_JOIN_IMAGES = TABLE_BOOKMARKS + " LEFT OUTER JOIN " +
             TABLE_IMAGES + " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " +
@@ -104,16 +109,17 @@ public class BrowserProvider extends Con
             TABLE_IMAGES + " ON " + qualifyColumn(TABLE_HISTORY, History.URL) + " = " +
             qualifyColumn(TABLE_IMAGES, Images.URL);
 
     static final UriMatcher URI_MATCHER = new UriMatcher(UriMatcher.NO_MATCH);
 
     static final Map<String, String> BOOKMARKS_PROJECTION_MAP;
     static final Map<String, String> HISTORY_PROJECTION_MAP;
     static final Map<String, String> IMAGES_PROJECTION_MAP;
+    static final Map<String, String> COMBINED_PROJECTION_MAP;
     static final Map<String, String> SCHEMA_PROJECTION_MAP;
 
     static {
         // We will reuse this.
         HashMap<String, String> map;
 
         // Bookmarks
         URI_MATCHER.addURI(BrowserContract.AUTHORITY, "bookmarks", BOOKMARKS);
@@ -169,16 +175,31 @@ public class BrowserProvider extends Con
         map.put(Images.FAVICON_URL, Images.FAVICON_URL);
         map.put(Images.THUMBNAIL, Images.THUMBNAIL);
         map.put(Images.DATE_CREATED, Images.DATE_CREATED);
         map.put(Images.DATE_MODIFIED, Images.DATE_MODIFIED);
         map.put(Images.GUID, Images.GUID);
         map.put(Images.IS_DELETED, Images.IS_DELETED);
         IMAGES_PROJECTION_MAP = Collections.unmodifiableMap(map);
 
+        // Combined bookmarks and history
+        URI_MATCHER.addURI(BrowserContract.AUTHORITY, "combined", COMBINED);
+
+        map = new HashMap<String, String>();
+        map.put(Combined._ID, Combined._ID);
+        map.put(Combined.BOOKMARK_ID, Combined.BOOKMARK_ID);
+        map.put(Combined.HISTORY_ID, Combined.HISTORY_ID);
+        map.put(Combined.URL, Combined.URL);
+        map.put(Combined.TITLE, Combined.TITLE);
+        map.put(Combined.VISITS, Combined.VISITS);
+        map.put(Combined.DATE_LAST_VISITED, Combined.DATE_LAST_VISITED);
+        map.put(Combined.FAVICON, Combined.FAVICON);
+        map.put(Combined.THUMBNAIL, Combined.THUMBNAIL);
+        COMBINED_PROJECTION_MAP = Collections.unmodifiableMap(map);
+
         // Schema
         URI_MATCHER.addURI(BrowserContract.AUTHORITY, "schema", SCHEMA);
 
         map = new HashMap<String, String>();
         map.put(Schema.VERSION, Schema.VERSION);
         SCHEMA_PROJECTION_MAP = Collections.unmodifiableMap(map);
     }
 
@@ -332,26 +353,76 @@ public class BrowserProvider extends Con
             debug("Creating " + VIEW_HISTORY_WITH_IMAGES + " view");
 
             db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_HISTORY_WITH_IMAGES + " AS " +
                     "SELECT " + qualifyColumn(TABLE_HISTORY, "*") +
                     ", " + Images.FAVICON + ", " + Images.THUMBNAIL + " FROM " +
                     TABLE_HISTORY_JOIN_IMAGES);
         }
 
+        private void createCombinedWithImagesView(SQLiteDatabase db) {
+            debug("Creating " + VIEW_COMBINED_WITH_IMAGES + " view");
+
+            db.execSQL("CREATE VIEW IF NOT EXISTS " + VIEW_COMBINED_WITH_IMAGES + " AS" +
+                    " SELECT " + Combined.BOOKMARK_ID + ", " +
+                                 Combined.HISTORY_ID + ", " +
+                                 // We need to return an _id column because CursorAdapter requires it for its
+                                 // default implementation for the getItemId() method. However, since
+                                 // we're not using this feature in the parts of the UI using this view,
+                                 // we can just use 0 for all rows.
+                                 "0 AS " + Combined._ID + ", " +
+                                 Combined.URL + ", " +
+                                 Combined.TITLE + ", " +
+                                 Combined.VISITS + ", " +
+                                 Combined.DATE_LAST_VISITED + ", " +
+                                 qualifyColumn(TABLE_IMAGES, Images.FAVICON) + " AS " + Combined.FAVICON + ", " +
+                                 qualifyColumn(TABLE_IMAGES, Images.THUMBNAIL) + " AS " + Combined.THUMBNAIL +
+                    " FROM (" +
+                        // Bookmarks without history.
+                        " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " +
+                                     qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " AS " + Combined.URL + ", " +
+                                     qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + " AS " + Combined.TITLE + ", " +
+                                     "-1 AS " + Combined.HISTORY_ID + ", " +
+                                     "-1 AS " + Combined.VISITS + ", " +
+                                     "-1 AS " + Combined.DATE_LAST_VISITED +
+                        " FROM " + TABLE_BOOKMARKS +
+                        " WHERE " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE)  + " = " + Bookmarks.TYPE_BOOKMARK + " AND " +
+                                    qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) +
+                                        " NOT IN (SELECT " + History.URL + " FROM " + TABLE_HISTORY + ")" +
+                        " UNION ALL" +                
+                        // History with and without bookmark.
+                        " SELECT " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks._ID) + " AS " + Combined.BOOKMARK_ID + ", " +
+                                     qualifyColumn(TABLE_HISTORY, History.URL) + " AS " + Combined.URL + ", " +
+                                     // Prioritze bookmark titles over history titles, since the user may have
+                                     // customized the title for a bookmark.
+                                     "COALESCE(" + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TITLE) + ", " +
+                                                   qualifyColumn(TABLE_HISTORY, History.TITLE) +")" + " AS " + Combined.TITLE + ", " +
+                                     qualifyColumn(TABLE_HISTORY, History._ID) + " AS " + Combined.HISTORY_ID + ", " +
+                                     qualifyColumn(TABLE_HISTORY, History.VISITS) + " AS " + Combined.VISITS + ", " +
+                                     qualifyColumn(TABLE_HISTORY, History.DATE_LAST_VISITED) + " AS " + Combined.DATE_LAST_VISITED +
+                        " FROM " + TABLE_HISTORY + " LEFT OUTER JOIN " + TABLE_BOOKMARKS +
+                            " ON " + qualifyColumn(TABLE_BOOKMARKS, Bookmarks.URL) + " = " + qualifyColumn(TABLE_HISTORY, History.URL) +
+                        " WHERE " + qualifyColumn(TABLE_HISTORY, History.URL) + " IS NOT NULL AND (" +
+                                        qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE) + " IS NULL OR " +
+                                        qualifyColumn(TABLE_BOOKMARKS, Bookmarks.TYPE)  + " = " + Bookmarks.TYPE_BOOKMARK + ")" +
+                    ") LEFT OUTER JOIN " + TABLE_IMAGES +
+                        " ON " + Combined.URL + " = " + qualifyColumn(TABLE_IMAGES, Images.URL));
+        }
+
         @Override
         public void onCreate(SQLiteDatabase db) {
             debug("Creating browser.db: " + db.getPath());
 
             createBookmarksTable(db);
             createHistoryTable(db);
             createImagesTable(db);
 
             createBookmarksWithImagesView(db);
             createHistoryWithImagesView(db);
+            createCombinedWithImagesView(db);
 
             createOrUpdateSpecialFolder(db, Bookmarks.PLACES_FOLDER_GUID,
                 R.string.bookmarks_folder_places, 0);
 
             createOrUpdateAllSpecialFolders(db);
 
             // FIXME: Create default bookmarks here (bug 728224)
         }
@@ -553,16 +624,20 @@ public class BrowserProvider extends Con
 
             createHistoryWithImagesView(db);
         }
 
         private void upgradeDatabaseFrom3to4(SQLiteDatabase db) {
             migrateBookmarksTable(db, new BookmarkMigrator3to4());
         }
 
+        private void upgradeDatabaseFrom4to5(SQLiteDatabase db) {
+            createCombinedWithImagesView(db);
+        }
+
         @Override
         public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
             debug("Upgrading browser.db: " + db.getPath() + " from " +
                     oldVersion + " to " + newVersion);
 
             db.beginTransaction();
 
             // We have to do incremental upgrades until we reach the current
@@ -575,16 +650,20 @@ public class BrowserProvider extends Con
 
                     case 3:
                         upgradeDatabaseFrom2to3(db);
                         break;
 
                     case 4:
                         upgradeDatabaseFrom3to4(db);
                         break;
+
+                    case 5:
+                        upgradeDatabaseFrom4to5(db);
+                        break;
                  }
              }
 
              db.setTransactionSuccessful();
              db.endTransaction();
         }
 
         @Override
@@ -1246,16 +1325,28 @@ public class BrowserProvider extends Con
             case SCHEMA: {
                 debug("Query is on schema.");
                 MatrixCursor schemaCursor = new MatrixCursor(new String[] { Schema.VERSION });
                 schemaCursor.newRow().add(DATABASE_VERSION);
 
                 return schemaCursor;
             }
 
+            case COMBINED: {
+                debug("Query is on combined: " + uri);
+
+                if (TextUtils.isEmpty(sortOrder))
+                    sortOrder = DEFAULT_HISTORY_SORT_ORDER;
+
+                qb.setProjectionMap(COMBINED_PROJECTION_MAP);
+                qb.setTables(VIEW_COMBINED_WITH_IMAGES);
+
+                break;
+            }
+
             default:
                 throw new UnsupportedOperationException("Unknown query URI " + uri);
         }
 
         trace("Running built query.");
         Cursor cursor = qb.query(db, projection, selection, selectionArgs, null,
                 null, sortOrder, limit);
         cursor.setNotificationUri(getContext().getContentResolver(),
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -40,16 +40,17 @@
 package org.mozilla.gecko.db;
 
 import java.io.ByteArrayOutputStream;
 
 import org.mozilla.gecko.db.BrowserContract.Bookmarks;
 import org.mozilla.gecko.db.BrowserContract.History;
 import org.mozilla.gecko.db.BrowserContract.ImageColumns;
 import org.mozilla.gecko.db.BrowserContract.Images;
+import org.mozilla.gecko.db.BrowserContract.Combined;
 import org.mozilla.gecko.db.BrowserContract.URLColumns;
 import org.mozilla.gecko.db.DBUtils;
 
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.database.ContentObserver;
 import android.database.Cursor;
@@ -83,16 +84,17 @@ public class LocalBrowserDB implements B
 
     // Use wrapped Boolean so that we can have a null state
     private Boolean mDesktopBookmarksExist;
 
     private final Uri mBookmarksUriWithProfile;
     private final Uri mParentsUriWithProfile;
     private final Uri mHistoryUriWithProfile;
     private final Uri mImagesUriWithProfile;
+    private final Uri mCombinedUriWithProfile;
     private final Uri mDeletedHistoryUriWithProfile;
 
     private static final String[] DEFAULT_BOOKMARK_COLUMNS =
             new String[] { Bookmarks._ID,
                            Bookmarks.GUID,
                            Bookmarks.URL,
                            Bookmarks.TITLE,
                            Bookmarks.TYPE,
@@ -104,16 +106,17 @@ public class LocalBrowserDB implements B
         mProfile = profile;
         mMobileFolderId = -1;
         mDesktopBookmarksExist = null;
 
         mBookmarksUriWithProfile = appendProfile(Bookmarks.CONTENT_URI);
         mParentsUriWithProfile = appendProfile(Bookmarks.PARENTS_CONTENT_URI);
         mHistoryUriWithProfile = appendProfile(History.CONTENT_URI);
         mImagesUriWithProfile = appendProfile(Images.CONTENT_URI);
+        mCombinedUriWithProfile = appendProfile(Combined.CONTENT_URI);
 
         mDeletedHistoryUriWithProfile = mHistoryUriWithProfile.buildUpon().
             appendQueryParameter(BrowserContract.PARAM_SHOW_DELETED, "1").build();
     }
 
     // Invalidate cached data
     public void invalidateCachedState() {
         mDesktopBookmarksExist = null;
@@ -124,70 +127,75 @@ public class LocalBrowserDB implements B
                                                                        String.valueOf(limit)).build();
     }
 
     private Uri bookmarksUriWithLimit(int limit) {
         return mBookmarksUriWithProfile.buildUpon().appendQueryParameter(BrowserContract.PARAM_LIMIT,
                                                                          String.valueOf(limit)).build();
     }
 
+    private Uri combinedUriWithLimit(int limit) {
+        return mCombinedUriWithProfile.buildUpon().appendQueryParameter(BrowserContract.PARAM_LIMIT,
+                String.valueOf(limit)).build();
+    }
+
     private Uri appendProfile(Uri uri) {
         return uri.buildUpon().appendQueryParameter(BrowserContract.PARAM_PROFILE, mProfile).build();
     }
 
     private Cursor filterAllSites(ContentResolver cr, String[] projection, CharSequence constraint,
             int limit, CharSequence urlFilter) {
-        // The history selection queries for sites with a url or title
+        // The combined history/bookmarks selection queries for sites with a url or title
         // containing the constraint string
-        String selection = "(" + History.URL + " LIKE ? OR " +
-                                 History.TITLE + " LIKE ?)";
+        String selection = "(" + Combined.URL + " LIKE ? OR " +
+                                 Combined.TITLE + " LIKE ?)";
 
         final String historySelectionArg = "%" + constraint.toString() + "%";
         String[] selectionArgs = new String[] { historySelectionArg, historySelectionArg };
 
         if (urlFilter != null) {
-            selection = DBUtils.concatenateWhere(selection, "(" + History.URL + " NOT LIKE ?)");
+            selection = DBUtils.concatenateWhere(selection, "(" + Combined.URL + " NOT LIKE ?)");
             selectionArgs = DBUtils.appendSelectionArgs(selectionArgs, new String[] { urlFilter.toString() });
         }
 
         // Our version of frecency is computed by scaling the number of visits by a multiplier
         // that approximates Gaussian decay, based on how long ago the entry was last visited.
         // Since we're limited by the math we can do with sqlite, we're calculating this
         // approximation using the Cauchy distribution: multiplier = 15^2 / (age^2 + 15^2).
         // Using 15 as our scale parameter, we get a constant 15^2 = 225. Following this math,
         // frecencyScore = numVisits * max(1, 100 * 225 / (age*age + 225)). (See bug 704977)
-        final String age = "(" + History.DATE_LAST_VISITED + " - " + System.currentTimeMillis() + ") / 86400000";
-        final String sortOrder = History.VISITS + " * MAX(1, 100 * 225 / (" + age + "*" + age + " + 225)) DESC";
+        final String age = "(" + Combined.DATE_LAST_VISITED + " - " + System.currentTimeMillis() + ") / 86400000";
+        final String sortOrder = Combined.VISITS + " * MAX(1, 100 * 225 / (" + age + "*" + age + " + 225)) DESC";
 
-        Cursor c = cr.query(historyUriWithLimit(limit),
+        Cursor c = cr.query(combinedUriWithLimit(limit),
                             projection,
                             selection,
                             selectionArgs,
                             sortOrder);
 
         return new LocalDBCursor(c);
     }
 
     public Cursor filter(ContentResolver cr, CharSequence constraint, int limit) {
         return filterAllSites(cr,
-                              new String[] { History._ID,
-                                             History.URL,
-                                             History.TITLE,
-                                             History.FAVICON },
+                              new String[] { Combined._ID,
+                                             Combined.URL,
+                                             Combined.TITLE,
+                                             Combined.FAVICON },
                               constraint,
                               limit,
                               null);
     }
 
     public Cursor getTopSites(ContentResolver cr, int limit) {
         return filterAllSites(cr,
-                              new String[] { History._ID,
-                                             History.URL,
-                                             History.TITLE,
-                                             History.THUMBNAIL },
+                              new String[] { Combined._ID,
+                                             Combined.URL,
+                                             Combined.TITLE,
+                                             Combined.THUMBNAIL },
                               "",
                               limit,
                               BrowserDB.ABOUT_PAGES_URL_FILTER);
     }
 
     private void truncateHistory(ContentResolver cr) {
         Cursor cursor = null;
 
deleted file mode 100644
--- a/mobile/android/base/resources/menu-v11/gecko_menu.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item android:id="@+id/reload"
-          android:icon="@drawable/ic_menu_reload"
-          android:title="@string/reload"/>
-
-    <item android:id="@+id/forward"
-          android:icon="@drawable/ic_menu_forward"
-          android:title="@string/forward"/>
-
-    <item android:id="@+id/bookmark"
-          android:icon="@drawable/ic_menu_bookmark_add"
-          android:title="@string/bookmark"/>
-
-    <item android:id="@+id/share"
-          android:icon="@drawable/ic_menu_share"
-          android:title="@string/share" /> 
-    
-    <item android:id="@+id/save_as_pdf"
-          android:icon="@drawable/ic_menu_save_as_pdf"
-          android:title="@string/save_as_pdf" />
-
-    <item android:id="@+id/site_settings"
-          android:title="@string/site_settings_title" />
-
-    <item android:id="@+id/addons"
-          android:title="@string/addons"/>
-
-    <item android:id="@+id/downloads"
-          android:title="@string/downloads"/>
-
-    <item android:id="@+id/char_encoding"
-          android:visible="false"
-          android:title="@string/char_encoding"/>
-
-    <item android:id="@+id/settings"
-          android:title="@string/settings" />
-
-    <item android:id="@+id/quit"
-          android:title="@string/quit"
-          android:orderInCategory="10" />
-</menu>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/menu-v11/gecko_menu.xml.in
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/reload"
+          android:icon="@drawable/ic_menu_reload"
+          android:title="@string/reload"/>
+
+    <item android:id="@+id/forward"
+          android:icon="@drawable/ic_menu_forward"
+          android:title="@string/forward"/>
+
+    <item android:id="@+id/bookmark"
+          android:icon="@drawable/ic_menu_bookmark_add"
+          android:title="@string/bookmark"/>
+
+    <item android:id="@+id/share"
+          android:icon="@drawable/ic_menu_share"
+          android:title="@string/share" /> 
+    
+    <item android:id="@+id/save_as_pdf"
+          android:icon="@drawable/ic_menu_save_as_pdf"
+          android:title="@string/save_as_pdf" />
+
+    <item android:id="@+id/site_settings"
+          android:title="@string/site_settings_title" />
+
+    <item android:id="@+id/addons"
+          android:title="@string/addons"/>
+
+    <item android:id="@+id/downloads"
+          android:title="@string/downloads"/>
+
+    <item android:id="@+id/char_encoding"
+          android:visible="false"
+          android:title="@string/char_encoding"/>
+
+    <item android:id="@+id/settings"
+          android:title="@string/settings" />
+
+#ifdef MOZ_PROFILING
+    <item android:id="@+id/toggle_profiling"
+          android:title="@string/toggle_profiling" />
+#endif
+
+    <item android:id="@+id/quit"
+          android:title="@string/quit"
+          android:orderInCategory="10" />
+</menu>
deleted file mode 100644
--- a/mobile/android/base/resources/menu/gecko_menu.xml
+++ /dev/null
@@ -1,44 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<menu xmlns:android="http://schemas.android.com/apk/res/android">
-
-    <item android:id="@+id/reload"
-          android:icon="@drawable/ic_menu_reload"
-          android:title="@string/reload"/>
-
-    <item android:id="@+id/forward"
-          android:icon="@drawable/ic_menu_forward"
-          android:title="@string/forward"/>
-
-    <item android:id="@+id/bookmark"
-          android:icon="@drawable/ic_menu_bookmark_add"
-          android:title="@string/bookmark"/>
-
-    <item android:id="@+id/share"
-          android:icon="@drawable/ic_menu_share"
-          android:title="@string/share" /> 
-    
-    <item android:id="@+id/save_as_pdf"
-          android:icon="@drawable/ic_menu_save_as_pdf"
-          android:title="@string/save_as_pdf" />
-
-    <item android:id="@+id/site_settings"
-          android:title="@string/site_settings_title" />
-
-    <item android:id="@+id/addons"
-          android:title="@string/addons"/>
-
-    <item android:id="@+id/downloads"
-          android:title="@string/downloads"
-          android:visible="false"/>
-
-    <item android:id="@+id/char_encoding"
-          android:visible="false"
-          android:title="@string/char_encoding"/>
-
-    <item android:id="@+id/settings"
-          android:title="@string/settings" />
-
-    <item android:id="@+id/quit"
-          android:title="@string/quit"
-          android:orderInCategory="10" />
-</menu>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/menu/gecko_menu.xml.in
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:id="@+id/reload"
+          android:icon="@drawable/ic_menu_reload"
+          android:title="@string/reload"/>
+
+    <item android:id="@+id/forward"
+          android:icon="@drawable/ic_menu_forward"
+          android:title="@string/forward"/>
+
+    <item android:id="@+id/bookmark"
+          android:icon="@drawable/ic_menu_bookmark_add"
+          android:title="@string/bookmark"/>
+
+    <item android:id="@+id/share"
+          android:icon="@drawable/ic_menu_share"
+          android:title="@string/share" /> 
+    
+    <item android:id="@+id/save_as_pdf"
+          android:icon="@drawable/ic_menu_save_as_pdf"
+          android:title="@string/save_as_pdf" />
+
+    <item android:id="@+id/site_settings"
+          android:title="@string/site_settings_title" />
+
+    <item android:id="@+id/addons"
+          android:title="@string/addons"/>
+
+    <item android:id="@+id/downloads"
+          android:title="@string/downloads"
+          android:visible="false"/>
+
+    <item android:id="@+id/char_encoding"
+          android:visible="false"
+          android:title="@string/char_encoding"/>
+
+    <item android:id="@+id/settings"
+          android:title="@string/settings" />
+
+#ifdef MOZ_PROFILING
+    <item android:id="@+id/toggle_profiling"
+          android:title="@string/toggle_profiling" />
+#endif
+
+    <item android:id="@+id/quit"
+          android:title="@string/quit"
+          android:orderInCategory="10" />
+</menu>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -83,16 +83,19 @@
 
   <string name="reload">&reload;</string>
   <string name="forward">&forward;</string>
   <string name="new_tab">&new_tab;</string>
   <string name="new_tab_opened">&new_tab_opened;</string>
   <string name="addons">&addons;</string>
   <string name="downloads">&downloads;</string>
   <string name="char_encoding">&char_encoding;</string>
+  <!-- This string only appears in developer builds, which
+       is why it is not localizable. -->
+  <string name="toggle_profiling">Toggle Profiling</string>
 
   <string name="site_settings_title">&site_settings_title;</string>
   <string name="site_settings_cancel">&site_settings_cancel;</string>
   <string name="site_settings_clear">&site_settings_clear;</string>
   <string name="site_settings_no_settings">&site_settings_no_settings;</string>
 
   <string name="contextmenu_open_new_tab">&contextmenu_open_new_tab;</string>
   <string name="contextmenu_remove_bookmark">&contextmenu_remove_bookmark;</string>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/CommandProcessor.java
@@ -0,0 +1,83 @@
+/* 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 org.mozilla.gecko.sync;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.json.simple.JSONArray;
+
+public class CommandProcessor {
+  private static final String LOG_TAG = "Command";
+  protected ConcurrentHashMap<String, CommandRunner> commands = new ConcurrentHashMap<String, CommandRunner>();
+
+  private final static CommandProcessor processor = new CommandProcessor();
+
+  public static CommandProcessor getProcessor() {
+    return processor;
+  }
+
+  public static class Command {
+    public final String commandType;
+    public final List<String> args;
+
+    public Command(String commandType, List<String> args) {
+      this.commandType = commandType;
+      this.args = args;
+    }
+  }
+
+  public void registerCommand(String commandType, CommandRunner command) {
+    commands.put(commandType, command);
+  }
+
+  public void processCommand(ExtendedJSONObject unparsedCommand) {
+    Command command = parseCommand(unparsedCommand);
+    if (command == null) {
+      Logger.debug(LOG_TAG, "Invalid command: " + unparsedCommand + " will not be processed.");
+      return;
+    }
+
+    CommandRunner executableCommand = commands.get(command.commandType);
+    if (executableCommand == null) {
+      Logger.debug(LOG_TAG, "Command \"" + command.commandType + "\" not registered and will not be processed.");
+      return;
+    }
+
+    executableCommand.executeCommand(command.args);
+  }
+
+  /**
+   * Parse a JSON command into a ParsedCommand object for easier handling.
+   *
+   * @param unparsedCommand - command as ExtendedJSONObject
+   * @return - null if command is invalid, else return ParsedCommand with
+   *           no null attributes.
+   */
+  protected Command parseCommand(ExtendedJSONObject unparsedCommand) {
+    String type = (String) unparsedCommand.get("command");
+    if (type == null) {
+      return null;
+    }
+
+    try {
+      JSONArray unparsedArgs = unparsedCommand.getArray("args");
+      if (unparsedArgs == null) {
+        return null;
+      }
+      ArrayList<String> args = new ArrayList<String>(unparsedArgs.size());
+
+      for (int i = 0; i < unparsedArgs.size(); i++) {
+        args.add(unparsedArgs.get(i).toString());
+      }
+
+      return new Command(type, args);
+    } catch (NonArrayJSONException e) {
+      Logger.debug(LOG_TAG, "Unable to parse args array. Invalid command");
+      return null;
+    }
+  }
+}
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/sync/CommandRunner.java
@@ -0,0 +1,11 @@
+/* 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 org.mozilla.gecko.sync;
+
+import java.util.List;
+
+public interface CommandRunner {
+  public void executeCommand(List<String> args);
+}
--- a/mobile/android/base/sync/GlobalSession.java
+++ b/mobile/android/base/sync/GlobalSession.java
@@ -4,16 +4,17 @@
 
 package org.mozilla.gecko.sync;
 
 import java.io.IOException;
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
 import org.mozilla.gecko.sync.delegates.FreshStartDelegate;
 import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
@@ -150,19 +151,38 @@ public class GlobalSession implements Cr
 
     config = new SyncConfiguration(prefsPath, this);
     config.userAPI       = userAPI;
     config.serverURL     = serverURI;
     config.username      = username;
     config.password      = password;
     config.syncKeyBundle = syncKeyBundle;
 
+    registerCommands();
     prepareStages();
   }
 
+  protected void registerCommands() {
+    CommandProcessor processor = CommandProcessor.getProcessor();
+
+    processor.registerCommand("resetEngine", new CommandRunner() {
+      @Override
+      public void executeCommand(List<String> args) {
+        resetClient(new String[] { args.get(0) });
+      }
+    });
+
+    processor.registerCommand("resetAll", new CommandRunner() {
+      @Override
+      public void executeCommand(List<String> args) {
+        resetClient(null);
+      }
+    });
+  }
+
   protected void prepareStages() {
     stages = new HashMap<Stage, GlobalSyncStage>();
     stages.put(Stage.checkPreconditions,      new CheckPreconditionsStage());
     stages.put(Stage.ensureClusterURL,        new EnsureClusterURLStage());
     stages.put(Stage.fetchInfoCollections,    new FetchInfoCollectionsStage());
     stages.put(Stage.fetchMetaGlobal,         new FetchMetaGlobalStage());
     stages.put(Stage.ensureKeysStage,         new EnsureKeysStage());
     stages.put(Stage.syncClientsEngine,       new SyncClientsEngineStage());
@@ -410,17 +430,17 @@ public class GlobalSession implements Cr
     if (remoteSyncID == null) {
       // Corrupt meta/global.
       freshStart();
       return;
     }
     String localSyncID = this.getSyncID();
     if (!remoteSyncID.equals(localSyncID)) {
       // Sync ID has changed. Reset timestamps and fetch new keys.
-      resetClient();
+      resetClient(null);
       if (config.collectionKeys != null) {
         config.collectionKeys.clear();
       }
       config.syncID = remoteSyncID;
       // TODO TODO TODO
     }
     config.persistToPrefs();
     advance();
@@ -463,17 +483,17 @@ public class GlobalSession implements Cr
     final String newSyncID   = session.generateSyncID();
     final String metaURL     = session.config.metaURL();
     final String credentials = session.credentials();
 
     wipeServer(session, new WipeServerDelegate() {
 
       @Override
       public void onWiped(long timestamp) {
-        session.resetClient();
+        session.resetClient(null);
         session.config.collectionKeys.clear();      // TODO: make sure we clear our keys timestamp.
         session.config.persistToPrefs();
 
         MetaGlobal mg = new MetaGlobal(metaURL, credentials);
         mg.setSyncID(newSyncID);
         mg.setStorageVersion(STORAGE_VERSION);
 
         // It would be good to set the X-If-Unmodified-Since header to `timestamp`
@@ -638,20 +658,22 @@ public class GlobalSession implements Cr
     };
     request.delete();
   }
 
   /**
    * Reset our state. Clear our sync ID, reset each engine, drop any
    * cached records.
    */
-  private void resetClient() {
+  private void resetClient(String[] engines) {
+    if (engines == null) {
+      // Set `engines` to be *all* the engines.
+    }
     // TODO: futz with config?!
     // TODO: engines?!
-
   }
 
   /**
    * Suggest that your Sync client needs to be upgraded to work
    * with this server.
    */
   public void requiresUpgrade() {
     Logger.info(LOG_TAG, "Client outdated storage version; requires update.");
--- a/mobile/android/base/sync/net/BaseResource.java
+++ b/mobile/android/base/sync/net/BaseResource.java
@@ -112,18 +112,16 @@ public class BaseResource implements Res
     context.setAttribute(ClientContext.AUTH_CACHE, authCache);
   }
 
   /**
    * Apply the provided credentials string to the provided request.
    * @param credentials a string, "user:pass".
    */
   private static void applyCredentials(String credentials, HttpUriRequest request, HttpContext context) {
-    addAuthCacheToContext(request, context);
-
     Credentials creds = new UsernamePasswordCredentials(credentials);
     Header header = BasicScheme.authenticate(creds, "US-ASCII", false);
     request.addHeader(header);
     Logger.trace(LOG_TAG, "Adding Basic Auth header.");
   }
 
   /**
    * Invoke this after delegate and request have been set.
@@ -139,16 +137,18 @@ public class BaseResource implements Res
 
     // TODO: Eventually we should use Apache HttpAsyncClient. It's not out of alpha yet.
     // Until then, we synchronously make the request, then invoke our delegate's callback.
     String credentials = delegate.getCredentials();
     if (credentials != null) {
       BaseResource.applyCredentials(credentials, request, context);
     }
 
+    addAuthCacheToContext(request, context);
+
     HttpParams params = client.getParams();
     HttpConnectionParams.setConnectionTimeout(params, delegate.connectionTimeout());
     HttpConnectionParams.setSoTimeout(params, delegate.socketTimeout());
     HttpConnectionParams.setStaleCheckingEnabled(params, false);
     HttpProtocolParams.setContentCharset(params, charset);
     HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
     delegate.addHeaders(request, client);
   }
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java
@@ -206,17 +206,17 @@ public class AndroidBrowserBookmarksData
     cv.put(BrowserContract.Bookmarks.TYPE, rec.type.equalsIgnoreCase(TYPE_FOLDER) ?
                                            BrowserContract.Bookmarks.TYPE_FOLDER :
                                            BrowserContract.Bookmarks.TYPE_BOOKMARK);
 
     // Note that we don't set the modified timestamp: we allow the
     // content provider to do that for us.
     return cv;
   }
-  
+
   /**
    * Returns a cursor over non-deleted records that list the given androidID as a parent.
    */
   public Cursor getChildren(long androidID) throws NullCursorException {
     return getChildren(androidID, false);
   }
 
   /**
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
@@ -147,27 +147,29 @@ public class AndroidBrowserBookmarksRepo
    * = Fennec folders =
    *
    * guid        folder_id   parent
    * ----------  ----------  ----------
    * mobile      ?           0
    *
   */
   public static final Map<String, String> SPECIAL_GUID_PARENTS;
+
   static {
     HashMap<String, String> m = new HashMap<String, String>();
     m.put("places",  null);
     m.put("menu",    "places");
     m.put("toolbar", "places");
     m.put("tags",    "places");
     m.put("unfiled", "places");
     m.put("mobile",  "places");
     SPECIAL_GUID_PARENTS = Collections.unmodifiableMap(m);
   }
 
+
   /**
    * A map of guids to their localized name strings.
    */
   // Oh, if only we could make this final and initialize it in the static initializer.
   public static Map<String, String> SPECIAL_GUIDS_MAP;
 
   /**
    * Return true if the provided record GUID should be skipped
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataAccessor.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryDataAccessor.java
@@ -1,127 +1,96 @@
-/* ***** 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 Android Sync Client.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Jason Voll <jvoll@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/. */
 
 package org.mozilla.gecko.sync.repositories.android;
 
 import org.json.simple.JSONArray;
 import org.json.simple.JSONObject;
 import org.mozilla.gecko.db.BrowserContract;
+import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.repositories.domain.HistoryRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 
 import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
-import android.util.Log;
 
-public class AndroidBrowserHistoryDataAccessor extends AndroidBrowserRepositoryDataAccessor {
+public class AndroidBrowserHistoryDataAccessor extends
+    AndroidBrowserRepositoryDataAccessor {
 
   private AndroidBrowserHistoryDataExtender dataExtender;
 
   public AndroidBrowserHistoryDataAccessor(Context context) {
     super(context);
     dataExtender = new AndroidBrowserHistoryDataExtender(context);
   }
-  
+
   public AndroidBrowserHistoryDataExtender getHistoryDataExtender() {
     return dataExtender;
   }
 
   @Override
   protected Uri getUri() {
     return BrowserContractHelpers.HISTORY_CONTENT_URI;
   }
 
   @Override
   protected ContentValues getContentValues(Record record) {
     ContentValues cv = new ContentValues();
     HistoryRecord rec = (HistoryRecord) record;
-    cv.put(BrowserContract.History.GUID,          rec.guid);
-    cv.put(BrowserContract.History.TITLE,         rec.title);
-    cv.put(BrowserContract.History.URL,           rec.histURI);
+    cv.put(BrowserContract.History.GUID, rec.guid);
+    cv.put(BrowserContract.History.TITLE, rec.title);
+    cv.put(BrowserContract.History.URL, rec.histURI);
     if (rec.visits != null) {
       JSONArray visits = rec.visits;
       long mostRecent = 0;
       for (int i = 0; i < visits.size(); i++) {
         JSONObject visit = (JSONObject) visits.get(i);
-        long visitDate = (Long) visit.get(AndroidBrowserHistoryRepositorySession.KEY_DATE);
+        long visitDate = (Long) visit
+            .get(AndroidBrowserHistoryRepositorySession.KEY_DATE);
         if (visitDate > mostRecent) {
           mostRecent = visitDate;
         }
       }
       // Fennec stores milliseconds. The rest of Sync works in microseconds.
       cv.put(BrowserContract.History.DATE_LAST_VISITED, mostRecent / 1000);
       cv.put(BrowserContract.History.VISITS, Long.toString(visits.size()));
     }
     return cv;
   }
 
   @Override
   protected String[] getAllColumns() {
     return BrowserContractHelpers.HistoryColumns;
   }
-  
+
   @Override
   public Uri insert(Record record) {
     HistoryRecord rec = (HistoryRecord) record;
-    Log.d(LOG_TAG, "Storing visits for " + record.guid);
+    Logger.debug(LOG_TAG, "Storing visits for " + record.guid);
     dataExtender.store(record.guid, rec.visits);
-    Log.d(LOG_TAG, "Storing record " + record.guid);
+    Logger.debug(LOG_TAG, "Storing record " + record.guid);
     return super.insert(record);
   }
 
   @Override
   public void update(String oldGUID, Record newRecord) {
     HistoryRecord rec = (HistoryRecord) newRecord;
     String newGUID = newRecord.guid;
-    Log.d(LOG_TAG, "Storing visits for " + newGUID + ", replacing " + oldGUID);
+    Logger.debug(LOG_TAG, "Storing visits for " + newGUID + ", replacing " + oldGUID);
     dataExtender.delete(oldGUID);
     dataExtender.store(newGUID, rec.visits);
     super.update(oldGUID, newRecord);
   }
 
   @Override
-  protected void delete(String guid) {
-    Log.d(LOG_TAG, "Deleting record " + guid);
-    super.delete(guid);
+  public int purgeGuid(String guid) {
+    Logger.debug(LOG_TAG, "Purging record with " + guid);
     dataExtender.delete(guid);
+    return super.purgeGuid(guid);
   }
 
   public void closeExtender() {
     dataExtender.close();
   }
 }
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java
@@ -1,47 +1,15 @@
-/* ***** 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 Android Sync Client.
- *
- * The Initial Developer of the Original Code is
- * the Mozilla Foundation.
- * Portions created by the Initial Developer are Copyright (C) 2011
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- * Jason Voll <jvoll@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/. */
 
 package org.mozilla.gecko.sync.repositories.android;
 
+import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.sync.repositories.domain.PasswordRecord;
 import org.mozilla.gecko.sync.repositories.domain.Record;
 
 import android.content.ContentValues;
 import android.content.Context;
 import android.net.Uri;
 
 public class AndroidBrowserPasswordsDataAccessor extends AndroidBrowserRepositoryDataAccessor {
@@ -50,30 +18,30 @@ public class AndroidBrowserPasswordsData
     super(context);
   }
 
   @Override
   protected ContentValues getContentValues(Record record) {
     PasswordRecord rec = (PasswordRecord) record;
 
     ContentValues cv = new ContentValues();
-    cv.put(PasswordColumns.GUID,            rec.guid);
-    cv.put(PasswordColumns.HOSTNAME,        rec.hostname);
-    cv.put(PasswordColumns.HTTP_REALM,      rec.httpRealm);
-    cv.put(PasswordColumns.FORM_SUBMIT_URL, rec.formSubmitURL);
-    cv.put(PasswordColumns.USERNAME_FIELD,  rec.usernameField);
-    cv.put(PasswordColumns.PASSWORD_FIELD,  rec.passwordField);
+    cv.put(BrowserContract.Passwords.GUID,            rec.guid);
+    cv.put(BrowserContract.Passwords.HOSTNAME,        rec.hostname);
+    cv.put(BrowserContract.Passwords.HTTP_REALM,      rec.httpRealm);
+    cv.put(BrowserContract.Passwords.FORM_SUBMIT_URL, rec.formSubmitURL);
+    cv.put(BrowserContract.Passwords.USERNAME_FIELD,  rec.usernameField);
+    cv.put(BrowserContract.Passwords.PASSWORD_FIELD,  rec.passwordField);
     
     // TODO Do encryption of username/password here. Bug 711636
-    cv.put(PasswordColumns.ENC_TYPE,           rec.encType);
-    cv.put(PasswordColumns.ENCRYPTED_USERNAME, rec.username);
-    cv.put(PasswordColumns.ENCRYPTED_PASSWORD, rec.password);
+    cv.put(BrowserContract.Passwords.ENC_TYPE,           rec.encType);
+    cv.put(BrowserContract.Passwords.ENCRYPTED_USERNAME, rec.username);
+    cv.put(BrowserContract.Passwords.ENCRYPTED_PASSWORD, rec.password);
     
-    cv.put(PasswordColumns.TIMES_USED,     rec.timesUsed);
-    cv.put(PasswordColumns.TIME_LAST_USED, rec.timeLastUsed);
+    cv.put(BrowserContract.Passwords.TIMES_USED,     rec.timesUsed);
+    cv.put(BrowserContract.Passwords.TIME_LAST_USED, rec.timeLastUsed);
     return cv;
   }
 
   @Override
   protected Uri getUri() {
     return BrowserContractHelpers.PASSWORDS_CONTENT_URI;
   }
 
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java
@@ -56,19 +56,30 @@ public abstract class AndroidBrowserRepo
   private final RepoUtils.QueryHelper queryHelper;
 
   public AndroidBrowserRepositoryDataAccessor(Context context) {
     this.context = context;
     this.queryHelper = new RepoUtils.QueryHelper(context, getUri(), LOG_TAG);
   }
 
   protected abstract String[] getAllColumns();
+
+  /**
+   * Produce a <code>ContentValues</code> instance that represents the provided <code>Record</code>.
+   *
+   * @param record The <code>Record</code> to be converted.
+   * @return The <code>ContentValues</code> corresponding to <code>record</code>.
+   */
   protected abstract ContentValues getContentValues(Record record);
+
   protected abstract Uri getUri();
 
+  /**
+   * Dump all the records in raw format.
+   */
   public void dumpDB() {
     Cursor cur = null;
     try {
       cur = queryHelper.safeQuery(".dumpDB", null, null, null, null);
       RepoUtils.dumpCursor(cur);
     } catch (NullCursorException e) {
     } finally {
       if (cur != null) {
@@ -78,46 +89,44 @@ public abstract class AndroidBrowserRepo
   }
 
   public String dateModifiedWhere(long timestamp) {
     return BrowserContract.SyncColumns.DATE_MODIFIED + " >= " + Long.toString(timestamp);
   }
 
   public void wipe() {
     Uri uri = getUri();
-    Logger.info(LOG_TAG, "wiping: " + uri);
+    Logger.debug(LOG_TAG, "Wiping: " + uri);
     context.getContentResolver().delete(uri, null, null);
   }
-  
+
   public void purgeDeleted() throws NullCursorException {
     String where = BrowserContract.SyncColumns.IS_DELETED + "= 1";
-    Cursor cur = queryHelper.safeQuery(".purgeDeleted", GUID_COLUMNS, where, null, null);
+    Uri uri = getUri();
+    Logger.info(LOG_TAG, "Purging deleted from: " + uri);
+    context.getContentResolver().delete(uri, where, null);
+  }
 
-    try {
-      if (!cur.moveToFirst()) {
-        return;
-      }
-      while (!cur.isAfterLast()) {
-        delete(RepoUtils.getStringFromCursor(cur, BrowserContract.SyncColumns.GUID));
-        cur.moveToNext();
-      }
-    } finally {
-      cur.close();
-    }
-  }
-  
-  protected void delete(String guid) {
+  /**
+   * Remove matching records from the database entirely, i.e., do not set a
+   * deleted flag, delete entirely.
+   *
+   * @param guid
+   *          The GUID of the record to be deleted.
+   * @return The number of records deleted.
+   */
+  public int purgeGuid(String guid) {
     String where  = BrowserContract.SyncColumns.GUID + " = ?";
     String[] args = new String[] { guid };
 
     int deleted = context.getContentResolver().delete(getUri(), where, args);
-    if (deleted == 1) {
-      return;
+    if (deleted != 1) {
+      Logger.warn(LOG_TAG, "Unexpectedly deleted " + deleted + " records for guid " + guid);
     }
-    Logger.warn(LOG_TAG, "Unexpectedly deleted " + deleted + " rows for guid " + guid);
+    return deleted;
   }
 
   public void update(String guid, Record newRecord) {
     String where  = BrowserContract.SyncColumns.GUID + " = ?";
     String[] args = new String[] { guid };
     ContentValues cv = getContentValues(newRecord);
     int updated = context.getContentResolver().update(getUri(), cv, where, args);
     if (updated != 1) {
@@ -127,61 +136,65 @@ public abstract class AndroidBrowserRepo
 
   public Uri insert(Record record) {
     ContentValues cv = getContentValues(record);
     return context.getContentResolver().insert(getUri(), cv);
   }
 
   /**
    * Fetch all records.
+   * <p>
    * The caller is responsible for closing the cursor.
    *
-   * @return A cursor. You *must* close this when you're done with it.
+   * @return A cursor. You </b>must</b> close this when you're done with it.
    * @throws NullCursorException
    */
   public Cursor fetchAll() throws NullCursorException {
     return queryHelper.safeQuery(".fetchAll", getAllColumns(), null, null, null);
   }
-  
+
   /**
    * Fetch GUIDs for records modified since the provided timestamp.
+   * <p>
    * The caller is responsible for closing the cursor.
    *
-   * @param timestamp
-   * @return A cursor. You *must* close this when you're done with it.
+   * @param timestamp A timestamp in milliseconds.
+   * @return A cursor. You <b>must</b> close this when you're done with it.
    * @throws NullCursorException
    */
   public Cursor getGUIDsSince(long timestamp) throws NullCursorException {
     return queryHelper.safeQuery(".getGUIDsSince",
                                  GUID_COLUMNS,
                                  dateModifiedWhere(timestamp),
                                  null, null);
   }
 
   /**
    * Fetch records modified since the provided timestamp.
+   * <p>
    * The caller is responsible for closing the cursor.
    *
-   * @param timestamp
-   * @return A cursor. You *must* close this when you're done with it.
+   * @param timestamp A timestamp in milliseconds.
+   * @return A cursor. You <b>must</b> close this when you're done with it.
    * @throws NullCursorException
    */
   public Cursor fetchSince(long timestamp) throws NullCursorException {
     return queryHelper.safeQuery(".fetchSince",
                                  getAllColumns(),
                                  dateModifiedWhere(timestamp),
                                  null, null);
   }
 
   /**
    * Fetch records for the provided GUIDs.
+   * <p>
    * The caller is responsible for closing the cursor.
    *
-   * @param guids
-   * @return A cursor. You *must* close this when you're done with it.
+   * @param guids The GUIDs of the records to fetch.
+   * @return A cursor. You <b>must</b> close this when you're done with it.
    * @throws NullCursorException
    */
   public Cursor fetch(String guids[]) throws NullCursorException {
     String where = computeSQLInClause(guids.length, "guid");
     return queryHelper.safeQuery(".fetch", getAllColumns(), where, guids, null);
   }
 
   protected String computeSQLInClause(int items, String field) {
@@ -193,27 +206,16 @@ public abstract class AndroidBrowserRepo
     }
     if (i < items) {
       builder.append("?");
     }
     builder.append(")");
     return builder.toString();
   }
 
-  public void delete(Record record) {
-    String where  = BrowserContract.SyncColumns.GUID + " = ?";
-    String[] args = new String[] { record.guid };
-
-    int deleted = context.getContentResolver().delete(getUri(), where, args);
-    if (deleted == 1) {
-      return;
-    }
-    Logger.warn(LOG_TAG, "Unexpectedly deleted " + deleted + " rows for guid " + record.guid);
-  }
-
   public void updateByGuid(String guid, ContentValues cv) {
     String where  = BrowserContract.SyncColumns.GUID + " = ?";
     String[] args = new String[] { guid };
 
     int updated = context.getContentResolver().update(getUri(), cv, where, args);
     if (updated == 1) {
       return;
     }
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java
@@ -484,19 +484,19 @@ public abstract class AndroidBrowserRepo
           return;
         }
       }
     };
     storeWorkQueue.execute(command);
   }
 
   protected void storeRecordDeletion(final Record record) {
-    // TODO: we ought to mark the record as deleted rather than deleting it,
+    // TODO: we ought to mark the record as deleted rather than purging it,
     // in order to support syncing to multiple destinations. Bug 722607.
-    dbHelper.delete(record);      // TODO: mm?
+    dbHelper.purgeGuid(record.guid);
     delegate.onRecordStoreSucceeded(record);
   }
 
   protected Record insert(Record record) throws NoGuidForIdException, NullCursorException, ParentNotFoundException {
     Record toStore = prepareRecord(record);
     Uri recordURI = dbHelper.insert(toStore);
     long id = RepoUtils.getAndroidIdFromUri(recordURI);
     Logger.debug(LOG_TAG, "Inserted as " + id);
--- a/mobile/android/base/sync/repositories/android/BrowserContractHelpers.java
+++ b/mobile/android/base/sync/repositories/android/BrowserContractHelpers.java
@@ -1,21 +1,29 @@
 /* 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 org.mozilla.gecko.sync.repositories.android;
 
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.mozilla.gecko.db.BrowserContract;
+
 import android.net.Uri;
 
-// This will exist implicitly when we're merged into Fennec, or explicitly
-// due to our own imported copy of Fennec's BrowserContract.java.in.
-import org.mozilla.gecko.db.BrowserContract;
+public class BrowserContractHelpers extends BrowserContract {
+  protected static Uri withSync(Uri u) {
+    return u.buildUpon()
+            .appendQueryParameter(PARAM_IS_SYNC, "true")
+            .build();
+  }
 
-public class BrowserContractHelpers extends BrowserContract {
   protected static Uri withSyncAndDeleted(Uri u) {
     return u.buildUpon()
             .appendQueryParameter(PARAM_IS_SYNC, "true")
             .appendQueryParameter(PARAM_SHOW_DELETED, "true")
             .build();
   }
 
   public static final Uri IMAGES_CONTENT_URI               = withSyncAndDeleted(Images.CONTENT_URI);
@@ -23,19 +31,19 @@ public class BrowserContractHelpers exte
   public static final Uri BOOKMARKS_PARENTS_CONTENT_URI    = withSyncAndDeleted(Bookmarks.PARENTS_CONTENT_URI);
   public static final Uri BOOKMARKS_POSITIONS_CONTENT_URI  = withSyncAndDeleted(Bookmarks.POSITIONS_CONTENT_URI);
   public static final Uri HISTORY_CONTENT_URI              = withSyncAndDeleted(History.CONTENT_URI);
   public static final Uri SCHEMA_CONTENT_URI               = withSyncAndDeleted(Schema.CONTENT_URI);
 
   public static final Uri PASSWORDS_CONTENT_URI            = null;
   /*
   public static final Uri PASSWORDS_CONTENT_URI            = withSyncAndDeleted(Passwords.CONTENT_URI);
-  public static final Uri FORM_HISTORY_CONTENT_URI         = withSyncAndDeleted(FormHistory.CONTENT_URI);
-  public static final Uri DELETED_FORM_HISTORY_CONTENT_URI = withSyncAndDeleted(DeletedFormHistory.CONTENT_URI);
    */
+  public static final Uri FORM_HISTORY_CONTENT_URI         = withSync(FormHistory.CONTENT_URI);
+  public static final Uri DELETED_FORM_HISTORY_CONTENT_URI = withSync(DeletedFormHistory.CONTENT_URI);
 
   public static final String[] PasswordColumns = new String[] {
     CommonColumns._ID,
     SyncColumns.GUID,
     SyncColumns.DATE_CREATED,
     SyncColumns.DATE_MODIFIED,
     SyncColumns.IS_DELETED,
     Passwords.HOSTNAME,
@@ -72,9 +80,74 @@ public class BrowserContractHelpers exte
     Bookmarks.URL,
     Bookmarks.TYPE,
     Bookmarks.PARENT,
     Bookmarks.POSITION,
     Bookmarks.TAGS,
     Bookmarks.DESCRIPTION,
     Bookmarks.KEYWORD
   };
+
+
+  public static final String[] FormHistoryColumns = new String[] {
+    FormHistory.ID,
+    FormHistory.GUID,
+    FormHistory.FIELD_NAME,
+    FormHistory.VALUE,
+    FormHistory.TIMES_USED,
+    FormHistory.FIRST_USED,
+    FormHistory.LAST_USED
+  };
+
+  public static final String[] DeletedColumns = new String[] {
+    DeletedFormHistory.ID,
+    DeletedFormHistory.GUID,
+    DeletedFormHistory.TIME_DELETED
+  };
+
+  // Mapping from Sync types to Fennec types.
+  public static final String[] BOOKMARK_TYPE_CODE_TO_STRING = {
+    // Observe omissions: "microsummary", "item".
+    "folder", "bookmark", "separator", "livemark", "query"
+  };
+  private static final int MAX_BOOKMARK_TYPE_CODE = BOOKMARK_TYPE_CODE_TO_STRING.length - 1;
+  public static final Map<String, Integer> BOOKMARK_TYPE_STRING_TO_CODE;
+  static {
+    HashMap<String, Integer> t = new HashMap<String, Integer>();
+    t.put("folder",    Bookmarks.TYPE_FOLDER);
+    t.put("bookmark",  Bookmarks.TYPE_BOOKMARK);
+    t.put("separator", Bookmarks.TYPE_SEPARATOR);
+    t.put("livemark",  Bookmarks.TYPE_LIVEMARK);
+    t.put("query",     Bookmarks.TYPE_QUERY);
+    BOOKMARK_TYPE_STRING_TO_CODE = Collections.unmodifiableMap(t);
+  }
+
+  /**
+   * Convert a database bookmark type code into the Sync string equivalent.
+   *
+   * @param code one of the <code>Bookmarks.TYPE_*</code> enumerations.
+   * @return the string equivalent, or null if not found.
+   */
+  public static String typeStringForCode(int code) {
+    if (0 <= code && code <= MAX_BOOKMARK_TYPE_CODE) {
+      return BOOKMARK_TYPE_CODE_TO_STRING[code];
+    }
+    return null;
+  }
+
+  /**
+   * Convert a Sync type string into a Fennec type code.
+   *
+   * @param type a type string, such as "livemark".
+   * @return the type code, or -1 if not found.
+   */
+  public static int typeCodeForString(String type) {
+    Integer found = BOOKMARK_TYPE_STRING_TO_CODE.get(type);
+    if (found == null) {
+      return -1;
+    }
+    return found.intValue();
+  }
+
+  public static boolean isSupportedType(String type) {
+    return BOOKMARK_TYPE_STRING_TO_CODE.containsKey(type);
+  }
 }
--- a/mobile/android/base/sync/repositories/android/RepoUtils.java
+++ b/mobile/android/base/sync/repositories/android/RepoUtils.java
@@ -88,17 +88,17 @@ public class RepoUtils {
     // TODO: don't look up columns by name!
     return cur.getString(cur.getColumnIndex(colId));
   }
 
   public static long getLongFromCursor(Cursor cur, String colId) {
     return cur.getLong(cur.getColumnIndex(colId));
   }
 
-  public static long getIntFromCursor(Cursor cur, String colId) {
+  public static int getIntFromCursor(Cursor cur, String colId) {
     return cur.getInt(cur.getColumnIndex(colId));
   }
 
   public static JSONArray getJSONArrayFromCursor(Cursor cur, String colId) {
     String jsonArrayAsString = getStringFromCursor(cur, colId);
     if (jsonArrayAsString == null) {
       return new JSONArray();
     }
@@ -141,28 +141,28 @@ public class RepoUtils {
       Logger.debug(LOG_TAG, "Returning history record " + rec.guid + " (" + rec.androidID + ")");
       Logger.debug(LOG_TAG, "> Visited:          " + rec.fennecDateVisited);
       Logger.debug(LOG_TAG, "> Visits:           " + rec.fennecVisitCount);
       if (Logger.LOG_PERSONAL_INFORMATION) {
         Logger.pii(LOG_TAG, "> Title:            " + rec.title);
         Logger.pii(LOG_TAG, "> URI:              " + rec.histURI);
       }
     } catch (Exception e) {
-      Logger.debug(LOG_TAG, "Exception logging bookmark record " + rec, e);
+      Logger.debug(LOG_TAG, "Exception logging history record " + rec, e);
     }
     return rec;
   }
 
   public static void logClient(ClientRecord rec) {
     if (Logger.logVerbose(LOG_TAG)) {
       Logger.trace(LOG_TAG, "Returning client record " + rec.guid + " (" + rec.androidID + ")");
-      Logger.trace(LOG_TAG, "Client Name: " + rec.name);
-      Logger.trace(LOG_TAG, "Client Type: " + rec.type);
+      Logger.trace(LOG_TAG, "Client Name:   " + rec.name);
+      Logger.trace(LOG_TAG, "Client Type:   " + rec.type);
       Logger.trace(LOG_TAG, "Last Modified: " + rec.lastModified);
-      Logger.trace(LOG_TAG, "Deleted: " + rec.deleted);
+      Logger.trace(LOG_TAG, "Deleted:       " + rec.deleted);
     }
   }
 
   public static PasswordRecord passwordFromMirrorCursor(Cursor cur) {
     
     String guid = getStringFromCursor(cur, BrowserContract.SyncColumns.GUID);
     String collection = "passwords";
     long lastModified = getLongFromCursor(cur, BrowserContract.SyncColumns.DATE_MODIFIED);
@@ -212,44 +212,53 @@ public class RepoUtils {
     }
     return s.substring(0, width);
   }
 
   private static String spaces(int i) {
     return "                                     ".substring(0, i);
   }
 
+  private static String dashes(int i) {
+    return "-------------------------------------".substring(0, i);
+  }
+
   public static void dumpCursor(Cursor cur) {
+    dumpCursor(cur, 18, "records");
+  }
+
+  public static void dumpCursor(Cursor cur, int columnWidth, String tag) {
     int originalPosition = cur.getPosition();
     try {
       String[] columnNames = cur.getColumnNames();
       int columnCount      = cur.getColumnCount();
 
-      // 12 chars each column.
       for (int i = 0; i < columnCount; ++i) {
-        System.out.print(fixedWidth(12, columnNames[i]) + " | ");
+        System.out.print(fixedWidth(columnWidth, columnNames[i]) + " | ");
       }
-      System.out.println("");
+      System.out.println("(" + cur.getCount() + " " + tag + ")");
       for (int i = 0; i < columnCount; ++i) {
-        System.out.print("------------" + " | ");
+        System.out.print(dashes(columnWidth) + " | ");
       }
       System.out.println("");
       if (!cur.moveToFirst()) {
         System.out.println("EMPTY");
         return;
       }
 
       cur.moveToFirst();
-      while (cur.moveToNext()) {
+      while (!cur.isAfterLast()) {
         for (int i = 0; i < columnCount; ++i) {
-          System.out.print(fixedWidth(12, cur.getString(i)) + " | ");
+          System.out.print(fixedWidth(columnWidth, cur.getString(i)) + " | ");
         }
         System.out.println("");
+        cur.moveToNext();
       }
-      for (int i = 0; i < columnCount; ++i) {
-        System.out.print("---------------");
+      for (int i = 0; i < columnCount-1; ++i) {
+        System.out.print(dashes(columnWidth + 3));
       }
+      System.out.print(dashes(columnWidth + 3 - 1));
       System.out.println("");
     } finally {
       cur.moveToPosition(originalPosition);
     }
   }
 }
--- a/mobile/android/base/sync/repositories/domain/BookmarkRecord.java
+++ b/mobile/android/base/sync/repositories/domain/BookmarkRecord.java
@@ -38,17 +38,16 @@
 
 package org.mozilla.gecko.sync.repositories.domain;
 
 import org.json.simple.JSONArray;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NonArrayJSONException;
 import org.mozilla.gecko.sync.Utils;
-import org.mozilla.gecko.sync.repositories.android.AndroidBrowserBookmarksDataAccessor;
 import org.mozilla.gecko.sync.repositories.android.RepoUtils;
 
 import android.util.Log;
 
 /**
  * Covers the fields used by all bookmark objects.
  * @author rnewman
  *
@@ -136,83 +135,146 @@ public class BookmarkRecord extends Reco
     out.androidPosition = this.androidPosition;
 
     out.children        = this.copyChildren();
     out.tags            = this.copyTags();
 
     return out;
   }
 
+  public boolean isBookmark() {
+    if (type == null) {
+      return false;
+    }
+    return type.equals("bookmark");
+  }
+
+  public boolean isFolder() {
+    if (type == null) {
+      return false;
+    }
+    return type.equals("folder");
+  }
+
+  public boolean isLivemark() {
+    if (type == null) {
+      return false;
+    }
+    return type.equals("livemark");
+  }
+
+  public boolean isSeparator() {
+    if (type == null) {
+      return false;
+    }
+    return type.equals("separator");
+  }
+
+  public boolean isMicrosummary() {
+    if (type == null) {
+      return false;
+    }
+    return type.equals("microsummary");
+  }
+
+  public boolean isQuery() {
+    if (type == null) {
+      return false;
+    }
+    return type.equals("query");
+  }
+
+  /**
+   * Return true if this record should have the Sync fields
+   * of a bookmark, microsummary, or query.
+   */
+  private boolean isBookmarkIsh() {
+    if (type == null) {
+      return false;
+    }
+    return type.equals("bookmark") ||
+           type.equals("microsummary") ||
+           type.equals("query");
+  }
+
   @Override
   protected void initFromPayload(ExtendedJSONObject payload) {
     this.type        = (String) payload.get("type");
     this.title       = (String) payload.get("title");
     this.description = (String) payload.get("description");
     this.parentID    = (String) payload.get("parentid");
     this.parentName  = (String) payload.get("parentName");
 
-    // Bookmark.
+    // bookmark, microsummary, query.
+    if (isBookmarkIsh()) {
+      this.keyword = (String) payload.get("keyword");
+    try {
+      this.tags = payload.getArray("tags");
+    } catch (NonArrayJSONException e) {
+      Logger.warn(LOG_TAG, "Got non-array tags in bookmark record " + this.guid, e);
+      this.tags = new JSONArray();
+    }
+    }
+
+    // bookmark.
     if (isBookmark()) {
       this.bookmarkURI = (String) payload.get("bmkUri");
-      this.keyword     = (String) payload.get("keyword");
-      try {
-        this.tags = payload.getArray("tags");
-      } catch (NonArrayJSONException e) {
-        Log.e(LOG_TAG, "Got non-array tags in bookmark record " + this.guid, e);
-        this.tags = new JSONArray();
-      }
+      return;
     }
 
-    // Folder.
+    // folder.
     if (isFolder()) {
       try {
         this.children = payload.getArray("children");
       } catch (NonArrayJSONException e) {
         Log.e(LOG_TAG, "Got non-array children in bookmark record " + this.guid, e);
         // Let's see if we can recover later by using the parentid pointers.
         this.children = new JSONArray();
       }
+      return;
     }
 
-    // TODO: predecessor ID?
-    // TODO: type-specific attributes:
-    /*
-      public String generatorURI;
-      public String staticTitle;
-      public String folderName;
-      public String queryID;
-      public String siteURI;
-      public String feedURI;
-      public String pos;
-     */
+    if (isLivemark()) {
+      // TODO: siteUri, feedUri.
+      return;
+    }
+    if (isQuery()) {
+      // TODO: queryId (optional), folderName.
+      return;
+    }
+    if (isMicrosummary()) {
+      // TODO: generatorUri, staticTitle.
+      return;
+    }
+    if (isSeparator()) {
+      this.pos = payload.getString("pos");
+      return;
+    }
   }
 
   @Override
   protected void populatePayload(ExtendedJSONObject payload) {
     putPayload(payload, "type", this.type);
     putPayload(payload, "title", this.title);
     putPayload(payload, "description", this.description);
     putPayload(payload, "parentid", this.parentID);
     putPayload(payload, "parentName", this.parentName);
+    putPayload(payload, "keyword", this.keyword);
+
+    if (this.tags != null) {
+      payload.put("tags", this.tags);
+    }
 
     if (isBookmark()) {
       payload.put("bmkUri", bookmarkURI);
-      payload.put("keyword", keyword);
-      payload.put("tags", this.tags);
     } else if (isFolder()) {
       payload.put("children", this.children);
     }
-  }
 
-  public boolean isBookmark() {
-    return AndroidBrowserBookmarksDataAccessor.TYPE_BOOKMARK.equalsIgnoreCase(this.type);
-  }
-
-  public boolean isFolder() {
-    return AndroidBrowserBookmarksDataAccessor.TYPE_FOLDER.equalsIgnoreCase(this.type);
+    // TODO: fields for other types.
   }
 
   private void trace(String s) {
     Logger.trace(LOG_TAG, s);
   }
 
   @Override
   public boolean equalPayloads(Object o) {
@@ -221,16 +283,20 @@ public class BookmarkRecord extends Reco
       return false;
     }
 
     BookmarkRecord other = (BookmarkRecord) o;
     if (!super.equalPayloads(other)) {
       return false;
     }
 
+    if (!RepoUtils.stringsEqual(this.type, other.type)) {
+      return false;
+    }
+
     // Check children.
     if (isFolder() && (this.children != other.children)) {
       trace("BookmarkRecord.equals: this folder: " + this.title + ", " + this.guid);
       trace("BookmarkRecord.equals: other: " + other.title + ", " + other.guid);
       if (this.children  == null &&
           other.children != null) {
         trace("Records differ: one children array is null.");
         return false;
@@ -255,17 +321,16 @@ public class BookmarkRecord extends Reco
       }
     }
 
     trace("Checking strings.");
     return RepoUtils.stringsEqual(this.title, other.title)
         && RepoUtils.stringsEqual(this.bookmarkURI, other.bookmarkURI)
         && RepoUtils.stringsEqual(this.parentID, other.parentID)
         && RepoUtils.stringsEqual(this.parentName, other.parentName)
-        && RepoUtils.stringsEqual(this.type, other.type)
         && RepoUtils.stringsEqual(this.description, other.description)
         && RepoUtils.stringsEqual(this.keyword, other.keyword)
         && jsonArrayStringsEqual(this.tags, other.tags);
   }
 
   // TODO: two records can be congruent if their child lists are different.
   @Override
   public boolean congruentWith(Object o) {
--- a/mobile/android/base/sync/stage/SyncClientsEngineStage.java
+++ b/mobile/android/base/sync/stage/SyncClientsEngineStage.java
@@ -5,17 +5,20 @@
 package org.mozilla.gecko.sync.stage;
 
 import java.io.UnsupportedEncodingException;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+import org.mozilla.gecko.sync.CommandProcessor;
 import org.mozilla.gecko.sync.CryptoRecord;
+import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.sync.GlobalSession;
 import org.mozilla.gecko.sync.HTTPFailureException;
 import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NoCollectionKeysSetException;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.CryptoException;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
@@ -273,18 +276,22 @@ public class SyncClientsEngineStage impl
 
   protected void processCommands(JSONArray commands) {
     if (commands == null ||
         commands.size() == 0) {
       return;
     }
 
     commandsProcessedShouldUpload = true;
+    CommandProcessor processor = CommandProcessor.getProcessor();
 
     // TODO: Bug 715792 - Process commands here.
+    for (int i = 0; i < commands.size(); i++) {
+      processor.processCommand(new ExtendedJSONObject((JSONObject)commands.get(i)));
+    }
   }
 
   protected void checkAndUpload() {
     if (!shouldUpload()) {
       Logger.debug(LOG_TAG, "Not uploading client record.");
       session.advance();
       return;
     }
--- a/mobile/android/base/sync/syncadapter/SyncAdapter.java
+++ b/mobile/android/base/sync/syncadapter/SyncAdapter.java
@@ -2,22 +2,26 @@
  * 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 org.mozilla.gecko.sync.syncadapter;
 
 import java.io.IOException;
 import java.net.URI;
 import java.security.NoSuchAlgorithmException;
+import java.util.List;
 import java.util.concurrent.TimeUnit;
 
 import org.json.simple.parser.ParseException;
 import org.mozilla.gecko.sync.AlreadySyncingException;
+import org.mozilla.gecko.sync.CommandRunner;
+import org.mozilla.gecko.sync.CommandProcessor;
 import org.mozilla.gecko.sync.GlobalConstants;
 import org.mozilla.gecko.sync.GlobalSession;
+import org.mozilla.gecko.sync.Logger;
 import org.mozilla.gecko.sync.NonObjectJSONException;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.SyncConfigurationException;
 import org.mozilla.gecko.sync.SyncException;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.crypto.KeyBundle;
 import org.mozilla.gecko.sync.delegates.ClientsDataDelegate;
 import org.mozilla.gecko.sync.delegates.GlobalSessionCallback;
@@ -57,16 +61,25 @@ public class SyncAdapter extends Abstrac
   private final AccountManager mAccountManager;
   private final Context        mContext;
 
   public SyncAdapter(Context context, boolean autoInitialize) {
     super(context, autoInitialize);
     mContext = context;
     Log.d(LOG_TAG, "AccountManager.get(" + mContext + ")");
     mAccountManager = AccountManager.get(context);
+
+    // Register the displayURI command here so our SyncService
+    // can receive notifications to open a URI.
+    CommandProcessor.getProcessor().registerCommand("displayURI", new CommandRunner() {
+      @Override
+      public void executeCommand(List<String> args) {
+        displayURI(args.get(0), args.get(1));
+      }
+    });
   }
 
   private SharedPreferences getGlobalPrefs() {
     return mContext.getSharedPreferences("sync.prefs.global", SHARED_PREFERENCES_MODE);
   }
 
   /**
    * Backoff.
@@ -475,9 +488,14 @@ public class SyncAdapter extends Abstrac
   public void informNodeAssigned(GlobalSession session, URI oldClusterURL, URI newClusterURL) {
     setClusterURLIsStale(false);
   }
 
   @Override
   public void informUnauthorizedResponse(GlobalSession session, URI oldClusterURL) {
     setClusterURLIsStale(true);
   }
+
+  public void displayURI(String uri, String clientId) {
+    Logger.info(LOG_TAG, "Received a URI for display: " + uri + " from " + clientId);
+    // TODO: Bug 732147 - Send tab to device: receiving pushed tabs
+  }
 }
--- a/mobile/android/base/tests/testAboutPage.java.in
+++ b/mobile/android/base/tests/testAboutPage.java.in
@@ -27,21 +27,25 @@ public class testAboutPage extends BaseT
         // Look for the 'More' menu if this device/OS uses it
         if (mSolo.waitForText("^More$")) {
             mSolo.clickOnText("^More$");
         }
 
         mSolo.waitForText("^Settings$");
         mSolo.clickOnText("^Settings$");
 
+        // Set up listeners to catch the page load we're about to do
+        Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
+        Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
+
         // Tap on the "About Xxxx" setting
         mSolo.waitForText("About (Fennec|Nightly|Aurora|Firefox)");
         mSolo.clickOnText("About (Fennec|Nightly|Aurora|Firefox)");
 
         // Wait for the new tab and page to load
-        mActions.expectGeckoEvent("Tab:Added").blockForEvent();
-        mActions.expectGeckoEvent("DOMContentLoaded").blockForEvent();
+        tabEventExpecter.blockForEvent();
+        contentEventExpecter.blockForEvent();
 
         // Grab the title to make sure the about: page was loaded
         awesomebar = mDriver.findElement(getActivity(), "awesome_bar");
         mAsserter.ok(awesomebar.getText().matches("About (Fennec|Nightly|Aurora|Firefox)"), "page title match", "about: page title is correct");
     }
 }
--- a/mobile/android/base/tests/testBookmark.java.in
+++ b/mobile/android/base/tests/testBookmark.java.in
@@ -28,20 +28,20 @@ public class testBookmark extends BaseTe
 
         mAsserter.ok(bookmarksList != null, "checking that bookmarks list exists", "bookmarks list exists");
 
         // No folders should be visible if no desktop bookmarks exist
         mAsserter.is(bookmarksList.getChildCount(), 2,
             "bookmarks list has 2 children (the bookmark we added and the hidden header)");
 
         // Click on the bookmark we created (the first item is the header view)
+        // and wait for the bookmarked page to load
+        Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
         mSolo.clickInList(2);
-
-        // Wait for the bookmarked page to load
-        mActions.expectGeckoEvent("DOMContentLoaded").blockForEvent();
+        contentEventExpecter.blockForEvent();
 
         // Clean up the bookmark we created
         cleanUpBookmark();
     }
 
     private ListView openBookmarksList() {
         Activity awesomeBarActivity = clickOnAwesomeBar();
 
--- a/mobile/android/base/tests/testBrowserProvider.java.in
+++ b/mobile/android/base/tests/testBrowserProvider.java.in
@@ -26,16 +26,17 @@ public class testBrowserProvider extends
     private String MENU_FOLDER_GUID;
     private String TAGS_FOLDER_GUID;
     private String TOOLBAR_FOLDER_GUID;
     private String UNFILED_FOLDER_GUID;
 
     private Uri mBookmarksUri;
     private Uri mHistoryUri;
     private Uri mImagesUri;
+    private Uri mCombinedUri;
 
     private String mBookmarksIdCol;
     private String mBookmarksTitleCol;
     private String mBookmarksUrlCol;
     private String mBookmarksFaviconCol;
     private String mBookmarksThumbnailCol;
     private String mBookmarksParentCol;
     private String mBookmarksTypeCol;
@@ -62,20 +63,31 @@ public class testBrowserProvider extends
     private String mHistoryIsDeletedCol;
     private String mHistoryDateCreatedCol;
     private String mHistoryDateModifiedCol;
 
     private String mImagesThumbnailCol;
     private String mImagesFaviconCol;
     private String mImagesUrlCol;
 
+    private String mCombinedIdCol;
+    private String mCombinedBookmarkIdCol;
+    private String mCombinedHistoryIdCol;
+    private String mCombinedUrlCol;
+    private String mCombinedTitleCol;
+    private String mCombinedVisitsCol;
+    private String mCombinedLastVisitedCol;
+    private String mCombinedFaviconCol;
+    private String mCombinedThumbnailCol;
+
     private void loadContractInfo() throws Exception {
         mBookmarksUri = getContentUri("Bookmarks");
         mHistoryUri = getContentUri("History");
         mImagesUri = getContentUri("Images");
+        mCombinedUri = getContentUri("Combined");
 
         PLACES_FOLDER_GUID = getStringColumn("Bookmarks", "PLACES_FOLDER_GUID");
         MOBILE_FOLDER_GUID = getStringColumn("Bookmarks", "MOBILE_FOLDER_GUID");
         MENU_FOLDER_GUID = getStringColumn("Bookmarks", "MENU_FOLDER_GUID");
         TAGS_FOLDER_GUID = getStringColumn("Bookmarks", "TAGS_FOLDER_GUID");
         TOOLBAR_FOLDER_GUID = getStringColumn("Bookmarks", "TOOLBAR_FOLDER_GUID");
         UNFILED_FOLDER_GUID = getStringColumn("Bookmarks", "UNFILED_FOLDER_GUID");
 
@@ -108,16 +120,26 @@ public class testBrowserProvider extends
         mHistoryGuidCol = getStringColumn("History", "GUID");
         mHistoryIsDeletedCol = getStringColumn("History", "IS_DELETED");
         mHistoryDateCreatedCol = getStringColumn("History", "DATE_CREATED");
         mHistoryDateModifiedCol = getStringColumn("History", "DATE_MODIFIED");
 
         mImagesUrlCol = getStringColumn("Images", "URL");
         mImagesFaviconCol = getStringColumn("Images", "FAVICON");
         mImagesThumbnailCol = getStringColumn("Images", "THUMBNAIL");
+
+        mCombinedIdCol = getStringColumn("Combined", "_ID");
+        mCombinedBookmarkIdCol = getStringColumn("Combined", "BOOKMARK_ID");
+        mCombinedHistoryIdCol = getStringColumn("Combined", "HISTORY_ID");
+        mCombinedUrlCol = getStringColumn("Combined", "URL");
+        mCombinedTitleCol = getStringColumn("Combined", "TITLE");
+        mCombinedVisitsCol = getStringColumn("Combined", "VISITS");
+        mCombinedLastVisitedCol = getStringColumn("Combined", "DATE_LAST_VISITED");
+        mCombinedFaviconCol = getStringColumn("Combined", "FAVICON");
+        mCombinedThumbnailCol = getStringColumn("Combined", "THUMBNAIL");
     }
 
     private void loadMobileFolderId() throws Exception {
         Cursor c = getBookmarkByGuid(MOBILE_FOLDER_GUID);
         mAsserter.is(c.moveToFirst(), true, "Mobile bookmarks folder is present");
 
         mMobileFolderId = c.getLong(c.getColumnIndex(mBookmarksIdCol));
     }
@@ -255,16 +277,18 @@ public class testBrowserProvider extends
         mTests.add(new TestUpdateBookmarksImages());
 
         mTests.add(new TestInsertHistory());
         mTests.add(new TestInsertHistoryImages());
         mTests.add(new TestDeleteHistory());
         mTests.add(new TestDeleteHistoryImages());
         mTests.add(new TestUpdateHistory());
         mTests.add(new TestUpdateHistoryImages());
+
+        mTests.add(new TestCombinedView());
     }
 
     public void testBrowserProvider() throws Exception {
         setTestType("mochitest");
 
         loadMobileFolderId();
 
         for (int i = 0; i < mTests.size(); i++) {
@@ -871,9 +895,130 @@ public class testBrowserProvider extends
 
             c = getImagesByUrl(h.getAsString(mHistoryUrlCol));
             mAsserter.is(c.moveToFirst(), true, "Updated images found");
 
             mAsserter.is(new String(c.getBlob(c.getColumnIndex(mImagesFaviconCol)), "UTF8"),
                          newFavicon, "Updated image has corresponding favicon image");
         }
     }
+
+    class TestCombinedView extends Test {
+        public void test() throws Exception {
+            final String TITLE_1 = "Test Page 1";
+            final String TITLE_2 = "Test Page 2";
+            final String TITLE_3_HISTORY = "Test Page 3 (History Entry)";
+            final String TITLE_3_BOOKMARK = "Test Page 3 (Bookmark Entry)";
+
+            final String URL_1 = "http://example.com";
+            final String URL_2 = "http://example.org";
+            final String URL_3 = "http://examples2.com";
+
+            final String HISTORY_FAVICON = "HISTORY_FAVICON";
+            final String HISTORY_THUMBNAIL = "HISTORY_THUMBNAIL";
+            final String BOOKMARK_FAVICON = "BOOKMARK_FAVICON";
+            final String BOOKMARK_THUMBNAIL = "BOOKMARK_THUMBNAIL";
+
+            final int VISITS = 10;
+            final long LAST_VISITED = System.currentTimeMillis();
+
+            // Create a basic history entry with images
+            ContentValues basicHistory = createHistoryEntry(TITLE_1, URL_1, VISITS, LAST_VISITED);
+            basicHistory.put(mHistoryFaviconCol, HISTORY_FAVICON.getBytes("UTF8"));
+            basicHistory.put(mHistoryThumbnailCol, HISTORY_THUMBNAIL.getBytes("UTF8"));
+            long basicHistoryId = ContentUris.parseId(mProvider.insert(mHistoryUri, basicHistory));
+
+            // Create a basic bookmark entry with images
+            ContentValues basicBookmark = createBookmark(TITLE_2, URL_2, mMobileFolderId,
+                mBookmarksTypeBookmark, 0, "tags", "description", "keyword");
+            basicBookmark.put(mBookmarksFaviconCol, BOOKMARK_FAVICON.getBytes("UTF8"));
+            basicBookmark.put(mBookmarksThumbnailCol, BOOKMARK_THUMBNAIL.getBytes("UTF8"));
+            long basicBookmarkId = ContentUris.parseId(mProvider.insert(mBookmarksUri, basicBookmark));
+
+            // Create a history entry and bookmark entry with the same URL to
+            // represent a visited bookmark
+            ContentValues combinedHistory = createHistoryEntry(TITLE_3_HISTORY, URL_3, VISITS, LAST_VISITED);
+            combinedHistory.put(mHistoryFaviconCol, HISTORY_FAVICON.getBytes("UTF8"));
+            combinedHistory.put(mHistoryThumbnailCol, HISTORY_THUMBNAIL.getBytes("UTF8"));
+            long combinedHistoryId = ContentUris.parseId(mProvider.insert(mHistoryUri, combinedHistory));
+
+            ContentValues combinedBookmark = createBookmark(TITLE_3_BOOKMARK, URL_3, mMobileFolderId,
+                mBookmarksTypeBookmark, 0, "tags", "description", "keyword");
+            combinedBookmark.put(mBookmarksFaviconCol, BOOKMARK_FAVICON.getBytes("UTF8"));
+            combinedBookmark.put(mBookmarksThumbnailCol, BOOKMARK_THUMBNAIL.getBytes("UTF8"));
+            long combinedBookmarkId = ContentUris.parseId(mProvider.insert(mBookmarksUri, combinedBookmark));
+
+            // Create a bookmark folder to make sure it _doesn't_ show up in the results
+            ContentValues folderBookmark = createBookmark("", "", mMobileFolderId,
+                mBookmarksTypeFolder, 0, "tags", "description", "keyword");
+            mProvider.insert(mBookmarksUri, folderBookmark);
+
+            // Sort entries by url so we can check them individually
+            Cursor c = mProvider.query(mCombinedUri, null, "", null, mCombinedUrlCol);
+
+            mAsserter.is(c.getCount(), 3, "3 combined entries found");
+
+            // First combined entry is basic history entry
+            mAsserter.is(c.moveToFirst(), true, "Found basic history entry");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedIdCol))), new Long(0),
+                         "Combined _id column should always be 0");
+            // TODO: Should we change BrowserProvider to make this return -1, not 0?
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol))), new Long(0),
+                         "Bookmark id should be 0 for basic history entry");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedHistoryIdCol))), new Long(basicHistoryId),
+                         "Basic history entry has correct history id");
+            mAsserter.is(c.getString(c.getColumnIndex(mCombinedTitleCol)), TITLE_1,
+                         "Basic history entry has correct title");
+            mAsserter.is(c.getString(c.getColumnIndex(mCombinedUrlCol)), URL_1,
+                         "Basic history entry has correct url");
+            mAsserter.is(c.getInt(c.getColumnIndex(mCombinedVisitsCol)), VISITS,
+                         "Basic history entry has correct number of visits");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedLastVisitedCol))), new Long(LAST_VISITED),
+                         "Basic history entry has correct last visit time");
+            mAsserter.is(new String(c.getBlob(c.getColumnIndex(mCombinedFaviconCol)), "UTF8"),
+                          HISTORY_FAVICON, "Basic history entry has corresponding favicon image");
+            mAsserter.is(new String(c.getBlob(c.getColumnIndex(mCombinedThumbnailCol)), "UTF8"),
+                          HISTORY_THUMBNAIL, "Basic history entry has corresponding thumbnail image");
+
+            // Second combined entry is basic bookmark entry
+            mAsserter.is(c.moveToNext(), true, "Found basic bookmark entry");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedIdCol))), new Long(0),
+                         "Combined _id column should always be 0");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol))), new Long(basicBookmarkId),
+                         "Basic bookmark entry has correct bookmark id");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedHistoryIdCol))), new Long(-1),
+                         "History id should be -1 for basic bookmark entry");
+            mAsserter.is(c.getString(c.getColumnIndex(mCombinedTitleCol)), TITLE_2,
+                         "Basic bookmark entry has correct title");
+            mAsserter.is(c.getString(c.getColumnIndex(mCombinedUrlCol)), URL_2,
+                         "Basic bookmark entry has correct url");
+            mAsserter.is(c.getInt(c.getColumnIndex(mCombinedVisitsCol)), -1,
+                         "Visits should be -1 for basic bookmark entry");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedLastVisitedCol))), new Long(-1),
+                         "Last visited should be -1 for basic bookmark entry");
+            mAsserter.is(new String(c.getBlob(c.getColumnIndex(mCombinedFaviconCol)), "UTF8"),
+                          BOOKMARK_FAVICON, "Basic bookmark entry has corresponding favicon image");
+            mAsserter.is(new String(c.getBlob(c.getColumnIndex(mCombinedThumbnailCol)), "UTF8"),
+                          BOOKMARK_THUMBNAIL, "Basic bookmark entry has corresponding thumbnail image");
+
+            // Third combined entry is a combined history/bookmark entry
+            mAsserter.is(c.moveToNext(), true, "Found third combined entry");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedIdCol))), new Long(0),
+                         "Combined _id column should always be 0");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedBookmarkIdCol))), new Long(combinedBookmarkId),
+                         "Combined entry has correct bookmark id");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedHistoryIdCol))), new Long(combinedHistoryId),
+                         "Combined entry has correct history id");
+            mAsserter.is(c.getString(c.getColumnIndex(mCombinedTitleCol)), TITLE_3_BOOKMARK,
+                         "Combined entry has title corresponding to bookmark entry");
+            mAsserter.is(c.getString(c.getColumnIndex(mCombinedUrlCol)), URL_3,
+                         "Combined entry has correct url");
+            mAsserter.is(c.getInt(c.getColumnIndex(mCombinedVisitsCol)), VISITS,
+                         "Combined entry has correct number of visits");
+            mAsserter.is(new Long(c.getLong(c.getColumnIndex(mCombinedLastVisitedCol))), new Long(LAST_VISITED),
+                         "Combined entry has correct last visit time");
+            mAsserter.is(new String(c.getBlob(c.getColumnIndex(mCombinedFaviconCol)), "UTF8"),
+                          BOOKMARK_FAVICON, "Combined entry has bookmark favicon image");
+            mAsserter.is(new String(c.getBlob(c.getColumnIndex(mCombinedThumbnailCol)), "UTF8"),
+                          BOOKMARK_THUMBNAIL, "Combined entry has bookmark thumbnail image");
+        }
+    }
 }
--- a/mobile/android/base/tests/testBrowserProviderPerf.java.in
+++ b/mobile/android/base/tests/testBrowserProviderPerf.java.in
@@ -14,117 +14,232 @@ import java.lang.reflect.Method;
 import java.util.UUID;
 import java.util.Random;
 
 /*
  * This test is meant to exercise the performance of Fennec's
  * history and bookmarks content provider.
  */
 public class testBrowserProviderPerf extends ContentProviderTest {
-    private final int NUMBER_OF_URLS = 10000;
+    private final int NUMBER_OF_BASIC_HISTORY_URLS = 10000;
+    private final int NUMBER_OF_BASIC_BOOKMARK_URLS = 500;
+    private final int NUMBER_OF_COMBINED_URLS = 500;
+
     private final int NUMBER_OF_KNOWN_URLS = 200;
     private final int BATCH_SIZE = 500;
 
     private final String KNOWN_PREFIX = "mymozillatest";
 
     private Method mFilterMethod;
     private Random mGenerator;
 
+    private final String MOBILE_FOLDER_GUID = "mobile";
+    private long mMobileFolderId;
+
+    private Uri mBookmarksUri;
     private Uri mHistoryUri;
+
+    private String mBookmarksIdCol;
+    private String mBookmarksTitleCol;
+    private String mBookmarksUrlCol;
+    private String mBookmarksParentCol;
+    private String mBookmarksTypeCol;
+    private String mBookmarksPositionCol;
+    private String mBookmarksTagsCol;
+    private String mBookmarksDescriptionCol;
+    private String mBookmarksKeywordCol;
+    private String mBookmarksGuidCol;
+    private int mBookmarksTypeBookmark;
+
     private String mHistoryTitleCol;
     private String mHistoryUrlCol;
     private String mHistoryVisitsCol;
     private String mHistoryFaviconCol;
     private String mHistoryThumbnailCol;
     private String mHistoryLastVisitedCol;
 
     private void loadFilterMethod() throws Exception {
         Class browserDBClass = mClassLoader.loadClass("org.mozilla.gecko.db.BrowserDB");
 
         mFilterMethod =
             browserDBClass.getDeclaredMethod("filter", ContentResolver.class,
                                              CharSequence.class, int.class);
     }
 
     private void loadContractInfo() throws Exception {
+        mBookmarksUri = getContentUri("Bookmarks");
         mHistoryUri = getContentUri("History");
 
+        mBookmarksIdCol = getStringColumn("Bookmarks", "_ID");
+        mBookmarksTitleCol = getStringColumn("Bookmarks", "TITLE");
+        mBookmarksUrlCol = getStringColumn("Bookmarks", "URL");
+        mBookmarksParentCol = getStringColumn("Bookmarks", "PARENT");
+        mBookmarksTypeCol = getStringColumn("Bookmarks", "TYPE");
+        mBookmarksPositionCol = getStringColumn("Bookmarks", "POSITION");
+        mBookmarksTagsCol = getStringColumn("Bookmarks", "TAGS");
+        mBookmarksDescriptionCol = getStringColumn("Bookmarks", "DESCRIPTION");
+        mBookmarksKeywordCol= getStringColumn("Bookmarks", "KEYWORD");
+        mBookmarksGuidCol= getStringColumn("Bookmarks", "GUID");
+        mBookmarksTypeBookmark = getIntColumn("Bookmarks", "TYPE_BOOKMARK");
+
         mHistoryTitleCol = getStringColumn("History", "TITLE");
         mHistoryUrlCol = getStringColumn("History", "URL");
         mHistoryVisitsCol = getStringColumn("History", "VISITS");
         mHistoryLastVisitedCol = getStringColumn("History", "DATE_LAST_VISITED");
         mHistoryFaviconCol = getStringColumn("History", "FAVICON");
     }
 
+    private void loadMobileFolderId() throws Exception {
+        Cursor c = mProvider.query(mBookmarksUri, null,
+                                   mBookmarksGuidCol + " = ?",
+                                   new String[] { MOBILE_FOLDER_GUID },
+                                   null);
+        c.moveToFirst();
+        mMobileFolderId = c.getLong(c.getColumnIndex(mBookmarksIdCol));
+
+        c.close();
+    }
+
+    private ContentValues createBookmarkEntry(String title, String url, long parentId,
+            int type, int position, String tags, String description, String keyword) throws Exception {
+        ContentValues bookmark = new ContentValues();
+
+        bookmark.put(mBookmarksTitleCol, title);
+        bookmark.put(mBookmarksUrlCol, url);
+        bookmark.put(mBookmarksParentCol, parentId);
+        bookmark.put(mBookmarksTypeCol, type);
+        bookmark.put(mBookmarksPositionCol, position);
+        bookmark.put(mBookmarksTagsCol, tags);
+        bookmark.put(mBookmarksDescriptionCol, description);
+        bookmark.put(mBookmarksKeywordCol, keyword);
+
+        return bookmark;
+    }
+
+    private ContentValues createBookmarkEntryWithUrl(String url) throws Exception {
+        return createBookmarkEntry(url, url, mMobileFolderId,
+            mBookmarksTypeBookmark, 0, "tags", "description", "keyword");
+    }
+
+    private ContentValues createRandomBookmarkEntry() throws Exception {
+        return createRandomBookmarkEntry("");
+    }
+
+    private ContentValues createRandomBookmarkEntry(String knownPrefix) throws Exception {
+        String randomStr = createRandomUrl(knownPrefix);
+        return createBookmarkEntryWithUrl(randomStr);
+    }
+
     private ContentValues createHistoryEntry(String title, String url, int visits,
             long lastVisited, byte[] favicon) throws Exception {
         ContentValues historyEntry = new ContentValues();
 
         historyEntry.put(mHistoryTitleCol, title);
         historyEntry.put(mHistoryUrlCol, url);
         historyEntry.put(mHistoryVisitsCol, visits);
         historyEntry.put(mHistoryLastVisitedCol, lastVisited);
         historyEntry.put(mHistoryFaviconCol, favicon);
 
         return historyEntry;
     }
 
+    private ContentValues createHistoryEntryWithUrl(String url) throws Exception {
+        int visits = mGenerator.nextInt(500);
+        return createHistoryEntry(url, url, visits,
+            System.currentTimeMillis(), url.getBytes("UTF8"));
+    }
+
     private ContentValues createRandomHistoryEntry() throws Exception {
         return createRandomHistoryEntry("");
     }
 
     private ContentValues createRandomHistoryEntry(String knownPrefix) throws Exception {
-        String randomStr = knownPrefix + UUID.randomUUID().toString();
-        int visits = mGenerator.nextInt(500);
+        String randomStr = createRandomUrl(knownPrefix);
+        return createHistoryEntryWithUrl(randomStr);
+    }
 
-        return createHistoryEntry(randomStr, randomStr, visits,
-            System.currentTimeMillis(), randomStr.getBytes("UTF8"));
+    private String createRandomUrl(String knownPrefix) throws Exception {
+        return knownPrefix + UUID.randomUUID().toString();
     }
 
     private void addTonsOfUrls() throws Exception {
-        ContentValues[] entries = new ContentValues[BATCH_SIZE];
+        // Create some random bookmark entries
+        ContentValues[] bookmarkEntries = new ContentValues[BATCH_SIZE];
 
-        for (int i = 0; i < NUMBER_OF_URLS / BATCH_SIZE; i++) {
-            entries = new ContentValues[BATCH_SIZE];
+        for (int i = 0; i < NUMBER_OF_BASIC_BOOKMARK_URLS / BATCH_SIZE; i++) {
+            bookmarkEntries = new ContentValues[BATCH_SIZE];
 
             for (int j = 0; j < BATCH_SIZE; j++) {
-                entries[j] = createRandomHistoryEntry();
+                bookmarkEntries[j] = createRandomBookmarkEntry();
+            }
+
+            mProvider.bulkInsert(mBookmarksUri, bookmarkEntries);
+        }
+
+        // Create some random history entries
+        ContentValues[] historyEntries = new ContentValues[BATCH_SIZE];
+
+        for (int i = 0; i < NUMBER_OF_BASIC_HISTORY_URLS / BATCH_SIZE; i++) {
+            historyEntries = new ContentValues[BATCH_SIZE];
+
+            for (int j = 0; j < BATCH_SIZE; j++) {
+                historyEntries[j] = createRandomHistoryEntry();
             }
 
-            mProvider.bulkInsert(mHistoryUri, entries);
+            mProvider.bulkInsert(mHistoryUri, historyEntries);
         }
 
-        entries = new ContentValues[NUMBER_OF_KNOWN_URLS];
-        for (int i = 0; i < NUMBER_OF_KNOWN_URLS; i++) {
-            entries[i] = createRandomHistoryEntry(KNOWN_PREFIX);
+
+        // Create random bookmark/history entries with the same url
+        for (int i = 0; i < NUMBER_OF_COMBINED_URLS / BATCH_SIZE; i++) {
+            bookmarkEntries = new ContentValues[BATCH_SIZE];
+            historyEntries = new ContentValues[BATCH_SIZE];
+
+            for (int j = 0; j < BATCH_SIZE; j++) {
+                String url = createRandomUrl("");
+                bookmarkEntries[j] = createBookmarkEntryWithUrl(url);
+                historyEntries[j] = createHistoryEntryWithUrl(url);
+            }
+
+            mProvider.bulkInsert(mBookmarksUri, bookmarkEntries);
+            mProvider.bulkInsert(mHistoryUri, historyEntries);
         }
 
-        mProvider.bulkInsert(mHistoryUri, entries);
+        // Create some history entries with a known prefix
+        historyEntries = new ContentValues[NUMBER_OF_KNOWN_URLS];
+        for (int i = 0; i < NUMBER_OF_KNOWN_URLS; i++) {
+            historyEntries[i] = createRandomHistoryEntry(KNOWN_PREFIX);
+        }
+
+        mProvider.bulkInsert(mHistoryUri, historyEntries);
     }
 
     public void setUp() throws Exception {
         super.setUp("@ANDROID_PACKAGE_NAME@.db.BrowserProvider", "AUTHORITY");
 
         mGenerator = new Random(19580427);
 
         loadContractInfo();
         loadFilterMethod();
     }
 
     public void testBrowserProviderPerf() throws Exception {
         setTestType("talos");
 
+        loadMobileFolderId();
         addTonsOfUrls();
 
         long start = SystemClock.uptimeMillis();
 
         Cursor c = (Cursor) mFilterMethod.invoke(null, mResolver, KNOWN_PREFIX, 100);
         c.getCount(); // ensure query is not lazy loaded
 
         long end = SystemClock.uptimeMillis();
 
         mAsserter.dumpLog("__start_report" + Long.toString(end - start) + "__end_report");
+
+        c.close();
     }
 
     public void tearDown() throws Exception {
         super.tearDown();
     }
 }
--- a/mobile/android/base/tests/testCheck.java.in
+++ b/mobile/android/base/tests/testCheck.java.in
@@ -9,44 +9,44 @@ public class testCheck extends PixelTest
             Thread.sleep(length);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }
 
     public void testCheck() {
         setTestType("talos");
-        String url = getAbsoluteUrl("/startup_test/fennecmark/wikipedia.html");
+        String url = getAbsoluteUrl("/startup_test/fennecmark/timecube.html");
 
         mActions.expectGeckoEvent("Gecko:Ready").blockForEvent();
 
         loadAndPaint(url);
 
         mDriver.setupScrollHandling();
 
         // Setup scrolling coordinates.
-        int midX = mDriver.getGeckoLeft() + mDriver.getGeckoWidth()/2;
-        int midY = mDriver.getGeckoTop() + mDriver.getGeckoHeight()/2;
-        int endY = mDriver.getGeckoTop() + mDriver.getGeckoHeight()/6;
+        MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop());
+        int midX = mDriver.getGeckoWidth() / 2;
+        int height = mDriver.getGeckoHeight();
+        int topY = height / 8;
 
         mDriver.startCheckerboardRecording();
 
-        int i = 0;
-        // Scroll repeatedly downwards, then upwards. This test should take
-        // approximately 15 seconds.
-        do {
+        // Scroll repeatedly downwards, then upwards. On each iteration of i,
+        // increase the scroll distance to test different scroll amounts.
+        for (int i = 2; i < 7; i++) {
+            int botY = (height * i / 8);
             for (int j = 0; j < 3; j++) {
-                mActions.drag(midX, midX, midY, endY);
-                pause(500);
+                meh.dragSync(midX, botY, midX, topY, 200);
+                pause(1000);
             }
             for (int j = 0; j < 3; j++) {
-                mActions.drag(midX, midX, endY, midY);
-                pause(500);
+                meh.dragSync(midX, topY, midX, botY, 200);
+                pause(1000);
             }
-            i++;
-        } while (i < 5);
+        }
 
         float completeness = mDriver.stopCheckerboardRecording();
         mAsserter.dumpLog("__start_report" + completeness + "__end_report");
         long msecs = System.currentTimeMillis();
         mAsserter.dumpLog("__startTimestamp" + msecs + "__endTimestamp");
     }
 }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -188,16 +188,17 @@ var BrowserApp = {
     Services.obs.addObserver(this, "Preferences:Set", false);
     Services.obs.addObserver(this, "ScrollTo:FocusedInput", false);
     Services.obs.addObserver(this, "Sanitize:ClearAll", false);
     Services.obs.addObserver(this, "PanZoom:PanZoom", false);
     Services.obs.addObserver(this, "FullScreen:Exit", false);
     Services.obs.addObserver(this, "Viewport:Change", false);
     Services.obs.addObserver(this, "Passwords:Init", false);
     Services.obs.addObserver(this, "FormHistory:Init", false);
+    Services.obs.addObserver(this, "ToggleProfiling", false);
 
     Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
 
     function showFullScreenWarning() {
       NativeWindow.toast.show(Strings.browser.GetStringFromName("alertFullScreenToast"), "short");
     }
 
     window.addEventListener("fullscreen", function() {
@@ -947,16 +948,24 @@ var BrowserApp = {
     } else if (aTopic == "FormHistory:Init") {
       let fh = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
       // Force creation/upgrade of formhistory.sqlite
       let db = fh.DBConnection;
       sendMessageToJava({gecko: { type: "FormHistory:Init:Return" }});
       Services.obs.removeObserver(this, "FormHistory:Init", false);
     } else if (aTopic == "sessionstore-state-purge-complete") {
       sendMessageToJava({ gecko: { type: "Session:StatePurged" }});
+    } else if (aTopic == "ToggleProfiling") {
+      let profiler = Cc["@mozilla.org/tools/profiler;1"].
+                       getService(Ci.nsIProfiler);
+      if (profiler.IsActive()) {
+        profiler.StopProfiler();
+      } else {
+        profiler.StartProfiler(100000, 25, ["stackwalk"], 1);
+      }
     }
   },
 
   get defaultBrowserWidth() {
     delete this.defaultBrowserWidth;
     let width = Services.prefs.getIntPref("browser.viewport.desktopWidth");
     return this.defaultBrowserWidth = width;
   },
@@ -1496,18 +1505,17 @@ Tab.prototype = {
         selected: ("selected" in aParams) ? aParams.selected : true,
         title: aParams.title || aURL,
         delayLoad: aParams.delayLoad || false
       }
     };
     sendMessageToJava(message);
 
     this.overscrollController = new OverscrollController(this);
-    this.browser.contentWindow.controllers
-      .insertControllerAt(0, this.overscrollController);
+    this.browser.contentWindow.controllers.insertControllerAt(0, this.overscrollController);
 
     let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
                 Ci.nsIWebProgress.NOTIFY_LOCATION |
                 Ci.nsIWebProgress.NOTIFY_SECURITY;
     this.browser.addProgressListener(this, flags);
     this.browser.sessionHistory.addSHistoryListener(this);
 
     this.browser.addEventListener("DOMContentLoaded", this, true);
@@ -1548,18 +1556,17 @@ Tab.prototype = {
       }
     }
   },
 
   destroy: function() {
     if (!this.browser)
       return;
 
-    this.browser.controllers.contentWindow
-      .removeController(this.overscrollController);
+    this.browser.contentWindow.controllers.removeController(this.overscrollController);
 
     this.browser.removeProgressListener(this);
     this.browser.removeEventListener("DOMContentLoaded", this, true);
     this.browser.removeEventListener("DOMLinkAdded", this, true);
     this.browser.removeEventListener("DOMTitleChanged", this, true);
     this.browser.removeEventListener("DOMWindowClose", this, true);
     this.browser.removeEventListener("DOMWillOpenModalDialog", this, true);
     this.browser.removeEventListener("scroll", this, true);
@@ -2200,28 +2207,42 @@ Tab.prototype = {
   },
 
   observe: function(aSubject, aTopic, aData) {
     switch (aTopic) {
       case "before-first-paint":
         // Is it on the top level?
         let contentDocument = aSubject;
         if (contentDocument == this.browser.contentDocument) {
-          // reset CSS viewport and zoom to default on new page
+          // reset CSS viewport and zoom to default on new page, and then calculate
+          // them properly using the actual metadata from the page. note that the
+          // updateMetadata call takes into account the existing CSS viewport size
+          // and zoom when calculating the new ones, so we need to reset these
+          // things here before calling updateMetadata.
           this.setBrowserSize(kDefaultCSSViewportWidth, kDefaultCSSViewportHeight);
           this.setResolution(gScreenWidth / this.browserWidth, false);
-          // and then use the metadata to figure out how it needs to be updated
           ViewportHandler.updateMetadata(this);
 
-          // If we draw without a display-port, things can go wrong. While it's
-          // almost certain a display-port has been set via the
-          // MozScrolledAreaChanged event, make sure by sending a viewport
-          // update here. As it's the first paint, this will end up being a
-          // display-port request only.
-          this.sendViewportUpdate();
+          // Note that if we draw without a display-port, things can go wrong. By the
+          // time we execute this, it's almost certain a display-port has been set via
+          // the MozScrolledAreaChanged event. If that didn't happen, the updateMetadata
+          // call above does so at the end of the updateViewportSize function. As long
+          // as that is happening, we don't need to do it again here.
+
+          if (contentDocument instanceof ImageDocument) {
+            // for images, scale to fit width. this needs to happen *after* the call
+            // to updateMetadata above, because that call sets the CSS viewport which
+            // will affect the page size (i.e. contentDocument.body.scroll*) that we
+            // use in this calculation. also we call sendViewportUpdate after changing
+            // the resolution so that the display port gets recalculated appropriately.
+            let fitZoom = Math.min(gScreenWidth / contentDocument.body.scrollWidth,
+                                   gScreenHeight / contentDocument.body.scrollHeight);
+            this.setResolution(fitZoom, false);
+            this.sendViewportUpdate();
+          }
 
           BrowserApp.displayedDocumentChanged();
           this.contentDocumentIsDisplayed = true;
         }
         break;
     }
   },
 
--- a/mobile/android/components/SessionStore.idl
+++ b/mobile/android/components/SessionStore.idl
@@ -83,30 +83,30 @@ interface nsISessionStore : nsISupports
   nsIDOMNode forgetClosedTab(in nsIDOMWindow aWindow, in unsigned long aIndex);
 
   /**
    * @param aTab is the browser tab to get the value for.
    * @param aKey is the value's name.
    * 
    * @returns A string value or an empty string if none is set.
    */
-  AString getTabValue(in nsIDOMNode aTab, in AString aKey);
+  AString getTabValue(in jsval aTab, in AString aKey);
 
   /**
    * @param aTab         is the browser tab to set the value for.
    * @param aKey         is the value's name.
    * @param aStringValue is the value itself (use JSON.stringify/parse before setting JS objects).
    */
-  void setTabValue(in nsIDOMNode aTab, in AString aKey, in AString aStringValue);
+  void setTabValue(in jsval aTab, in AString aKey, in AString aStringValue);
 
   /**
    * @param aTab is the browser tab to get the value for.
    * @param aKey is the value's name.
    */
-  void deleteTabValue(in nsIDOMNode aTab, in AString aKey);
+  void deleteTabValue(in jsval aTab, in AString aKey);
 
   /**
    * @returns A boolean indicating we should restore previous browser session
    */
   boolean shouldRestore();
 
   /**
    * Restores the previous browser session using a fast, lightweight strategy
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -867,23 +867,23 @@ SessionStore.prototype = {
     if (!(aIndex in closedTabs))
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
 
     // remove closed tab from the array
     closedTabs.splice(aIndex, 1);
   },
 
   getTabValue: function ss_getTabValue(aTab, aKey) {
-    let browser = aTab.linkedBrowser;
+    let browser = aTab.browser;
     let data = browser.__SS_extdata || {};
     return data[aKey] || "";
   },
 
   setTabValue: function ss_setTabValue(aTab, aKey, aStringValue) {
-    let browser = aTab.linkedBrowser;
+    let browser = aTab.browser;
 
     // Thumbnails are actually stored in the cache, so do the save and update the URI
     if (aKey == "thumbnail") {
       let file = this._sessionCache.clone();
       file.append("thumbnail-" + browser.contentWindowId);
       file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
 
       let source = Services.io.newURI(aStringValue, "UTF8", null);
@@ -898,17 +898,17 @@ SessionStore.prototype = {
 
     if (!browser.__SS_extdata)
       browser.__SS_extdata = {};
     browser.__SS_extdata[aKey] = aStringValue;
     this.saveStateDelayed();
   },
 
   deleteTabValue: function ss_deleteTabValue(aTab, aKey) {
-    let browser = aTab.linkedBrowser;
+    let browser = aTab.browser;
     if (browser.__SS_extdata && browser.__SS_extdata[aKey])
       delete browser.__SS_extdata[aKey];
     else
       throw (Components.returnCode = Cr.NS_ERROR_INVALID_ARG);
   },
 
   shouldRestore: function ss_shouldRestore() {
     return this._shouldRestore;
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -226,16 +226,17 @@
 @BINPATH@/components/necko.xpt
 @BINPATH@/components/loginmgr.xpt
 @BINPATH@/components/parentalcontrols.xpt
 @BINPATH@/components/places.xpt
 @BINPATH@/components/plugin.xpt
 @BINPATH@/components/pref.xpt
 @BINPATH@/components/prefetch.xpt
 @BINPATH@/components/profile.xpt
+@BINPATH@/components/profiler.xpt
 @BINPATH@/components/proxyObject.xpt
 @BINPATH@/components/rdf.xpt
 @BINPATH@/components/satchel.xpt
 @BINPATH@/components/saxparser.xpt
 @BINPATH@/components/sessionstore.xpt
 #ifdef MOZ_SERVICES_SYNC
 @BINPATH@/components/services-crypto.xpt
 #endif
--- a/mobile/android/sync/java-sources.mn
+++ b/mobile/android/sync/java-sources.mn
@@ -1,1 +1,1 @@
-sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/ClientsDataDelegate.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/KeyBundleProvider.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/ConnectionMonitorThread.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/net/WBORequestDelegate.java sync/NoCollectionKeysSetException.java sync/NodeAuthenticationException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/NullClusterURLException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/ClientsDatabase.java sync/repositories/android/ClientsDatabaseAccessor.java sync/repositories/android/FennecTabsRepository.java sync/repositories/android/PasswordColumns.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/ClientRecord.java sync/repositories/domain/ClientRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoContentProviderException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAccounts.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FennecTabsServerSyncStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/stage/SyncClientsEngineStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java
+sync/AlreadySyncingException.java sync/CollectionKeys.java sync/CommandProcessor.java sync/CommandRunner.java sync/CredentialsSource.java sync/crypto/CryptoException.java sync/crypto/CryptoInfo.java sync/crypto/HKDF.java sync/crypto/HMACVerificationException.java sync/crypto/KeyBundle.java sync/crypto/MissingCryptoInputException.java sync/crypto/NoKeyBundleException.java sync/CryptoRecord.java sync/DelayedWorkTracker.java sync/delegates/ClientsDataDelegate.java sync/delegates/FreshStartDelegate.java sync/delegates/GlobalSessionCallback.java sync/delegates/InfoCollectionsDelegate.java sync/delegates/KeyUploadDelegate.java sync/delegates/MetaGlobalDelegate.java sync/delegates/WipeServerDelegate.java sync/ExtendedJSONObject.java sync/GlobalSession.java sync/HTTPFailureException.java sync/InfoCollections.java sync/jpake/BigIntegerHelper.java sync/jpake/Gx3OrGx4IsZeroOrOneException.java sync/jpake/IncorrectZkpException.java sync/jpake/JPakeClient.java sync/jpake/JPakeCrypto.java sync/jpake/JPakeJson.java sync/jpake/JPakeNoActivePairingException.java sync/jpake/JPakeNumGenerator.java sync/jpake/JPakeNumGeneratorRandom.java sync/jpake/JPakeParty.java sync/jpake/JPakeRequest.java sync/jpake/JPakeRequestDelegate.java sync/jpake/JPakeResponse.java sync/jpake/stage/CompleteStage.java sync/jpake/stage/ComputeFinalStage.java sync/jpake/stage/ComputeKeyVerificationStage.java sync/jpake/stage/ComputeStepOneStage.java sync/jpake/stage/ComputeStepTwoStage.java sync/jpake/stage/DecryptDataStage.java sync/jpake/stage/GetChannelStage.java sync/jpake/stage/GetRequestStage.java sync/jpake/stage/JPakeStage.java sync/jpake/stage/PutRequestStage.java sync/jpake/stage/VerifyPairingStage.java sync/jpake/Zkp.java sync/KeyBundleProvider.java sync/Logger.java sync/MetaGlobal.java sync/MetaGlobalException.java sync/MetaGlobalMissingEnginesException.java sync/MetaGlobalNotSetException.java sync/middleware/Crypto5MiddlewareRepository.java sync/middleware/Crypto5MiddlewareRepositorySession.java sync/middleware/MiddlewareRepository.java sync/middleware/MiddlewareRepositorySession.java sync/net/BaseResource.java sync/net/CompletedEntity.java sync/net/ConnectionMonitorThread.java sync/net/HandleProgressException.java sync/net/Resource.java sync/net/ResourceDelegate.java sync/net/SyncResourceDelegate.java sync/net/SyncResponse.java sync/net/SyncStorageCollectionRequest.java sync/net/SyncStorageCollectionRequestDelegate.java sync/net/SyncStorageRecordRequest.java sync/net/SyncStorageRequest.java sync/net/SyncStorageRequestDelegate.java sync/net/SyncStorageRequestIncrementalDelegate.java sync/net/SyncStorageResponse.java sync/net/TLSSocketFactory.java sync/net/WBOCollectionRequestDelegate.java sync/net/WBORequestDelegate.java sync/NoCollectionKeysSetException.java sync/NodeAuthenticationException.java sync/NonArrayJSONException.java sync/NonObjectJSONException.java sync/NullClusterURLException.java sync/PrefsSource.java sync/repositories/android/AndroidBrowserBookmarksDataAccessor.java sync/repositories/android/AndroidBrowserBookmarksRepository.java sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java sync/repositories/android/AndroidBrowserHistoryDataAccessor.java sync/repositories/android/AndroidBrowserHistoryDataExtender.java sync/repositories/android/AndroidBrowserHistoryRepository.java sync/repositories/android/AndroidBrowserHistoryRepositorySession.java sync/repositories/android/AndroidBrowserPasswordsDataAccessor.java sync/repositories/android/AndroidBrowserPasswordsRepository.java sync/repositories/android/AndroidBrowserPasswordsRepositorySession.java sync/repositories/android/AndroidBrowserRepository.java sync/repositories/android/AndroidBrowserRepositoryDataAccessor.java sync/repositories/android/AndroidBrowserRepositorySession.java sync/repositories/android/BrowserContractHelpers.java sync/repositories/android/CachedSQLiteOpenHelper.java sync/repositories/android/ClientsDatabase.java sync/repositories/android/ClientsDatabaseAccessor.java sync/repositories/android/FennecTabsRepository.java sync/repositories/android/RepoUtils.java sync/repositories/BookmarkNeedsReparentingException.java sync/repositories/BookmarksRepository.java sync/repositories/ConstrainedServer11Repository.java sync/repositories/delegates/DeferrableRepositorySessionCreationDelegate.java sync/repositories/delegates/DeferredRepositorySessionBeginDelegate.java sync/repositories/delegates/DeferredRepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/DeferredRepositorySessionFinishDelegate.java sync/repositories/delegates/DeferredRepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionBeginDelegate.java sync/repositories/delegates/RepositorySessionCleanDelegate.java sync/repositories/delegates/RepositorySessionCreationDelegate.java sync/repositories/delegates/RepositorySessionFetchRecordsDelegate.java sync/repositories/delegates/RepositorySessionFinishDelegate.java sync/repositories/delegates/RepositorySessionGuidsSinceDelegate.java sync/repositories/delegates/RepositorySessionStoreDelegate.java sync/repositories/delegates/RepositorySessionWipeDelegate.java sync/repositories/domain/BookmarkRecord.java sync/repositories/domain/BookmarkRecordFactory.java sync/repositories/domain/ClientRecord.java sync/repositories/domain/ClientRecordFactory.java sync/repositories/domain/HistoryRecord.java sync/repositories/domain/HistoryRecordFactory.java sync/repositories/domain/PasswordRecord.java sync/repositories/domain/Record.java sync/repositories/domain/TabsRecord.java sync/repositories/HashSetStoreTracker.java sync/repositories/HistoryRepository.java sync/repositories/IdentityRecordFactory.java sync/repositories/InactiveSessionException.java sync/repositories/InvalidBookmarkTypeException.java sync/repositories/InvalidRequestException.java sync/repositories/InvalidSessionTransitionException.java sync/repositories/MultipleRecordsForGuidException.java sync/repositories/NoContentProviderException.java sync/repositories/NoGuidForIdException.java sync/repositories/NoStoreDelegateException.java sync/repositories/NullCursorException.java sync/repositories/ParentNotFoundException.java sync/repositories/ProfileDatabaseException.java sync/repositories/RecordFactory.java sync/repositories/RecordFilter.java sync/repositories/Repository.java sync/repositories/RepositorySession.java sync/repositories/RepositorySessionBundle.java sync/repositories/Server11Repository.java sync/repositories/Server11RepositorySession.java sync/repositories/StoreTracker.java sync/repositories/StoreTrackingRepositorySession.java sync/setup/activities/AccountActivity.java sync/setup/activities/SetupFailureActivity.java sync/setup/activities/SetupSuccessActivity.java sync/setup/activities/SetupSyncActivity.java sync/setup/Constants.java sync/setup/SyncAccounts.java sync/setup/SyncAuthenticatorService.java sync/stage/AndroidBrowserBookmarksServerSyncStage.java sync/stage/AndroidBrowserHistoryServerSyncStage.java sync/stage/CheckPreconditionsStage.java sync/stage/CompletedStage.java sync/stage/EnsureClusterURLStage.java sync/stage/EnsureKeysStage.java sync/stage/FennecTabsServerSyncStage.java sync/stage/FetchInfoCollectionsStage.java sync/stage/FetchMetaGlobalStage.java sync/stage/GlobalSyncStage.java sync/stage/NoSuchStageException.java sync/stage/NoSyncIDException.java sync/stage/ServerSyncStage.java sync/stage/SyncClientsEngineStage.java sync/StubActivity.java sync/syncadapter/SyncAdapter.java sync/syncadapter/SyncService.java sync/SyncConfiguration.java sync/SyncConfigurationException.java sync/SyncException.java sync/synchronizer/ConcurrentRecordConsumer.java sync/synchronizer/RecordConsumer.java sync/synchronizer/RecordsChannel.java sync/synchronizer/RecordsChannelDelegate.java sync/synchronizer/RecordsConsumerDelegate.java sync/synchronizer/SerialRecordConsumer.java sync/synchronizer/SessionNotBegunException.java sync/synchronizer/Synchronizer.java sync/synchronizer/SynchronizerDelegate.java sync/synchronizer/SynchronizerSession.java sync/synchronizer/SynchronizerSessionDelegate.java sync/synchronizer/UnbundleError.java sync/synchronizer/UnexpectedSessionException.java sync/SynchronizerConfiguration.java sync/SynchronizerConfigurations.java sync/ThreadPool.java sync/UnexpectedJSONException.java sync/UnknownSynchronizerConfigurationVersionException.java sync/Utils.java
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -2058,17 +2058,17 @@ pref("slider.snapMultiplier", 6);
 // print_extra_margin enables platforms to specify an extra gap or margin
 // around the content of the page for Print Preview only
 pref("print.print_extra_margin", 90); // twips (90 twips is an eigth of an inch)
 
 // Whether to extend the native dialog with information on printing frames.
 pref("print.extend_native_print_dialog", true);
 
 // Locate Java by scanning the Sun JRE installation directory with a minimum version
-pref("plugin.scan.SunJRE", "1.3");
+pref("plugin.scan.SunJRE", "1.6");
 
 // Locate plugins by scanning the Adobe Acrobat installation directory with a minimum version
 pref("plugin.scan.Acrobat", "5.0");
 
 // Locate plugins by scanning the Quicktime installation directory with a minimum version
 pref("plugin.scan.Quicktime", "5.0");
 
 // Locate and scan the Window Media Player installation directory for plugins with a minimum version
--- a/mozglue/Makefile.in
+++ b/mozglue/Makefile.in
@@ -47,13 +47,17 @@ DIRS =
 ifdef MOZ_LINKER
 DIRS += linker
 endif
 
 ifeq (android,$(MOZ_WIDGET_TOOLKIT))
 DIRS += android
 endif
 
+ifeq (gonk,$(MOZ_WIDGET_TOOLKIT))
+DIRS += gonk
+endif
+
 DIRS += build
 
 TEST_DIRS = tests
 
 include $(topsrcdir)/config/rules.mk
--- a/mozglue/build/Makefile.in
+++ b/mozglue/build/Makefile.in
@@ -87,16 +87,23 @@ EXTRA_DSO_LDOPTS += $(ZLIB_LIBS)
 ifdef MOZ_MEMORY
 # To properly wrap jemalloc's pthread_atfork call.
 EXTRA_DSO_LDOPTS += -Wl,--wrap=pthread_atfork
 endif
 SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,$(DEPTH)/other-licenses/android)
 SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,../android)
 endif
 
+ifeq (gonk, $(MOZ_WIDGET_TOOLKIT))
+# To properly wrap jemalloc's pthread_atfork call.
+EXTRA_DSO_LDOPTS += -Wl,--wrap=pthread_atfork
+SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,android,$(DEPTH)/other-licenses/android)
+SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,gonk,../gonk)
+endif
+
 ifdef MOZ_LINKER
 # Add custom dynamic linker
 SHARED_LIBRARY_LIBS += $(call EXPAND_LIBNAME_PATH,linker,../linker)
 
 ifeq (arm, $(TARGET_CPU))
 EXTRA_DSO_LDOPTS += -Wl,-version-script,$(srcdir)/arm-eabi-filter
 endif
 
new file mode 100644
--- /dev/null
+++ b/mozglue/gonk/GonkGlue.cpp
@@ -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/. */
+
+#include <unistd.h>
+#include <vector>
+
+#define NS_EXPORT __attribute__ ((visibility("default")))
+
+/* Android doesn't have pthread_atfork(), so we need to use our own. */
+struct AtForkFuncs {
+  void (*prepare)(void);
+  void (*parent)(void);
+  void (*child)(void);
+};
+static std::vector<AtForkFuncs> atfork;
+
+extern "C" NS_EXPORT int
+__wrap_pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
+{
+  AtForkFuncs funcs;
+  funcs.prepare = prepare;
+  funcs.parent = parent;
+  funcs.child = child;
+  atfork.push_back(funcs);
+  return 0;
+}
+
+extern "C" NS_EXPORT pid_t
+__wrap_fork(void)
+{
+  pid_t pid;
+  for (std::vector<AtForkFuncs>::reverse_iterator it = atfork.rbegin();
+       it < atfork.rend(); ++it)
+    if (it->prepare)
+      it->prepare();
+
+  switch ((pid = fork())) {
+  case 0:
+    for (std::vector<AtForkFuncs>::iterator it = atfork.begin();
+         it < atfork.end(); ++it)
+      if (it->child)
+        it->child();
+    break;
+  default:
+    for (std::vector<AtForkFuncs>::iterator it = atfork.begin();
+         it < atfork.end(); ++it)
+      if (it->parent)
+        it->parent();
+  }
+  return pid;
+}
new file mode 100644
--- /dev/null
+++ b/mozglue/gonk/Makefile.in
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; 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/. */
+
+DEPTH		= ../..
+topsrcdir	= @top_srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+MODULE           = gonk
+LIBRARY_NAME     = gonk
+FORCE_STATIC_LIB = 1
+STL_FLAGS=
+
+DEFINES += \
+  -DANDROID_PACKAGE_NAME='"$(ANDROID_PACKAGE_NAME)"' \
+  $(NULL)
+
+CPPSRCS = \
+  GonkGlue.cpp \
+  $(NULL)
+
+LOCAL_INCLUDES += -I$(DEPTH)/build
+LOCAL_INCLUDES += -I$(topsrcdir)/xpcom/build
+
+include $(topsrcdir)/config/rules.mk
--- a/security/manager/ssl/src/nsCRLManager.cpp
+++ b/security/manager/ssl/src/nsCRLManager.cpp
@@ -33,18 +33,16 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsCRLManager.h"
 #include "nsCRLInfo.h"
 
 #include "nsCOMPtr.h"
-#include "nsIDateTimeFormat.h"
-#include "nsDateTimeFormatCID.h"
 #include "nsComponentManagerUtils.h"
 #include "nsReadableUtils.h"
 #include "nsNSSComponent.h"
 #include "nsCOMPtr.h"
 #include "nsICertificateDialogs.h"
 #include "nsIMutableArray.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch.h"
@@ -254,28 +252,26 @@ done:
         pref->GetCharPref(updateFreqCntPrefStr.get(),&dayCntStr);
       }
       dayCnt = atof(dayCntStr);
       nsMemory::Free(dayCntStr);
 
       bool toBeRescheduled = false;
       if(NS_SUCCEEDED(ComputeNextAutoUpdateTime(crlData, timingTypePref, dayCnt, &updateTime))){
         updateTimeStr.AssignWithConversion(updateTime);
-        nsMemory::Free(updateTime);
         pref->SetCharPref(updateTimePrefStr.get(),updateTimeStr.get());
         //Now, check if this update time is already in the past. This would
         //imply we have downloaded the same crl, or there is something wrong
         //with the next update date. We will not reschedule this crl in this
         //session anymore - or else, we land into a loop. It would anyway be
         //imported once the browser is restarted.
-        PRTime nextTime;
-        PR_ParseTimeString(updateTimeStr.get(),true, &nextTime);
-        if(LL_CMP(nextTime, > , PR_Now())){
+        if(LL_CMP(updateTime, > , PR_Now())){
           toBeRescheduled = true;
         }
+        nsMemory::Free(updateTime);
       }
       
       //Update the url to download from, next time
       crlData->GetLastFetchURL(updateURL);
       pref->SetCharPref(updateUrlPrefStr.get(),updateURL.get());
       
       pref->SetIntPref(updateErrCntPrefStr.get(),0);
       
@@ -414,16 +410,17 @@ nsCRLManager::DeleteCrl(PRUint32 aCrlInd
 }
 
 NS_IMETHODIMP
 nsCRLManager::ComputeNextAutoUpdateTime(nsICRLInfo *info, 
   PRUint32 autoUpdateType, double dayCnt, PRUnichar **nextAutoUpdate)
 {
   if (!info)
     return NS_ERROR_FAILURE;
+  NS_ENSURE_ARG_POINTER(nextAutoUpdate);
 
   PRTime microsecInDayCnt;
   PRTime now = PR_Now();
   PRTime tempTime;
   PRInt64 diff = 0;
   PRInt64 secsInDay = 86400UL;
   PRInt64 temp;
   PRInt64 cycleCnt = 0;
@@ -469,21 +466,16 @@ nsCRLManager::ComputeNextAutoUpdateTime(
   //Now, a basic constraing is that the next auto update date can never be after
   //next update, if one is defined
   if(LL_CMP(nextUpdate , > , 0 )) {
     if(LL_CMP(tempTime , > , nextUpdate)) {
       tempTime = nextUpdate;
     }
   }
 
-  nsAutoString nextAutoUpdateDate;
-  PRExplodedTime explodedTime;
-  nsCOMPtr<nsIDateTimeFormat> dateFormatter = do_CreateInstance(NS_DATETIMEFORMAT_CONTRACTID, &rv);
-  if (NS_FAILED(rv))
-    return rv;
-  PR_ExplodeTime(tempTime, PR_GMTParameters, &explodedTime);
-  dateFormatter->FormatPRExplodedTime(nsnull, kDateFormatShort, kTimeFormatSeconds,
-                                      &explodedTime, nextAutoUpdateDate);
-  *nextAutoUpdate = ToNewUnicode(nextAutoUpdateDate);
+  // Return value as string; no pref type for Int64/PRTime
+  char *tempTimeStr = PR_smprintf("%lli", tempTime);
+  *nextAutoUpdate = ToNewUnicode(nsDependentCString(tempTimeStr));
+  PR_smprintf_free(tempTimeStr);
 
   return NS_OK;
 }
 
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -1319,24 +1319,51 @@ nsresult nsNSSComponent::getParamsForNex
     if(crlsScheduledForDownload->Exists(&hashKey)){
       continue;
     }
 
     char *tempTimeString;
     PRTime tempTime;
     nsCAutoString timingPrefCString(updateTimePref);
     timingPrefCString.AppendWithConversion(tempCrlKey);
+    // No PRTime/Int64 type in prefs; stored as string; parsed here as PRInt64
     rv = pref->GetCharPref(timingPrefCString.get(), &tempTimeString);
     if (NS_FAILED(rv)){
-      continue;
-    }
-    rv = PR_ParseTimeString(tempTimeString,true, &tempTime);
-    nsMemory::Free(tempTimeString);
-    if (NS_FAILED(rv)){
-      continue;
+      // Assume corrupted. Force download. Pref should be reset after download.
+      tempTime = PR_Now();
+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+             ("get %s failed: forcing download\n", timingPrefCString.get()));
+    } else {
+      tempTime = (PRTime)nsCRT::atoll(tempTimeString);
+      nsMemory::Free(tempTimeString);
+      // nsCRT::atoll parses the first token in the string; three possibilities
+      //  -1- Alpha char: returns 0; change to PR_Now() and force update.
+      //  -2- Number (between epoch and PR_Now(), e.g. 0 - 1332280017 for
+      //      Tue Mar 20, 2012, 2:46pm approx): includes formatted date 
+      //      values (previous method of storing update date, e.g year, month 
+      //      or day, 2012, 1-31, 1-12 etc). Less than PR_Now() forces 
+      //      autoupdate.
+      //  -3- Number (larger than PR_Now()): no forced autoupdate
+      // Note: corrupt values within range of -2- will have an implicit 
+      // unflagged recovery. Corrupt values in range of -3- will be unflagged
+      // and unrecovered by this code.
+      if (tempTime == 0)
+        tempTime = PR_Now();
+#ifdef PR_LOGGING
+      PRExplodedTime explodedTime;
+      PR_ExplodeTime(tempTime, PR_GMTParameters, &explodedTime);
+      // Note: tm_month starts from 0 = Jan, hence +1
+      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
+             ("%s tempTime(%lli) "
+              "(m/d/y h:m:s = %02d/%02d/%d %02d:%02d:%02d GMT\n",
+              timingPrefCString.get(), tempTime,
+              explodedTime.tm_month+1, explodedTime.tm_mday,
+              explodedTime.tm_year, explodedTime.tm_hour,
+              explodedTime.tm_min, explodedTime.tm_sec));
+#endif
     }
 
     if(nearestUpdateTime == 0 || tempTime < nearestUpdateTime){
       nsCAutoString urlPrefCString(updateURLPref);
       urlPrefCString.AppendWithConversion(tempCrlKey);
       rv = pref->GetCharPref(urlPrefCString.get(), &tempUrl);
       if (NS_FAILED(rv) || (!tempUrl)){
         continue;
--- a/testing/mochitest/tests/SimpleTest/SimpleTest.js
+++ b/testing/mochitest/tests/SimpleTest/SimpleTest.js
@@ -310,21 +310,16 @@ SimpleTest.todo_isnot = function (a, b, 
 /**
  * Makes a test report, returns it as a DIV element.
 **/
 SimpleTest.report = function () {
     var passed = 0;
     var failed = 0;
     var todo = 0;
 
-    // Report tests which did not actually check anything.
-    if (SimpleTest._tests.length == 0)
-      // ToDo: Do s/todo/ok/ when all the tests are fixed. (Bug 483407)
-      SimpleTest.todo(false, "[SimpleTest.report()] No checks actually run.");
-
     var tallyAndCreateDiv = function (test) {
             var cls, msg, div;
             var diag = test.diag ? " - " + test.diag : "";
             if (test.todo && !test.result) {
                 todo++;
                 cls = "test_todo";
                 msg = "todo | " + test.name + diag;
             } else if (test.result && !test.todo) {
@@ -462,16 +457,17 @@ SimpleTest.requestLongerTimeout = functi
     if (parentRunner) {
         parentRunner.requestLongerTimeout(factor);
     }
 }
 
 SimpleTest.waitForFocus_started = false;
 SimpleTest.waitForFocus_loaded = false;
 SimpleTest.waitForFocus_focused = false;
+SimpleTest._pendingWaitForFocusCount = 0;
 
 /**
  * If the page is not yet loaded, waits for the load event. In addition, if
  * the page is not yet focused, focuses and waits for the window to be
  * focused. Calls the callback when completed. If the current page is
  * 'about:blank', then the page is assumed to not yet be loaded. Pass true for
  * expectBlankPage to not make this assumption if you expect a blank page to
  * be present.
@@ -482,16 +478,17 @@ SimpleTest.waitForFocus_focused = false;
  * @param callback
  *        function called when load and focus are complete
  * @param targetWindow
  *        optional window to be loaded and focused, defaults to 'window'
  * @param expectBlankPage
  *        true if targetWindow.location is 'about:blank'. Defaults to false
  */
 SimpleTest.waitForFocus = function (callback, targetWindow, expectBlankPage) {
+    SimpleTest._pendingWaitForFocusCount++;
     if (!targetWindow)
       targetWindow = window;
 
     SimpleTest.waitForFocus_started = false;
     expectBlankPage = !!expectBlankPage;
 
     var childTargetWindow = {};
     SpecialPowers.getFocusedElementForWindow(targetWindow, true, childTargetWindow);
@@ -503,16 +500,17 @@ SimpleTest.waitForFocus = function (call
     function getHref(aWindow) {
       return SpecialPowers.getPrivilegedProps(aWindow, 'location.href');
     }
 
     function maybeRunTests() {
         if (SimpleTest.waitForFocus_loaded &&
             SimpleTest.waitForFocus_focused &&
             !SimpleTest.waitForFocus_started) {
+            SimpleTest._pendingWaitForFocusCount--;
             SimpleTest.waitForFocus_started = true;
             setTimeout(callback, 0, targetWindow);
         }
     }
 
     function waitForEvent(event) {
         try {
             // Check to make sure that this isn't a load event for a blank or
@@ -670,16 +668,32 @@ SimpleTest.executeSoon = function(aFunc)