Merge from mozilla-central.
authorDavid Anderson <danderson@mozilla.com>
Fri, 09 Mar 2012 13:37:58 -0800
changeset 106370 57680b93b9c2cf4dea49c120c5a6321966bce3c3
parent 106369 8a9d061f5008a186b51bc115832c9bcce4d733f7 (current diff)
parent 88849 bfb1b7520ce9714dd7d089fb266fc40f004db923 (diff)
child 106371 3ab9cb07980d077b9c0e4c4f0425314dec431d1d
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
milestone13.0a1
Merge from mozilla-central.
accessible/src/atk/nsApplicationAccessibleWrap.cpp
accessible/src/atk/nsApplicationAccessibleWrap.h
accessible/src/base/NotificationController.cpp
accessible/src/base/nsAccessible.h
accessible/src/base/nsDocAccessible.cpp
accessible/src/base/nsDocAccessible.h
accessible/src/html/nsHTMLFormControlAccessible.cpp
accessible/src/html/nsHTMLFormControlAccessible.h
accessible/src/html/nsHyperTextAccessible.cpp
accessible/src/html/nsHyperTextAccessible.h
accessible/src/xforms/nsXFormsAccessible.cpp
accessible/src/xforms/nsXFormsAccessible.h
accessible/src/xul/nsXULFormControlAccessible.cpp
accessible/src/xul/nsXULFormControlAccessible.h
accessible/tests/mochitest/common.js
accessible/tests/mochitest/events.js
aclocal.m4
browser/app/profile/firefox.js
browser/base/content/browser.js
browser/base/content/nsContextMenu.js
browser/base/jar.mn
browser/components/dirprovider/DirectoryProvider.cpp
browser/components/migration/src/nsIEProfileMigrator.cpp
browser/components/nsBrowserContentHandler.js
browser/components/nsBrowserGlue.js
browser/devtools/styleinspector/CssLogic.jsm
browser/themes/gnomestripe/fullscreen-video.css
browser/themes/gnomestripe/section_collapsed-rtl.png
browser/themes/gnomestripe/section_collapsed.png
browser/themes/gnomestripe/section_expanded.png
browser/themes/pinstripe/fullscreen-video.css
browser/themes/pinstripe/section_collapsed-rtl.png
browser/themes/pinstripe/section_collapsed.png
browser/themes/pinstripe/section_expanded.png
browser/themes/winstripe/fullscreen-video.css
browser/themes/winstripe/section_collapsed-rtl.png
browser/themes/winstripe/section_collapsed.png
browser/themes/winstripe/section_expanded.png
build/mobile/robocop/Actions.java.in
build/unix/check_debug_ranges.py
caps/include/nsPrincipal.h
caps/include/nsScriptSecurityManager.h
caps/src/nsJSPrincipals.cpp
caps/src/nsNullPrincipal.cpp
caps/src/nsPrincipal.cpp
caps/src/nsScriptSecurityManager.cpp
caps/src/nsSystemPrincipal.cpp
config/autoconf.mk.in
configure.in
content/base/public/nsContentUtils.h
content/base/public/nsIHTMLToTextSink.h
content/base/src/nsContentUtils.cpp
content/base/src/nsFrameMessageManager.cpp
content/base/src/nsGenericElement.cpp
content/base/src/nsGkAtomList.h
content/base/src/nsNodeUtils.cpp
content/base/src/nsPlainTextSerializer.cpp
content/base/src/nsPlainTextSerializer.h
content/base/src/nsWebSocket.cpp
content/canvas/src/CanvasUtils.cpp
content/canvas/src/CanvasUtils.h
content/canvas/src/WebGLContext.h
content/canvas/test/webgl/test_webgl_conformance_test_suite.html
content/html/content/src/nsFormSubmission.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLTextAreaElement.cpp
content/html/document/src/ImageDocument.cpp
content/media/nsMediaCache.cpp
content/svg/content/src/nsSVGElement.cpp
content/svg/content/src/nsSVGUseElement.cpp
content/xbl/src/nsBindingManager.cpp
content/xslt/src/xml/txDOM.h
content/xslt/src/xslt/txKey.h
content/xslt/src/xslt/txKeyFunctionCall.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/base/nsFocusManager.cpp
dom/base/nsGlobalWindow.cpp
dom/base/nsJSEnvironment.cpp
dom/indexedDB/AsyncConnectionHelper.cpp
dom/indexedDB/IDBDatabase.cpp
dom/indexedDB/IDBIndex.cpp
dom/indexedDB/IDBObjectStore.cpp
dom/interfaces/base/domstubs.idl
dom/src/storage/nsDOMStorage.cpp
dom/src/storage/nsDOMStorage.h
dom/workers/RuntimeService.cpp
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerScope.cpp
editor/txtsvc/src/nsTextServicesDocument.cpp
embedding/android/GeckoAppShell.java
embedding/components/windowwatcher/src/nsWindowWatcher.cpp
gfx/gl/GLContext.cpp
gfx/gl/GLContextProviderEGL.cpp
gfx/thebes/gfxContext.cpp
gfx/thebes/gfxDWriteFontList.cpp
gfx/thebes/gfxDWriteFontList.h
gfx/thebes/gfxFT2Fonts.cpp
gfx/thebes/gfxFT2Fonts.h
gfx/thebes/gfxFont.cpp
gfx/thebes/gfxFont.h
gfx/thebes/gfxFontUtils.cpp
gfx/thebes/gfxFontUtils.h
gfx/thebes/gfxGDIFontList.cpp
gfx/thebes/gfxMacPlatformFontList.h
gfx/thebes/gfxMacPlatformFontList.mm
gfx/thebes/gfxPlatform.cpp
gfx/thebes/gfxPlatform.h
gfx/thebes/gfxPlatformFontList.cpp
gfx/thebes/gfxPlatformFontList.h
gfx/thebes/gfxPlatformGtk.cpp
gfx/thebes/gfxPlatformMac.cpp
gfx/thebes/gfxPlatformMac.h
gfx/thebes/gfxQtPlatform.cpp
gfx/thebes/gfxUserFontSet.cpp
gfx/thebes/gfxUserFontSet.h
gfx/thebes/gfxWindowsNativeDrawing.cpp
gfx/thebes/gfxWindowsNativeDrawing.h
gfx/thebes/gfxWindowsPlatform.cpp
gfx/thebes/gfxWindowsPlatform.h
image/src/RasterImage.cpp
ipc/testshell/XPCShellEnvironment.cpp
js/src/Makefile.in
js/src/MemoryMetrics.cpp
js/src/configure.in
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/Parser.cpp
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jsarray.cpp
js/src/jsarray.h
js/src/jsatom.cpp
js/src/jsbool.cpp
js/src/jsbool.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.cpp
js/src/jscompartment.h
js/src/jsexn.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsinterpinlines.h
js/src/jsnum.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/json.cpp
js/src/jsopcode.cpp
js/src/jsproxy.cpp
js/src/jsprvtd.h
js/src/jspubtd.h
js/src/jsscope.cpp
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/jstypedarray.cpp
js/src/jstypedarray.h
js/src/jsxml.cpp
js/src/methodjit/Compiler.cpp
js/src/methodjit/Compiler.h
js/src/methodjit/MonoIC.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/shell/js.cpp
js/src/shell/jsworkers.cpp
js/src/vm/Debugger.cpp
js/src/vm/GlobalObject.cpp
js/src/vm/RegExpObject-inl.h
js/src/vm/RegExpObject.cpp
js/src/vm/RegExpObject.h
js/src/vm/String.cpp
js/xpconnect/loader/mozJSComponentLoader.cpp
js/xpconnect/loader/mozJSSubScriptLoader.cpp
js/xpconnect/shell/xpcshell.cpp
js/xpconnect/src/XPCComponents.cpp
js/xpconnect/src/XPCJSRuntime.cpp
js/xpconnect/src/XPCVariant.cpp
js/xpconnect/src/XPCWrappedJS.cpp
js/xpconnect/src/XPCWrappedJSClass.cpp
js/xpconnect/src/XPCWrappedNative.cpp
js/xpconnect/src/nsXPConnect.cpp
js/xpconnect/src/xpcprivate.h
js/xpconnect/src/xpcpublic.h
js/xpconnect/wrappers/AccessCheck.cpp
layout/base/FramePropertyTable.cpp
layout/base/FramePropertyTable.h
layout/base/nsBidiPresUtils.cpp
layout/base/nsCSSFrameConstructor.cpp
layout/base/nsLayoutUtils.cpp
layout/base/nsPresContext.cpp
layout/base/nsPresContext.h
layout/base/tests/Makefile.in
layout/build/nsLayoutModule.cpp
layout/forms/nsComboboxControlFrame.cpp
layout/forms/nsComboboxControlFrame.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsBlockReflowContext.cpp
layout/generic/nsContainerFrame.cpp
layout/generic/nsContainerFrame.h
layout/generic/nsFrame.cpp
layout/generic/nsFrame.h
layout/generic/nsHTMLParts.h
layout/generic/nsIFrame.h
layout/generic/nsLineBox.cpp
layout/generic/nsLineBox.h
layout/generic/nsObjectFrame.cpp
layout/generic/nsTextFrameThebes.cpp
layout/inspector/src/inDOMView.cpp
layout/mathml/nsMathMLContainerFrame.cpp
layout/reftests/bugs/reftest.list
layout/reftests/svg/smil/reftest.list
layout/style/nsCSSAnonBoxes.cpp
layout/style/nsCSSParser.cpp
layout/style/nsCSSPseudoClasses.cpp
layout/style/nsCSSPseudoElements.cpp
layout/style/nsCSSScanner.cpp
layout/style/nsCSSScanner.h
layout/svg/base/src/nsSVGGradientFrame.h
layout/svg/base/src/nsSVGUtils.cpp
layout/tables/nsTableFrame.cpp
layout/tables/nsTableFrame.h
layout/tables/nsTableOuterFrame.cpp
layout/tables/nsTableOuterFrame.h
layout/xul/base/src/nsBoxFrame.h
layout/xul/base/src/nsMenuFrame.cpp
layout/xul/base/src/nsMenuFrame.h
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/AutoCompletePopup.java
mobile/android/base/GeckoApp.java
mobile/android/base/GeckoAppShell.java
mobile/android/base/Makefile.in
mobile/xul/app/mobile.js
mobile/xul/themes/core/browser.css
mobile/xul/themes/core/gingerbread/browser.css
mobile/xul/themes/core/honeycomb/browser.css
modules/libpref/src/init/all.js
mozglue/android/APKOpen.cpp
mozglue/android/APKOpen.h
mozglue/android/Makefile.in
netwerk/base/src/nsIOService.cpp
netwerk/protocol/http/HttpChannelChild.cpp
netwerk/protocol/http/HttpChannelChild.h
parser/html/nsHtml5TreeOperation.cpp
rdf/base/src/nsRDFContentSink.cpp
security/manager/ssl/src/nsCrypto.cpp
security/manager/ssl/src/nsNSSCertHelper.cpp
services/sync/modules/policies.js
services/sync/tests/unit/head_http_server.js
services/sync/tests/unit/test_syncengine_sync.js
services/sync/tests/unit/test_syncscheduler.js
startupcache/test/TestStartupCache.cpp
testing/testsuite-targets.mk
testing/xpcshell/xpcshell.ini
toolkit/components/aboutmemory/content/aboutMemory.js
toolkit/components/alerts/nsAlertsService.cpp
toolkit/components/downloads/nsDownloadManager.cpp
toolkit/components/places/History.cpp
toolkit/components/telemetry/Telemetry.cpp
toolkit/components/telemetry/TelemetryHistograms.h
toolkit/components/telemetry/TelemetryPing.js
toolkit/components/telemetry/tests/unit/test_TelemetryPing.js
toolkit/content/license.html
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/content/setting.xml
toolkit/mozapps/extensions/test/browser/addons/browser_inlinesettings1/options.xul
toolkit/mozapps/extensions/test/browser/browser_inlinesettings.js
toolkit/themes/winstripe/mozapps/extensions/extensions.css
toolkit/xre/nsAppRunner.cpp
widget/android/AndroidBridge.cpp
widget/android/AndroidBridge.h
widget/gtk2/nsGtkIMModule.cpp
widget/gtk2/nsGtkIMModule.h
widget/gtk2/nsNativeThemeGTK.cpp
widget/gtk2/nsNativeThemeGTK.h
widget/windows/JumpListBuilder.cpp
widget/windows/nsNativeThemeWin.cpp
widget/windows/nsNativeThemeWin.h
widget/windows/nsUXThemeData.cpp
widget/windows/nsUXThemeData.h
widget/windows/nsWindow.cpp
widget/windows/nsWindow.h
widget/windows/nsWindowDefs.h
widget/xpwidgets/GfxInfoX11.cpp
widget/xpwidgets/nsBaseWidget.h
widget/xpwidgets/nsIdleService.cpp
widget/xpwidgets/nsNativeTheme.cpp
widget/xpwidgets/nsNativeTheme.h
xpcom/base/nsStackWalk.cpp
xpcom/ds/nsAtomTable.cpp
xpcom/ds/nsDoubleHashtable.h
xpcom/glue/nsTHashtable.h
xpcom/io/nsDirectoryService.cpp
xpcom/tests/TestAtoms.cpp
--- a/accessible/public/nsIAccessibleEditableText.idl
+++ b/accessible/public/nsIAccessibleEditableText.idl
@@ -38,17 +38,17 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsISupports.idl"
 
 interface nsIEditor;
 
-[scriptable, uuid(52837507-202d-4e72-a482-5f068a1fd720)]
+[scriptable, uuid(e242d495-5cde-4b1c-8c84-2525b14939f5)]
 interface nsIAccessibleEditableText : nsISupports
 {
   /**
    * Sets the attributes for the text between the two given indices. The old
    * attributes are replaced by the new list of attributes. For example,
    * sets font styles, such as italic, bold...
    *
    * @param startPos - start index of the text whose attributes are modified.
@@ -98,24 +98,9 @@ interface nsIAccessibleEditableText : ns
 
   /**
    * Pastes text from the clipboard.
    *
    * @param position - index at which to insert the text from the system
    *                   clipboard into the text represented by this object.
    */
   void pasteText (in long position);
-
-  /**
-   * Returns an editor associated with the accessible.
-   */
-  [noscript] readonly attribute nsIEditor associatedEditor;
 };
-
-/*
- Assumptions:
-
- selectAttributes method takes an nsISupports parameter.
-        'set' methods throw exception on failure.
- 'wstring' inputs are potentially multibyte (UTF-16 for
-        instance); 'string' and UTF-8 may be a better choice.
-
-*/
--- a/accessible/src/atk/nsApplicationAccessibleWrap.cpp
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.cpp
@@ -692,16 +692,26 @@ nsApplicationAccessibleWrap::Unload()
     }
     // if (sATKLib) {
     //     PR_UnloadLibrary(sATKLib);
     //     sATKLib = nsnull;
     // }
 }
 
 NS_IMETHODIMP
+nsApplicationAccessibleWrap::GetName(nsAString& aName)
+{
+  // ATK doesn't provide a way to obtain an application name (for example,
+  // Firefox or Thunderbird) like IA2 does. Thus let's return an application
+  // name as accessible name that was used to get a branding name (for example,
+  // Minefield aka nightly Firefox or Daily aka nightly Thunderbird).
+  return GetAppName(aName);
+}
+
+NS_IMETHODIMP
 nsApplicationAccessibleWrap::GetNativeInterface(void **aOutAccessible)
 {
     *aOutAccessible = nsnull;
 
     if (!mAtkObject) {
         mAtkObject =
             reinterpret_cast<AtkObject *>
                             (g_object_new(MAI_TYPE_ATK_OBJECT, NULL));
--- a/accessible/src/atk/nsApplicationAccessibleWrap.h
+++ b/accessible/src/atk/nsApplicationAccessibleWrap.h
@@ -52,16 +52,18 @@ public:
 public:
     nsApplicationAccessibleWrap();
     virtual ~nsApplicationAccessibleWrap();
 
     // nsAccessNode
     virtual bool Init();
 
     // nsAccessible
+    NS_IMETHOD GetName(nsAString &aName);
+
     virtual bool AppendChild(nsAccessible* aChild);
     virtual bool RemoveChild(nsAccessible* aChild);
 
     // return the atk object for app root accessible
     NS_IMETHOD GetNativeInterface(void **aOutAccessible);
 };
 
 #endif   /* __NS_APP_ROOT_ACCESSIBLE_H__ */
--- a/accessible/src/base/NotificationController.cpp
+++ b/accessible/src/base/NotificationController.cpp
@@ -675,18 +675,17 @@ NotificationController::CreateTextChange
     return;
 
   nsHyperTextAccessible* textAccessible = container->AsHyperText();
   if (!textAccessible)
     return;
 
   // Don't fire event for the first html:br in an editor.
   if (aEvent->mAccessible->Role() == roles::WHITESPACE) {
-    nsCOMPtr<nsIEditor> editor;
-    textAccessible->GetAssociatedEditor(getter_AddRefs(editor));
+    nsCOMPtr<nsIEditor> editor = textAccessible->GetEditor();
     if (editor) {
       bool isEmpty = false;
       editor->GetDocumentIsEmpty(&isEmpty);
       if (isEmpty)
         return;
     }
   }
 
--- a/accessible/src/base/nsAccessible.h
+++ b/accessible/src/base/nsAccessible.h
@@ -210,16 +210,21 @@ public:
 
   /**
    * Return the states of accessible, not taking into account ARIA states.
    * Use State() to get complete set of states.
    */
   virtual PRUint64 NativeState();
 
   /**
+   * Return bit set of invisible and offscreen states.
+   */
+  PRUint64 VisibilityState();
+
+  /**
    * Returns attributes for accessible without explicitly setted ARIA
    * attributes.
    */
   virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
 
   /**
    * Used by ChildAtPoint() method to get direct or deepest child at point.
    */
@@ -697,18 +702,16 @@ protected:
   /**
    * Return ARIA role (helper method).
    */
   mozilla::a11y::role ARIARoleInternal();
 
   virtual nsIFrame* GetBoundsFrame();
   virtual void GetBoundsRect(nsRect& aRect, nsIFrame** aRelativeFrame);
 
-  PRUint64 VisibilityState(); 
-
   //////////////////////////////////////////////////////////////////////////////
   // Name helpers
 
   /**
    * Compute the name of HTML node.
    */
   nsresult GetHTMLName(nsAString& aName);
 
--- a/accessible/src/base/nsDocAccessible.cpp
+++ b/accessible/src/base/nsDocAccessible.cpp
@@ -328,18 +328,17 @@ nsDocAccessible::NativeState()
     state |= states::BUSY;
 
   nsIFrame* frame = GetFrame();
   if (!frame ||
       !frame->IsVisibleConsideringAncestors(nsIFrame::VISIBILITY_CROSS_CHROME_CONTENT_BOUNDARY)) {
     state |= states::INVISIBLE | states::OFFSCREEN;
   }
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   state |= editor ? states::EDITABLE : states::READONLY;
 
   return state;
 }
 
 // nsAccessible public method
 void
 nsDocAccessible::ApplyARIAState(PRUint64* aState)
@@ -548,47 +547,42 @@ nsDocAccessible::GetVirtualCursor(nsIAcc
     mVirtualCursor = new nsAccessiblePivot(this);
     mVirtualCursor->AddObserver(this);
   }
 
   NS_ADDREF(*aVirtualCursor = mVirtualCursor);
   return NS_OK;
 }
 
-// nsIAccessibleHyperText method
-NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+// nsHyperTextAccessible method
+already_AddRefed<nsIEditor>
+nsDocAccessible::GetEditor() const
 {
-  NS_ENSURE_ARG_POINTER(aEditor);
-  *aEditor = nsnull;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   // Check if document is editable (designMode="on" case). Otherwise check if
   // the html:body (for HTML document case) or document element is editable.
   if (!mDocument->HasFlag(NODE_IS_EDITABLE) &&
       !mContent->HasFlag(NODE_IS_EDITABLE))
-    return NS_OK;
+    return nsnull;
 
   nsCOMPtr<nsISupports> container = mDocument->GetContainer();
   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(container));
   if (!editingSession)
-    return NS_OK; // No editing session interface
+    return nsnull; // No editing session interface
 
   nsCOMPtr<nsIEditor> editor;
   editingSession->GetEditorForWindow(mDocument->GetWindow(), getter_AddRefs(editor));
-  if (!editor) {
-    return NS_OK;
-  }
-  bool isEditable;
+  if (!editor)
+    return nsnull;
+
+  bool isEditable = false;
   editor->GetIsDocumentEditable(&isEditable);
-  if (isEditable) {
-    NS_ADDREF(*aEditor = editor);
-  }
-  return NS_OK;
+  if (isEditable)
+    return editor.forget();
+
+  return nsnull;
 }
 
 // nsDocAccessible public method
 nsAccessible*
 nsDocAccessible::GetAccessible(nsINode* aNode) const
 {
   nsAccessible* accessible = mNodeToAccessibleMap.Get(aNode);
 
--- a/accessible/src/base/nsDocAccessible.h
+++ b/accessible/src/base/nsDocAccessible.h
@@ -128,18 +128,18 @@ public:
   virtual void ApplyARIAState(PRUint64* aState);
 
   virtual void SetRoleMapEntry(nsRoleMapEntry* aRoleMapEntry);
 
 #ifdef DEBUG_ACCDOCMGR
   virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
 #endif
 
-  // nsIAccessibleText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsDocAccessible
 
   /**
    * Return presentation shell for this document accessible.
    */
   nsIPresShell* PresShell() const { return mPresShell; }
 
--- a/accessible/src/html/nsHTMLFormControlAccessible.cpp
+++ b/accessible/src/html/nsHTMLFormControlAccessible.cpp
@@ -539,39 +539,40 @@ NS_IMETHODIMP nsHTMLTextFieldAccessible:
     if ( element ) {
       return element->Focus();
     }
     return NS_ERROR_FAILURE;
   }
   return NS_ERROR_INVALID_ARG;
 }
 
-NS_IMETHODIMP nsHTMLTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsHTMLTextFieldAccessible::GetEditor() const
 {
-  *aEditor = nsnull;
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(mContent));
-  NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
+  if (!editableElt)
+    return nsnull;
 
   // nsGenericHTMLElement::GetEditor has a security check.
   // Make sure we're not restricted by the permissions of
   // whatever script is currently running.
   nsCOMPtr<nsIJSContextStack> stack =
     do_GetService("@mozilla.org/js/xpc/ContextStack;1");
   bool pushed = stack && NS_SUCCEEDED(stack->Push(nsnull));
 
   nsCOMPtr<nsIEditor> editor;
-  nsresult rv = editableElt->GetEditor(aEditor);
+  editableElt->GetEditor(getter_AddRefs(editor));
 
   if (pushed) {
     JSContext* cx;
     stack->Pop(&cx);
     NS_ASSERTION(!cx, "context should be null");
   }
 
-  return rv;
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLTextFieldAccessible: Widgets
 
 bool
 nsHTMLTextFieldAccessible::IsWidget() const
 {
--- a/accessible/src/html/nsHTMLFormControlAccessible.h
+++ b/accessible/src/html/nsHTMLFormControlAccessible.h
@@ -133,18 +133,18 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& _retval); 
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual nsresult GetNameInternal(nsAString& aName);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 State();
   virtual PRUint64 NativeState();
 
--- a/accessible/src/html/nsHyperTextAccessible.cpp
+++ b/accessible/src/html/nsHyperTextAccessible.cpp
@@ -42,16 +42,17 @@
 #include "nsAccessibilityService.h"
 #include "nsAccUtils.h"
 #include "nsDocAccessible.h"
 #include "nsTextAttrs.h"
 #include "Role.h"
 #include "States.h"
 
 #include "nsIClipboard.h"
+#include "nsContentUtils.h"
 #include "nsFocusManager.h"
 #include "nsIDOMCharacterData.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMRange.h"
 #include "nsIDOMXULDocument.h"
 #include "nsIEditingSession.h"
 #include "nsIEditor.h"
 #include "nsIFrame.h"
@@ -160,18 +161,17 @@ nsHyperTextAccessible::NativeRole()
   return roles::TEXT_CONTAINER; // In ATK this works
 }
 
 PRUint64
 nsHyperTextAccessible::NativeState()
 {
   PRUint64 states = nsAccessibleWrap::NativeState();
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   if (editor) {
     PRUint32 flags;
     editor->GetFlags(&flags);
     if (0 == (flags & nsIPlaintextEditor::eEditorReadonlyMask)) {
       states |= states::EDITABLE;
     }
   } else if (mContent->Tag() == nsGkAtoms::article) {
     // We want <article> to behave like a document in terms of readonly state.
@@ -705,18 +705,17 @@ nsHyperTextAccessible::HypertextOffsetsT
   *aEndNode = nsnull;
 
   NS_ENSURE_ARG_POINTER(aEndOffset);
   *aEndOffset = -1;
 
   // If the given offsets are 0 and associated editor is empty then return
   // collapsed range with editor root element as range container.
   if (aStartHTOffset == 0 && aEndHTOffset == 0) {
-    nsCOMPtr<nsIEditor> editor;
-    GetAssociatedEditor(getter_AddRefs(editor));
+    nsCOMPtr<nsIEditor> editor = GetEditor();
     if (editor) {
       bool isEmpty = false;
       editor->GetDocumentIsEmpty(&isEmpty);
       if (isEmpty) {
         nsCOMPtr<nsIDOMElement> editorRootElm;
         editor->GetRootElement(getter_AddRefs(editorRootElm));
 
         nsCOMPtr<nsIDOMNode> editorRoot(do_QueryInterface(editorRootElm));
@@ -1148,25 +1147,24 @@ nsHyperTextAccessible::GetTextAttributes
                               accAtOffsetIdx);
   nsresult rv = textAttrsMgr.GetAttributes(*aAttributes, &startOffset,
                                            &endOffset);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Compute spelling attributes on text accessible only.
   nsIFrame *offsetFrame = accAtOffset->GetFrame();
   if (offsetFrame && offsetFrame->GetType() == nsGkAtoms::textFrame) {
-    nsCOMPtr<nsIDOMNode> node = accAtOffset->DOMNode();
-
     PRInt32 nodeOffset = 0;
     nsresult rv = RenderedToContentOffset(offsetFrame, offsetInAcc,
                                           &nodeOffset);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Set 'misspelled' text attribute.
-    rv = GetSpellTextAttribute(node, nodeOffset, &startOffset, &endOffset,
+    rv = GetSpellTextAttribute(accAtOffset->GetNode(), nodeOffset,
+                               &startOffset, &endOffset,
                                aAttributes ? *aAttributes : nsnull);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   *aStartOffset = startOffset;
   *aEndOffset = endOffset;
   return NS_OK;
 }
@@ -1450,118 +1448,121 @@ NS_IMETHODIMP nsHyperTextAccessible::Set
     return InsertText(aText, 0);
   }
   return NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::InsertText(const nsAString &aText, PRInt32 aPosition)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
 
   nsCOMPtr<nsIPlaintextEditor> peditor(do_QueryInterface(editor));
   NS_ENSURE_STATE(peditor);
 
   nsresult rv = SetSelectionRange(aPosition, aPosition);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return peditor->InsertText(aText);
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::CopyText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Copy();
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::CutText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Cut();
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::DeleteText(PRInt32 aStartPos, PRInt32 aEndPos)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aStartPos, aEndPos);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->DeleteSelection(nsIEditor::eNone);
 }
 
 NS_IMETHODIMP
 nsHyperTextAccessible::PasteText(PRInt32 aPosition)
 {
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  if (IsDefunct())
+    return NS_ERROR_FAILURE;
+
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_STATE(editor);
 
   nsresult rv = SetSelectionRange(aPosition, aPosition);
   NS_ENSURE_SUCCESS(rv, rv);
 
   return editor->Paste(nsIClipboard::kGlobalClipboard);
 }
 
-NS_IMETHODIMP
-nsHyperTextAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsHyperTextAccessible::GetEditor() const
 {
-  NS_ENSURE_ARG_POINTER(aEditor);
-  *aEditor = nsnull;
-
-  if (IsDefunct())
-    return NS_ERROR_FAILURE;
-
   if (!mContent->HasFlag(NODE_IS_EDITABLE)) {
     // If we're inside an editable container, then return that container's editor
-    nsCOMPtr<nsIAccessible> ancestor, current = this;
-    while (NS_SUCCEEDED(current->GetParent(getter_AddRefs(ancestor))) && ancestor) {
-      nsRefPtr<nsHyperTextAccessible> ancestorTextAccessible;
-      ancestor->QueryInterface(NS_GET_IID(nsHyperTextAccessible),
-                               getter_AddRefs(ancestorTextAccessible));
-      if (ancestorTextAccessible) {
+    nsAccessible* ancestor = Parent();
+    while (ancestor) {
+      nsHyperTextAccessible* hyperText = ancestor->AsHyperText();
+      if (hyperText) {
         // Recursion will stop at container doc because it has its own impl
-        // of GetAssociatedEditor()
-        return ancestorTextAccessible->GetAssociatedEditor(aEditor);
+        // of GetEditor()
+        return hyperText->GetEditor();
       }
-      current = ancestor;
+
+      ancestor = ancestor->Parent();
     }
-    return NS_OK;
+
+    return nsnull;
   }
 
   nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem =
     nsCoreUtils::GetDocShellTreeItemFor(mContent);
   nsCOMPtr<nsIEditingSession> editingSession(do_GetInterface(docShellTreeItem));
   if (!editingSession)
-    return NS_OK; // No editing session interface
-
-  NS_ENSURE_TRUE(mDoc, NS_ERROR_FAILURE);
-  nsIDocument* docNode = mDoc->GetDocumentNode();
-  NS_ENSURE_TRUE(docNode, NS_ERROR_FAILURE);
+    return nsnull; // No editing session interface
 
   nsCOMPtr<nsIEditor> editor;
-  return editingSession->GetEditorForWindow(docNode->GetWindow(), aEditor);
+  nsIDocument* docNode = mDoc->GetDocumentNode();
+  editingSession->GetEditorForWindow(docNode->GetWindow(),
+                                     getter_AddRefs(editor));
+  return editor.forget();
 }
 
 /**
   * =================== Caret & Selection ======================
   */
 
 nsresult
 nsHyperTextAccessible::SetSelectionRange(PRInt32 aStartPos, PRInt32 aEndPos)
@@ -1765,18 +1766,17 @@ nsHyperTextAccessible::GetSelectionDOMRa
     return;
 
   nsISelection* domSel = frameSelection->GetSelection(aType);
   if (!domSel)
     return;
 
   nsCOMPtr<nsINode> startNode = GetNode();
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   if (editor) {
     nsCOMPtr<nsIDOMElement> editorRoot;
     editor->GetRootElement(getter_AddRefs(editorRoot));
     startNode = do_QueryInterface(editorRoot);
   }
 
   if (!startNode)
     return;
@@ -1784,23 +1784,21 @@ nsHyperTextAccessible::GetSelectionDOMRa
   PRUint32 childCount = startNode->GetChildCount();
   nsCOMPtr<nsISelectionPrivate> privSel(do_QueryInterface(domSel));
   nsresult rv = privSel->
     GetRangesForIntervalArray(startNode, 0, startNode, childCount, true, aRanges);
   NS_ENSURE_SUCCESS(rv,);
 
   // Remove collapsed ranges
   PRUint32 numRanges = aRanges->Length();
-  for (PRUint32 count = 0; count < numRanges; count ++) {
-    bool isCollapsed = false;
-    (*aRanges)[count]->GetCollapsed(&isCollapsed);
-    if (isCollapsed) {
-      aRanges->RemoveElementAt(count);
+  for (PRUint32 idx = 0; idx < numRanges; idx ++) {
+    if ((*aRanges)[idx]->Collapsed()) {
+      aRanges->RemoveElementAt(idx);
       --numRanges;
-      --count;
+      --idx;
     }
   }
 }
 
 /*
  * Gets the number of selected regions.
  */
 NS_IMETHODIMP
@@ -1832,39 +1830,29 @@ nsHyperTextAccessible::GetSelectionBound
   GetSelectionDOMRanges(nsISelectionController::SELECTION_NORMAL, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (aSelectionNum < 0 || aSelectionNum >= rangeCount)
     return NS_ERROR_INVALID_ARG;
 
   nsRange* range = ranges[aSelectionNum];
 
-  // Get start point
-  nsCOMPtr<nsIDOMNode> startDOMNode;
-  range->GetStartContainer(getter_AddRefs(startDOMNode));
-  nsCOMPtr<nsINode> startNode(do_QueryInterface(startDOMNode));
-  PRInt32 startOffset = 0;
-  range->GetStartOffset(&startOffset);
+  // Get start and end points.
+  nsINode* startNode = range->GetStartParent();
+  nsINode* endNode = range->GetEndParent();
+  PRInt32 startOffset = range->StartOffset(), endOffset = range->EndOffset();
 
-  // Get end point
-  nsCOMPtr<nsIDOMNode> endDOMNode;
-  range->GetEndContainer(getter_AddRefs(endDOMNode));
-  nsCOMPtr<nsINode> endNode(do_QueryInterface(endDOMNode));
-  PRInt32 endOffset = 0;
-  range->GetEndOffset(&endOffset);
-
-  PRInt16 rangeCompareResult = 0;
-  nsresult rv = range->CompareBoundaryPoints(nsIDOMRange::START_TO_END, range,
-                                             &rangeCompareResult);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (rangeCompareResult < 0) {
-    // Make sure start is before end, by swapping offsets
-    // This occurs when the user selects backwards in the text
-    startNode.swap(endNode);
+  // Make sure start is before end, by swapping DOM points.  This occurs when
+  // the user selects backwards in the text.
+  PRInt32 rangeCompare = nsContentUtils::ComparePoints(endNode, endOffset,
+                                                       startNode, startOffset);
+  if (rangeCompare < 0) {
+    nsINode* tempNode = startNode;
+    startNode = endNode;
+    endNode = tempNode;
     PRInt32 tempOffset = startOffset;
     startOffset = endOffset;
     endOffset = tempOffset;
   }
 
   nsAccessible *startAccessible =
     DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
   if (!startAccessible) {
@@ -2319,78 +2307,64 @@ nsHyperTextAccessible::GetDOMPointByFram
 
 // nsHyperTextAccessible
 nsresult
 nsHyperTextAccessible::RangeBoundToHypertextOffset(nsRange *aRange,
                                                    bool aIsStartBound,
                                                    bool aIsStartHTOffset,
                                                    PRInt32 *aHTOffset)
 {
-  nsCOMPtr<nsIDOMNode> DOMNode;
+  nsINode* node = nsnull;
   PRInt32 nodeOffset = 0;
 
-  nsresult rv;
   if (aIsStartBound) {
-    rv = aRange->GetStartContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetStartOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetStartParent();
+    nodeOffset = aRange->StartOffset();
   } else {
-    rv = aRange->GetEndContainer(getter_AddRefs(DOMNode));
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = aRange->GetEndOffset(&nodeOffset);
-    NS_ENSURE_SUCCESS(rv, rv);
+    node = aRange->GetEndParent();
+    nodeOffset = aRange->EndOffset();
   }
 
-  nsCOMPtr<nsINode> node(do_QueryInterface(DOMNode));
   nsAccessible *startAcc =
     DOMPointToHypertextOffset(node, nodeOffset, aHTOffset);
 
   if (aIsStartHTOffset && !startAcc)
     *aHTOffset = 0;
 
   return NS_OK;
 }
 
 // nsHyperTextAccessible
 nsresult
-nsHyperTextAccessible::GetSpellTextAttribute(nsIDOMNode *aNode,
+nsHyperTextAccessible::GetSpellTextAttribute(nsINode* aNode,
                                              PRInt32 aNodeOffset,
                                              PRInt32 *aHTStartOffset,
                                              PRInt32 *aHTEndOffset,
                                              nsIPersistentProperties *aAttributes)
 {
   nsTArray<nsRange*> ranges;
   GetSelectionDOMRanges(nsISelectionController::SELECTION_SPELLCHECK, &ranges);
 
   PRUint32 rangeCount = ranges.Length();
   if (!rangeCount)
     return NS_OK;
 
+  nsCOMPtr<nsIDOMNode> DOMNode = do_QueryInterface(aNode);
   for (PRUint32 index = 0; index < rangeCount; index++) {
     nsRange* range = ranges[index];
 
     PRInt16 result;
-    nsresult rv = range->ComparePoint(aNode, aNodeOffset, &result);
+    nsresult rv = range->ComparePoint(DOMNode, aNodeOffset, &result);
     NS_ENSURE_SUCCESS(rv, rv);
     // ComparePoint checks boundary points, but we need to check that
     // text at aNodeOffset is inside the range.
     // See also bug 460690.
     if (result == 0) {
-      nsCOMPtr<nsIDOMNode> end;
-      rv = range->GetEndContainer(getter_AddRefs(end));
-      NS_ENSURE_SUCCESS(rv, rv);
-      PRInt32 endOffset;
-      rv = range->GetEndOffset(&endOffset);
-      NS_ENSURE_SUCCESS(rv, rv);
-      if (aNode == end && aNodeOffset == endOffset) {
+      if (aNode == range->GetEndParent() && aNodeOffset == range->EndOffset())
         result = 1;
-      }
     }
 
     if (result == 1) { // range is before point
       PRInt32 startHTOffset = 0;
       nsresult rv = RangeBoundToHypertextOffset(range, false, true,
                                                 &startHTOffset);
       NS_ENSURE_SUCCESS(rv, rv);
 
--- a/accessible/src/html/nsHyperTextAccessible.h
+++ b/accessible/src/html/nsHyperTextAccessible.h
@@ -259,16 +259,24 @@ public:
    *
    * @param  aOffset  [in] the given text offset
    */
   nsAccessible* GetChildAtOffset(PRUint32 aOffset)
   {
     return GetChildAt(GetChildIndexAtOffset(aOffset));
   }
 
+  //////////////////////////////////////////////////////////////////////////////
+  // EditableTextAccessible
+
+  /**
+   * Return the editor associated with the accessible.
+   */
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
+
 protected:
   // nsHyperTextAccessible
 
   /**
    * Transform magic offset into text offset.
    */
   inline PRInt32 ConvertMagicOffset(PRInt32 aOffset)
   {
@@ -404,17 +412,17 @@ protected:
    *
    * @param aIncludeDefAttrs  [in] points whether text attributes having default
    *                          values of attributes should be included
    * @param aSourceNode       [in] the node we start to traverse from
    * @param aStartOffset      [in, out] the start offset
    * @param aEndOffset        [in, out] the end offset
    * @param aAttributes       [out, optional] result attributes
    */
-  nsresult GetSpellTextAttribute(nsIDOMNode *aNode, PRInt32 aNodeOffset,
+  nsresult GetSpellTextAttribute(nsINode* aNode, PRInt32 aNodeOffset,
                                  PRInt32 *aStartOffset,
                                  PRInt32 *aEndOffset,
                                  nsIPersistentProperties *aAttributes);
 
 private:
   /**
    * End text offsets array.
    */
--- a/accessible/src/xforms/nsXFormsAccessible.cpp
+++ b/accessible/src/xforms/nsXFormsAccessible.cpp
@@ -268,34 +268,36 @@ nsXFormsEditableAccessible::NativeState(
     bool isRelevant = false;
     rv = sXFormsService->IsRelevant(DOMNode, &isRelevant);
     NS_ENSURE_SUCCESS(rv, state);
     if (isRelevant) {
       state |= states::EDITABLE | states::SELECTABLE_TEXT;
     }
   }
 
-  nsCOMPtr<nsIEditor> editor;
-  GetAssociatedEditor(getter_AddRefs(editor));
+  nsCOMPtr<nsIEditor> editor = GetEditor();
   NS_ENSURE_TRUE(editor, state);
   PRUint32 flags;
   editor->GetFlags(&flags);
   if (flags & nsIPlaintextEditor::eEditorSingleLineMask)
     state |= states::SINGLE_LINE;
   else
     state |= states::MULTI_LINE;
 
   return state;
 }
 
-NS_IMETHODIMP
-nsXFormsEditableAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsXFormsEditableAccessible::GetEditor() const
 {
   nsCOMPtr<nsIDOMNode> DOMNode(do_QueryInterface(mContent));
-  return sXFormsService->GetEditor(DOMNode, aEditor);
+
+  nsCOMPtr<nsIEditor> editor;
+  sXFormsService->GetEditor(DOMNode, getter_AddRefs(editor));
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXFormsSelectableAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 nsXFormsSelectableAccessible::
   nsXFormsSelectableAccessible(nsIContent* aContent, nsDocAccessible* aDoc) :
--- a/accessible/src/xforms/nsXFormsAccessible.h
+++ b/accessible/src/xforms/nsXFormsAccessible.h
@@ -139,18 +139,18 @@ public:
  * The class is base for accessible objects for XForms elements that have
  * editable area.
  */
 class nsXFormsEditableAccessible : public nsXFormsAccessible
 {
 public:
   nsXFormsEditableAccessible(nsIContent* aContent, nsDocAccessible* aDoc);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual PRUint64 NativeState();
 };
 
 
 /**
  * The class is base for accessible objects for XForms select and XForms
--- a/accessible/src/xul/nsXULFormControlAccessible.cpp
+++ b/accessible/src/xul/nsXULFormControlAccessible.cpp
@@ -845,24 +845,27 @@ NS_IMETHODIMP nsXULTextFieldAccessible::
 }
 
 bool
 nsXULTextFieldAccessible::CanHaveAnonChildren()
 {
   return false;
 }
 
-NS_IMETHODIMP nsXULTextFieldAccessible::GetAssociatedEditor(nsIEditor **aEditor)
+already_AddRefed<nsIEditor>
+nsXULTextFieldAccessible::GetEditor() const
 {
-  *aEditor = nsnull;
-
   nsCOMPtr<nsIContent> inputField = GetInputField();
   nsCOMPtr<nsIDOMNSEditableElement> editableElt(do_QueryInterface(inputField));
-  NS_ENSURE_TRUE(editableElt, NS_ERROR_FAILURE);
-  return editableElt->GetEditor(aEditor);
+  if (!editableElt)
+    return nsnull;
+
+  nsCOMPtr<nsIEditor> editor;
+  editableElt->GetEditor(getter_AddRefs(editor));
+  return editor.forget();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsXULTextFieldAccessible: nsAccessible protected
 
 void
 nsXULTextFieldAccessible::CacheChildren()
 {
--- a/accessible/src/xul/nsXULFormControlAccessible.h
+++ b/accessible/src/xul/nsXULFormControlAccessible.h
@@ -255,18 +255,18 @@ public:
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // nsIAccessible
   NS_IMETHOD GetValue(nsAString& aValue);
   NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
   NS_IMETHOD DoAction(PRUint8 index);
 
-  // nsIAccessibleEditableText
-  NS_IMETHOD GetAssociatedEditor(nsIEditor **aEditor);
+  // nsHyperTextAccessible
+  virtual already_AddRefed<nsIEditor> GetEditor() const;
 
   // nsAccessible
   virtual void ApplyARIAState(PRUint64* aState);
   virtual mozilla::a11y::role NativeRole();
   virtual PRUint64 NativeState();
   virtual bool CanHaveAnonChildren();
 
   // ActionAccessible
--- a/accessible/tests/mochitest/common.js
+++ b/accessible/tests/mochitest/common.js
@@ -608,14 +608,14 @@ function getNodePrettyName(aNode)
   } catch (e) {
     return "' no node info '";
   }
 }
 
 function getObjAddress(aObj)
 {
   var exp = /native\s*@\s*(0x[a-f0-9]+)/g;
-  var match = exp.exec(aObj.valueOf());
+  var match = exp.exec(aObj.toString());
   if (match)
     return match[1];
 
-  return aObj.valueOf();
+  return aObj.toString();
 }
--- a/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
+++ b/accessible/tests/mochitest/elm/test_nsApplicationAcc.html
@@ -10,51 +10,56 @@
   <script type="application/javascript" 
           src="../common.js"></script>
   <script type="application/javascript" 
           src="../role.js"></script>
 
   <script type="application/javascript">
     function doTest()
     {
-        var accessible = getApplicationAccessible();
-        if (!accessible) {
-          SimpleTest.finish();
-          return;
-        }
+      var accessible = getApplicationAccessible();
+      if (!accessible) {
+        SimpleTest.finish();
+        return;
+      }
+
+      var bundleServ =
+        Components.classes["@mozilla.org/intl/stringbundle;1"].
+        getService(Components.interfaces.nsIStringBundleService);
+      var brandBundle =
+        bundleServ.createBundle("chrome://branding/locale/brand.properties");
 
-        // nsIAccessible::name
-        var bundleServ = Components.classes["@mozilla.org/intl/stringbundle;1"]
-                         .getService(Components.interfaces.nsIStringBundleService);
-        var bundle = bundleServ.createBundle("chrome://branding/locale/brand.properties");
+      var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
+        getService(Components.interfaces.nsIXULAppInfo);
 
-        var applicationName = "";
-
+      // nsIAccessible::name
+      var applicationName = "";
+      if (LINUX || SOLARIS) {
+        applicationName = appInfo.name;
+      } else {
         try {
-            applicationName = bundle.GetStringFromName("brandShortName");
-        }  catch(e) {
+          applicationName = brandBundle.GetStringFromName("brandShortName");
+        } catch(e) {
         }
 
         if (applicationName == "")
-            applicationName = "Gecko based application";
-
-        is (accessible.name, applicationName, "wrong application accessible name");
-
-        // nsIAccessibleApplication
-        var appInfo = Components.classes["@mozilla.org/xre/app-info;1"].
-          getService(Components.interfaces.nsIXULAppInfo);
+          applicationName = "Gecko based application";
+      }
+      is (accessible.name, applicationName, "wrong application accessible name");
 
-        is(accessible.appName, appInfo.name, "Wrong application name");
-        is(accessible.appVersion, appInfo.version, "Wrong application version");
-        is(accessible.platformName, "Gecko", "Wrong platform name");
-        is(accessible.platformVersion, appInfo.platformVersion,
-           "Wrong platform version");
+      // nsIAccessibleApplication
+      is(accessible.appName, appInfo.name, "Wrong application name");
+      is(accessible.appVersion, appInfo.version, "Wrong application version");
+      is(accessible.platformName, "Gecko", "Wrong platform name");
+      is(accessible.platformVersion, appInfo.platformVersion,
+         "Wrong platform version");
 
-        SimpleTest.finish();
+      SimpleTest.finish();
     }
+
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
   </head>
   <body>
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=456121"
     title="nsApplicationAccessible::GetName does not return a default value when brand.properties does not exist">
--- a/accessible/tests/mochitest/events.js
+++ b/accessible/tests/mochitest/events.js
@@ -299,22 +299,30 @@ function eventQueue(aEventType)
         SimpleTest.finish();
 
       return;
     }
 
     // Start processing of next invoker.
     invoker = this.getNextInvoker();
 
+    this.setEventHandler(invoker);
+
     if (gLogger.isEnabled()) {
       gLogger.logToConsole("Event queue: \n  invoke: " + invoker.getID());
       gLogger.logToDOM("EQ: invoke: " + invoker.getID(), true);
     }
 
-    this.setEventHandler(invoker);
+    var infoText = "Invoke the '" + invoker.getID() + "' test { ";
+    for (var idx = 0; idx < this.mEventSeq.length; idx++) {
+      infoText += this.isEventUnexpected(idx) ? "un" : "";
+      infoText += "expected '" + this.getEventTypeAsString(idx) + "' event; ";
+    }
+    infoText += " }";
+    info(infoText);
 
     if (invoker.invoke() == INVOKER_ACTION_FAILED) {
       // Invoker failed to prepare action, fail and finish tests.
       this.processNextInvoker();
       return;
     }
 
     if (this.areAllEventsUnexpected())
--- a/aclocal.m4
+++ b/aclocal.m4
@@ -13,16 +13,17 @@ builtin(include, build/autoconf/altoptio
 builtin(include, build/autoconf/mozprog.m4)dnl
 builtin(include, build/autoconf/mozheader.m4)dnl
 builtin(include, build/autoconf/mozcommonheader.m4)dnl
 builtin(include, build/autoconf/acwinpaths.m4)dnl
 builtin(include, build/autoconf/lto.m4)dnl
 builtin(include, build/autoconf/gcc-pr49911.m4)dnl
 builtin(include, build/autoconf/frameptr.m4)dnl
 builtin(include, build/autoconf/compiler-opts.m4)dnl
+builtin(include, build/autoconf/expandlibs.m4)dnl
 
 MOZ_PROG_CHECKMSYS()
 
 # Read the user's .mozconfig script.  We can't do this in
 # configure.in: autoconf puts the argument parsing code above anything
 # expanded from configure.in, and we need to get the configure options
 # from .mozconfig in place before that argument parsing code.
 MOZ_READ_MOZCONFIG(.)
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -41,17 +41,17 @@ pref("toolkit.defaultChromeURI", "chrome
 pref("browser.chromeURL", "chrome://browser/content/");
 #ifdef MOZ_OFFICIAL_BRANDING
 pref("browser.homescreenURL", "file:///system/home/homescreen.html");
 #else
 pref("browser.homescreenURL", "file:///data/local/homescreen.html,file:///system/home/homescreen.html");
 #endif
 
 // URL for the dialer application.
-pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html");
+pref("dom.telephony.app.phone.url", "http://localhost:7777/data/local/apps/dialer/dialer.html http://localhost:7777/data/local/apps/homescreen/homescreen.html http://localhost:7777/apps/dialer/dialer.html http://localhost:7777/apps/homescreen/homescreen.html");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
 /* disable text selection */
 pref("browser.ignoreNativeFrameTextSelection", true);
 
 /* cache prefs */
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -61,17 +61,17 @@ pref("extensions.strictCompatibility", f
 // for it to be compatible by default.
 pref("extensions.minCompatibleAppVersion", "4.0");
 
 // Preferences for AMO integration
 pref("extensions.getAddons.cache.enabled", true);
 pref("extensions.getAddons.maxResults", 15);
 pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%");
 pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
-pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%");
+pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
 pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=firefox");
 pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%");
 
 // Blocklist preferences
 pref("extensions.blocklist.enabled", true);
 pref("extensions.blocklist.interval", 86400);
 // Controls what level the blocklist switches from warning about items to forcibly
 // blocking them.
@@ -209,16 +209,17 @@ pref("app.update.service.enabled", true)
 // Symmetric (can be overridden by individual extensions) update preferences.
 // e.g.
 //  extensions.{GUID}.update.enabled
 //  extensions.{GUID}.update.url
 //  .. etc ..
 //
 pref("extensions.update.enabled", true);
 pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
 pref("extensions.update.interval", 86400);  // Check for updates to Extensions and 
                                             // Themes every day
 // Non-symmetric (not shared by extensions) extension-specific [update] preferences
 pref("extensions.getMoreThemesURL", "https://addons.mozilla.org/%LOCALE%/firefox/getpersonas");
 pref("extensions.dss.enabled", false);          // Dynamic Skin Switching                                               
 pref("extensions.dss.switchPending", false);    // Non-dynamic switch pending after next
                                                 // restart.
 
@@ -1036,16 +1037,19 @@ pref("devtools.inspector.sidebarOpen", f
 pref("devtools.inspector.activeSidebar", "ruleview");
 
 // Enable the Debugger
 pref("devtools.debugger.enabled", false);
 
 // The default Debugger UI height
 pref("devtools.debugger.ui.height", 250);
 
+// Disable remote debugging protocol logging
+pref("devtools.debugger.log", false);
+
 // Enable the style inspector
 pref("devtools.styleinspector.enabled", true);
 
 // Enable the Tilt inspector
 pref("devtools.tilt.enabled", true);
 pref("devtools.tilt.intro_transition", true);
 pref("devtools.tilt.outro_transition", true);
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1558,59 +1558,16 @@ function delayedStartup(isLoadingBlank, 
   gBrowser.tabContainer.updateVisibility();
 
   gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
 
   var homeButton = document.getElementById("home-button");
   gHomeButton.updateTooltip(homeButton);
   gHomeButton.updatePersonalToolbarStyle(homeButton);
 
-#ifdef HAVE_SHELL_SERVICE
-  // Perform default browser checking (after window opens).
-  var shell = getShellService();
-  if (shell) {
-#ifdef DEBUG
-    var shouldCheck = false;
-#else
-    var shouldCheck = shell.shouldCheckDefaultBrowser;
-#endif
-    var willRecoverSession = false;
-    try {
-      var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
-               getService(Ci.nsISessionStartup);
-      willRecoverSession =
-        (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
-    }
-    catch (ex) { /* never mind; suppose SessionStore is broken */ }
-    if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
-      // Delay the set-default-browser prompt so it doesn't block
-      // initialisation of the session store service.
-      setTimeout(function () {
-        var brandBundle = document.getElementById("bundle_brand");
-        var shellBundle = document.getElementById("bundle_shell");
-
-        var brandShortName = brandBundle.getString("brandShortName");
-        var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
-        var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
-                                                           [brandShortName]);
-        var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
-                                                           [brandShortName]);
-        var checkEveryTime = { value: shouldCheck };
-        var ps = Services.prompt;
-        var rv = ps.confirmEx(window, promptTitle, promptMessage,
-                              ps.STD_YES_NO_BUTTONS,
-                              null, null, null, checkboxLabel, checkEveryTime);
-        if (rv == 0)
-          shell.setDefaultBrowser(true, false);
-        shell.shouldCheckDefaultBrowser = checkEveryTime.value;
-      }, 0);
-    }
-  }
-#endif
-
   // BiDi UI
   gBidiUI = isBidiEnabled();
   if (gBidiUI) {
     document.getElementById("documentDirection-separator").hidden = false;
     document.getElementById("documentDirection-swap").hidden = false;
     document.getElementById("textfieldDirection-separator").hidden = false;
     document.getElementById("textfieldDirection-swap").hidden = false;
   }
@@ -5369,29 +5326,33 @@ function setToolbarVisibility(toolbar, i
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
 }
 
 var TabsOnTop = {
   init: function TabsOnTop_init() {
+    this._initialized = true;
     this.syncUI();
     Services.prefs.addObserver(this._prefName, this, false);
   },
 
   uninit: function TabsOnTop_uninit() {
     Services.prefs.removeObserver(this._prefName, this);
   },
 
   toggle: function () {
     this.enabled = !Services.prefs.getBoolPref(this._prefName);
   },
 
   syncUI: function () {
+    if (!this._initialized)
+      return;
+
     let userEnabled = Services.prefs.getBoolPref(this._prefName);
     let enabled = userEnabled && gBrowser.tabContainer.visible;
 
     document.getElementById("cmd_ToggleTabsOnTop")
             .setAttribute("checked", userEnabled);
 
     document.documentElement.setAttribute("tabsontop", enabled);
     document.getElementById("navigator-toolbox").setAttribute("tabsontop", enabled);
--- a/browser/base/content/newtab/dropTargetShim.js
+++ b/browser/base/content/newtab/dropTargetShim.js
@@ -35,20 +35,22 @@ let gDropTargetShim = {
     node.addEventListener("dragend", this._end.bind(this), true);
   },
 
   /**
    * Handles the 'dragstart' event.
    * @param aEvent The 'dragstart' event.
    */
   _start: function DropTargetShim_start(aEvent) {
-    gGrid.lock();
+    if (aEvent.target.classList.contains("site")) {
+      gGrid.lock();
 
-    // XXX bug 505521 - Listen for dragover on the document.
-    document.documentElement.addEventListener("dragover", this._dragover, false);
+      // XXX bug 505521 - Listen for dragover on the document.
+      document.documentElement.addEventListener("dragover", this._dragover, false);
+    }
   },
 
   /**
    * Handles the 'drag' event and determines the current drop target.
    * @param aEvent The 'drag' event.
    */
   _drag: function DropTargetShim_drag(aEvent) {
     // Let's see if we find a drop target.
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -845,17 +845,17 @@ nsContextMenu.prototype = {
   saveVideoFrameAsImage: function () {
     urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal,
                      Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
     let name = "";
     try {
       let uri = makeURI(this.mediaURL);
       let url = uri.QueryInterface(Ci.nsIURL);
       if (url.fileBaseName)
-        name = url.fileBaseName + ".jpg";
+        name = decodeURI(url.fileBaseName) + ".jpg";
     } catch (e) { }
     if (!name)
       name = "snapshot.jpg";
     var video = this.target;
     var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
     canvas.width = video.videoWidth;
     canvas.height = video.videoHeight;
     var ctxDraw = canvas.getContext("2d");
--- a/browser/base/content/test/newtab/Makefile.in
+++ b/browser/base/content/test/newtab/Makefile.in
@@ -15,14 +15,16 @@ include $(topsrcdir)/config/rules.mk
 	browser_newtab_block.js \
 	browser_newtab_disable.js \
 	browser_newtab_drag_drop.js \
 	browser_newtab_drop_preview.js \
 	browser_newtab_private_browsing.js \
 	browser_newtab_reset.js \
 	browser_newtab_tabsync.js \
 	browser_newtab_unpin.js \
+	browser_newtab_bug722273.js \
 	browser_newtab_bug723102.js \
+	browser_newtab_bug723121.js \
 	head.js \
 	$(NULL)
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug722273.js
@@ -0,0 +1,62 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const NOW = Date.now() * 1000;
+const URL = "http://fake-site.com/";
+
+let tmp = {};
+Cu.import("resource:///modules/NewTabUtils.jsm", tmp);
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+  .getService(Ci.mozIJSSubScriptLoader)
+  .loadSubScript("chrome://browser/content/sanitize.js", tmp);
+
+let {NewTabUtils, Sanitizer} = tmp;
+
+let bhist = Cc["@mozilla.org/browser/global-history;2"]
+  .getService(Ci.nsIBrowserHistory);
+
+function runTests() {
+  clearHistory();
+  fillHistory();
+  yield addNewTabPageTab();
+
+  is(cells[0].site.url, URL, "first site is our fake site");
+
+  let page = {
+    update: function () {
+      executeSoon(TestRunner.next);
+    },
+
+    observe: function () {}
+  };
+
+  NewTabUtils.allPages.register(page);
+  yield clearHistory();
+
+  NewTabUtils.allPages.unregister(page);
+  ok(!cells[0].site, "the fake site is gone");
+}
+
+function fillHistory() {
+  let uri = makeURI(URL);
+  for (let i = 59; i > 0; i--)
+    bhist.addPageWithDetails(uri, "fake site", NOW - i * 60 * 1000000);
+}
+
+function clearHistory() {
+  let s = new Sanitizer();
+  s.prefDomain = "privacy.cpd.";
+
+  let prefs = gPrefService.getBranch(s.prefDomain);
+  prefs.setBoolPref("history", true);
+  prefs.setBoolPref("downloads", false);
+  prefs.setBoolPref("cache", false);
+  prefs.setBoolPref("cookies", false);
+  prefs.setBoolPref("formdata", false);
+  prefs.setBoolPref("offlineApps", false);
+  prefs.setBoolPref("passwords", false);
+  prefs.setBoolPref("sessions", false);
+  prefs.setBoolPref("siteSettings", false);
+
+  s.sanitize();
+}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_bug723121.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+function runTests() {
+  setLinks("0,1,2,3,4,5,6,7,8");
+  setPinnedLinks("");
+
+  yield addNewTabPageTab();
+  checkGridLocked(false, "grid is unlocked");
+
+  let cell = cells[0].node;
+  let site = cells[0].site.node;
+
+  sendDragEvent(site, "dragstart");
+  checkGridLocked(true, "grid is now locked");
+
+  sendDragEvent(site, "dragend");
+  checkGridLocked(false, "grid isn't locked anymore");
+
+  sendDragEvent(cell, "dragstart");
+  checkGridLocked(false, "grid isn't locked - dragstart was ignored");
+}
+
+function checkGridLocked(aLocked, aMessage) {
+  is(cw.gGrid.node.hasAttribute("locked"), aLocked, aMessage);
+}
+
+function sendDragEvent(aNode, aType) {
+  let ifaceReq = cw.QueryInterface(Ci.nsIInterfaceRequestor);
+  let windowUtils = ifaceReq.getInterface(Ci.nsIDOMWindowUtils);
+
+  let dataTransfer = {
+    mozUserCancelled: false,
+    setData: function () null,
+    setDragImage: function () null,
+    getData: function () "about:blank"
+  };
+
+  let event = cw.document.createEvent("DragEvents");
+  event.initDragEvent(aType, true, true, cw, 0, 0, 0, 0, 0,
+                      false, false, false, false, 0, null, dataTransfer);
+
+  windowUtils.dispatchDOMEventViaPresShell(aNode, event, true);
+}
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -124,20 +124,21 @@ function addNewTabPageTab() {
 
   // Wait for the new tab page to be loaded.
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
 
     cw = browser.contentWindow;
 
     if (NewTabUtils.allPages.enabled) {
-      cells = cw.gGrid.cells;
-
       // Continue when the link cache has been populated.
-      NewTabUtils.links.populateCache(TestRunner.next);
+      NewTabUtils.links.populateCache(function () {
+        cells = cw.gGrid.cells;
+        executeSoon(TestRunner.next);
+      });
     } else {
       TestRunner.next();
     }
 
   }, true);
 }
 
 /**
@@ -241,16 +242,18 @@ function unpinCell(aCell) {
 
 /**
  * Simulates a drop and drop operation.
  * @param aDropTarget the cell that is the drop target
  * @param aDragSource the cell that contains the dragged site (optional)
  */
 function simulateDrop(aDropTarget, aDragSource) {
   let event = {
+    clientX: 0,
+    clientY: 0,
     dataTransfer: {
       mozUserCancelled: false,
       setData: function () null,
       setDragImage: function () null,
       getData: function () "about:blank#99\nblank"
     }
   };
 
--- a/browser/components/certerror/content/aboutCertError.css
+++ b/browser/components/certerror/content/aboutCertError.css
@@ -42,25 +42,11 @@
 /* Logical CSS rules belong here, but presentation & theming rules
    should live in the CSS of the appropriate theme */
 
 #technicalContentText {
   overflow: auto;
   white-space: pre-wrap;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  cursor: pointer;
-  -moz-padding-start: 20px;
-  position: relative;
-  left: -20px;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  left: auto;
-  right: -20px;
-}
-
-div[collapsed] > p,
-div[collapsed] > div {
+.expander[collapsed] + * {
   display: none;
 }
--- a/browser/components/certerror/content/aboutCertError.xhtml
+++ b/browser/components/certerror/content/aboutCertError.xhtml
@@ -255,28 +255,28 @@
           <div id="whatShouldIDoContentText">
             <p>&certerror.whatShouldIDo.content;</p>
             <button id='getMeOutOfHereButton'>&certerror.getMeOutOfHere.label;</button>
           </div>
         </div>
         
         <!-- The following sections can be unhidden by default by setting the
              "browser.xul.error_pages.expert_bad_cert" pref to true -->
-        <div id="technicalContent" collapsed="true">
-          <h2 onclick="toggle('technicalContent');" id="technicalContentHeading">&certerror.technical.heading;</h2>
-          <p id="technicalContentText"/>
-        </div>
+        <h2 id="technicalContent" class="expander" collapsed="true">
+          <button onclick="toggle('technicalContent');">&certerror.technical.heading;</button>
+        </h2>
+        <p id="technicalContentText"/>
         
-        <div id="expertContent" collapsed="true">
-          <h2 onclick="toggle('expertContent');" id="expertContentHeading">&certerror.expert.heading;</h2>
-          <div>
-            <p>&certerror.expert.content;</p>
-            <p>&certerror.expert.contentPara2;</p>
-            <button id='exceptionDialogButton'>&certerror.addException.label;</button>
-          </div>
+        <h2 id="expertContent" class="expander" collapsed="true">
+          <button onclick="toggle('expertContent');">&certerror.expert.heading;</button>
+        </h2>
+        <div>
+          <p>&certerror.expert.content;</p>
+          <p>&certerror.expert.contentPara2;</p>
+          <button id='exceptionDialogButton'>&certerror.addException.label;</button>
         </div>
       </div>
     </div>
 
     <!--
     - Note: It is important to run the script this way, instead of using
     - an onload handler. This is because error pages are loaded as
     - LOAD_BACKGROUND, which means that onload handlers will not be executed.
--- a/browser/components/dirprovider/DirectoryProvider.cpp
+++ b/browser/components/dirprovider/DirectoryProvider.cpp
@@ -51,16 +51,17 @@
 #include "nsCategoryManagerUtils.h"
 #include "nsComponentManagerUtils.h"
 #include "nsCOMArray.h"
 #include "nsDirectoryServiceUtils.h"
 #include "mozilla/ModuleUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsStringAPI.h"
 #include "nsXULAppAPI.h"
+#include "nsIPrefLocalizedString.h"
 
 namespace mozilla {
 namespace browser {
 
 NS_IMPL_ISUPPORTS2(DirectoryProvider,
                    nsIDirectoryServiceProvider,
                    nsIDirectoryServiceProvider2)
 
@@ -195,17 +196,28 @@ AppendDistroSearchDirs(nsIProperties* aD
     nsCOMPtr<nsIFile> localePlugins;
     rv = searchPlugins->Clone(getter_AddRefs(localePlugins));
     if (NS_FAILED(rv))
       return;
 
     localePlugins->AppendNative(NS_LITERAL_CSTRING("locale"));
 
     nsCString locale;
-    rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale));
+    nsCOMPtr<nsIPrefLocalizedString> prefString;
+    rv = prefs->GetComplexValue("general.useragent.locale",
+                                NS_GET_IID(nsIPrefLocalizedString),
+                                getter_AddRefs(prefString));
+    if (NS_SUCCEEDED(rv)) {
+      nsAutoString wLocale;
+      prefString->GetData(getter_Copies(wLocale));
+      CopyUTF16toUTF8(wLocale, locale);
+    } else {
+      rv = prefs->GetCharPref("general.useragent.locale", getter_Copies(locale));
+    }
+
     if (NS_SUCCEEDED(rv)) {
 
       nsCOMPtr<nsIFile> curLocalePlugins;
       rv = localePlugins->Clone(getter_AddRefs(curLocalePlugins));
       if (NS_SUCCEEDED(rv)) {
 
         curLocalePlugins->AppendNative(locale);
         rv = curLocalePlugins->Exists(&exists);
--- a/browser/components/migration/Makefile.in
+++ b/browser/components/migration/Makefile.in
@@ -38,16 +38,18 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 DIRS		= public src
 
+TEST_DIRS += tests
+
 include $(topsrcdir)/config/rules.mk
 
 # Needed for preprocessor removal of IE Profile Migrator label - bug 236901
 ifeq ($(OS_ARCH),WINNT)
 ifdef GNU_CXX
 DEFINES += -DNO_IE_MIGRATOR=1
 endif
 endif
--- a/browser/components/migration/content/migration.js
+++ b/browser/components/migration/content/migration.js
@@ -60,16 +60,17 @@ var MigrationWizard = {
     os.addObserver(this, "Migration:Ended", false);
 
     this._wiz = document.documentElement;
 
     if ("arguments" in window && window.arguments.length > 1) {
       this._source = window.arguments[0];
       this._migrator = window.arguments[1].QueryInterface(kIMig);
       this._autoMigrate = window.arguments[2].QueryInterface(kIPStartup);
+      this._skipImportSourcePage = window.arguments[3];
 
       if (this._autoMigrate) {
         // Show the "nothing" option in the automigrate case to provide an
         // easily identifiable way to avoid migration and create a new profile.
         var nothing = document.getElementById("nothing");
         nothing.hidden = false;
       }
     }
@@ -89,17 +90,17 @@ var MigrationWizard = {
   },
 
   // 1 - Import Source
   onImportSourcePageShow: function ()
   {
     // Reference to the "From File" radio button 
     var fromfile = null;
 
-    //XXXquark This function is called before init, so check for bookmarks here
+    // init is not called when openDialog opens the wizard, so check for bookmarks here.
     if ("arguments" in window && window.arguments[0] == "bookmarks") {
       this._bookmarks = true;
 
       fromfile = document.getElementById("fromfile");
       fromfile.hidden = false;
 
       var importBookmarks = document.getElementById("importBookmarks");
       importBookmarks.hidden = false;
@@ -146,16 +147,22 @@ var MigrationWizard = {
       // We didn't find a migrator, notify the user
       document.getElementById("noSources").hidden = false;
 
       this._wiz.canAdvance = false;
 
       document.getElementById("importBookmarks").hidden = true;
       document.getElementById("importAll").hidden = true;
     }
+
+    // Advance to the next page if the caller told us to.
+    if (this._migrator && this._skipImportSourcePage) {
+      this._wiz.advance();
+      this._wiz.canRewind = false;
+    }
   },
   
   onImportSourcePageAdvanced: function ()
   {
     var newSource = document.getElementById("importSourceGroup").selectedItem.id;
     
     if (newSource == "nothing" || newSource == "fromfile") {
       if(newSource == "fromfile")
--- a/browser/components/migration/src/FirefoxProfileMigrator.js
+++ b/browser/components/migration/src/FirefoxProfileMigrator.js
@@ -222,16 +222,22 @@ FirefoxProfileMigrator.prototype = {
    *          profile directory path to migrate from
    */
   migrate : function Firefox_migrate(aItems, aStartup, aProfile)
   {
     if (aStartup) {
       this._replaceBookmarks = true;
     }
 
+    // Ensure that aProfile is not the current profile.
+    if (this._paths.currentProfile.path === this._sourceProfile.path) {
+      throw new Exception("Source and destination profiles are the same");
+      return;
+    }
+
     Services.obs.notifyObservers(null, "Migration:Started", null);
 
     // Reset pending count.  If this count becomes 0, "Migration:Ended"
     // notification is sent
     this._pendingCount = 1;
 
     if (aItems & MIGRATOR.HISTORY)
       this._migrateHistory();
@@ -273,16 +279,21 @@ FirefoxProfileMigrator.prototype = {
    * @todo    Bug 715315 - make sure source databases are not in-use
    */
   getMigrateData: function Firefox_getMigrateData(aProfile, aDoingStartup)
   {
     this._sourceProfile = Cc[LOCAL_FILE_CID].createInstance(Ci.nsILocalFile);
     this._sourceProfile.initWithPath(aProfile);
 
     let result = 0;
+
+    // Ensure that aProfile is not the current profile.
+    if (this._paths.currentProfile.path === this._sourceProfile.path)
+      return result;
+
     if (!this._sourceProfile.exists() || !this._sourceProfile.isReadable()) {
       Cu.reportError("source profile directory doesn't exist or is not readable");
       return result;
     }
 
     // Migration initiated from the UI is not supported.
     if (!aDoingStartup)
       return result;
--- a/browser/components/migration/src/ProfileMigrator.js
+++ b/browser/components/migration/src/ProfileMigrator.js
@@ -11,47 +11,67 @@ const Cu = Components.utils;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 
 function ProfileMigrator() {
 }
 
 ProfileMigrator.prototype = {
-  migrate: function PM_migrate(aStartup) {
+  migrate: function PM_migrate(aStartup, aKey) {
     // By opening the wizard with a supplied migrator, it will automatically
     // migrate from it.
-    let [key, migrator] = this._getDefaultMigrator();
+    let key = null, migrator = null;
+    let skipImportSourcePage = Cc["@mozilla.org/supports-PRBool;1"]
+                                 .createInstance(Ci.nsISupportsPRBool);
+    if (aKey) {
+      key = aKey;
+      migrator = this._getMigratorIfSourceExists(key);
+      if (!migrator) {
+        Cu.reportError("Invalid migrator key specified or source does not exist.");
+        return;
+      }
+      // If the migrator was passed to us from the caller, use that migrator
+      // and skip the import source page.
+      skipImportSourcePage.data = true;
+    } else {
+      [key, migrator] = this._getDefaultMigrator();
+    }
     if (!key)
         return;
 
     let params = Cc["@mozilla.org/array;1"].createInstance(Ci.nsIMutableArray);
     params.appendElement(this._toCString(key), false);
     params.appendElement(migrator, false);
     params.appendElement(aStartup, false);
+    params.appendElement(skipImportSourcePage, false);
 
     Services.ww.openWindow(null,
                            "chrome://browser/content/migration/migration.xul",
                            "_blank",
                            "chrome,dialog,modal,centerscreen,titlebar",
                            params);
   },
 
   _toCString: function PM__toCString(aStr) {
     let cstr = Cc["@mozilla.org/supports-cstring;1"].
                createInstance(Ci.nsISupportsCString);
     cstr.data = aStr;
     return cstr;
   },
 
   _getMigratorIfSourceExists: function PM__getMigratorIfSourceExists(aKey) {
-    let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
-    let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
-    if (migrator.sourceExists)
-      return migrator;
+    try {
+      let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
+      let migrator = Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
+      if (migrator.sourceExists)
+        return migrator;
+    } catch (ex) {
+      Cu.reportError("Could not get migrator: " + ex);
+    }
     return null;
   },
 
   // We don't yet support checking for the default browser on all platforms,
   // needless to say we don't have migrators for all browsers.  Thus, for each
   // platform, there's a fallback list of migrators used in these cases.
   _PLATFORM_FALLBACK_LIST:
 #ifdef XP_WIN
--- a/browser/components/migration/src/nsIEProfileMigrator.cpp
+++ b/browser/components/migration/src/nsIEProfileMigrator.cpp
@@ -1407,28 +1407,36 @@ nsIEProfileMigrator::CopyFavoritesBatche
     // Initialize the default bookmarks
     nsCOMPtr<nsIFile> profile;
     GetProfilePath(nsnull, profile);
     rv = InitializeBookmarks(profile);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // 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 &&
         NS_SUCCEEDED(regKey->Open(nsIWindowsRegKey::ROOT_KEY_CURRENT_USER,
                                   REGISTRY_IE_TOOLBAR_KEY,
                                   nsIWindowsRegKey::ACCESS_READ))) {
       nsAutoString linksFolderName;
       if (NS_SUCCEEDED(regKey->ReadStringValue(
                          NS_LITERAL_STRING("LinksFolderName"),
-                         linksFolderName)))
+                         linksFolderName))) {
         personalToolbarFolderName = linksFolderName;
+      }
+      else {
+        personalToolbarFolderName.AssignLiteral("Links");
+      }
     }
+
     folder = bookmarksMenuFolderId;
   }
 
   nsCOMPtr<nsIProperties> fileLocator =
     do_GetService("@mozilla.org/file/directory_service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIFile> favoritesDirectory;
   (void)fileLocator->Get(NS_WIN_FAVORITES_DIR, NS_GET_IID(nsIFile),
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/Makefile.in
@@ -0,0 +1,15 @@
+# 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@
+relativesrcdir = browser/components/migration/tests
+
+include $(DEPTH)/config/autoconf.mk
+
+XPCSHELL_TESTS = unit
+
+include $(topsrcdir)/config/rules.mk
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/bookmarks.html
@@ -0,0 +1,16 @@
+<!DOCTYPE NETSCAPE-Bookmark-file-1>
+<!-- This is an automatically generated file.
+     It will be read and overwritten.
+     DO NOT EDIT! -->
+<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8">
+<TITLE>Bookmarks</TITLE>
+<H1>Bookmarks Menu</H1>
+
+<DL><p>
+    <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+    <DT><H3 ADD_DATE="1233157910" LAST_MODIFIED="1233157972" PERSONAL_TOOLBAR_FOLDER="true">Bookmarks Toolbar</H3>
+<DD>Add bookmarks to this folder to see them displayed on the Bookmarks Toolbar
+    <DL><p>
+        <DT><A HREF="http://example.com/" ADD_DATE="1233157972" LAST_MODIFIED="1233157984">example</A>
+    </DL><p>
+</DL><p>
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/head_migration.js
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+
+const IMIGRATOR = Ci.nsIBrowserProfileMigrator;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+                                  "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
+                                  "resource://gre/modules/FileUtils.jsm");
+
+// Initialize profile.
+let gProfD = do_get_profile();
+
+function newMigratorFor(aKey) {
+  let cid = "@mozilla.org/profile/migrator;1?app=browser&type=" + aKey;
+  return Cc[cid].createInstance(Ci.nsIBrowserProfileMigrator);
+}
+
+let (bookmarkshtml = do_get_file("bookmarks.html")) {
+  bookmarkshtml.copyTo(gProfD, "bookmarks.html");
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/test_IE_bookmarks.js
@@ -0,0 +1,28 @@
+/* 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/. */
+
+function run_test() {
+  let migrator = newMigratorFor("ie");
+
+  // Sanity check for the source.
+  do_check_true(migrator.sourceExists);
+
+  // Ensure bookmarks migration is available.
+  let availableSources = migrator.getMigrateData("FieldOfFlowers", false);
+  do_check_true((availableSources & IMIGRATOR.BOOKMARKS) > 0);
+
+  // Needed to enforce bookmarks replacing.
+  let startup = {
+    doStartup: function () {},
+    get directory() do_get_profile()
+  }
+  migrator.migrate(IMIGRATOR.BOOKMARKS, startup, "FieldOfFlowers");
+
+  // Check that at least two bookmark have been added to the menu and the
+  // toolbar.  The first one comes from bookmarks.html, the others from IE.
+  do_check_true(PlacesUtils.bookmarks
+                           .getIdForItemAt(PlacesUtils.bookmarksMenuFolderId, 1) > 0);
+  do_check_true(PlacesUtils.bookmarks
+                           .getIdForItemAt(PlacesUtils.toolbarFolderId, 1) > 0);
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/migration/tests/unit/xpcshell.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+head = head_migration.js
+tail =
+
+[test_IE_bookmarks.js]
+skip-if = os != "win"
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -406,31 +406,76 @@ BrowserGlue.prototype = {
 
     // If there are plugins installed that are outdated, and the user hasn't
     // been warned about them yet, open the plugins update page.
     if (Services.prefs.getBoolPref(PREF_PLUGINS_NOTIFYUSER))
       this._showPluginUpdatePage();
 
     // For any add-ons that were installed disabled and can be enabled offer
     // them to the user
-    var win = this.getMostRecentBrowserWindow();
-    var browser = win.gBrowser;
     var changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED);
-    AddonManager.getAddonsByIDs(changedIDs, function(aAddons) {
-      aAddons.forEach(function(aAddon) {
-        // If the add-on isn't user disabled or can't be enabled then skip it
-        if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
-          return;
+    if (changedIDs.length > 0) {
+      AddonManager.getAddonsByIDs(changedIDs, function(aAddons) {
+        var win = this.getMostRecentBrowserWindow();
+        var browser = win.gBrowser;
+        aAddons.forEach(function(aAddon) {
+          // If the add-on isn't user disabled or can't be enabled then skip it.
+          if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
+            return;
 
-        browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
-      })
-    });
+          browser.selectedTab = browser.addTab("about:newaddon?id=" + aAddon.id);
+        })
+      });
+    }
 
     let keywordURLUserSet = Services.prefs.prefHasUserValue("keyword.URL");
     Services.telemetry.getHistogramById("FX_KEYWORD_URL_USERSET").add(keywordURLUserSet);
+
+    // Perform default browser checking.
+    var shell;
+    try {
+      shell = Components.classes["@mozilla.org/browser/shell-service;1"]
+        .getService(Components.interfaces.nsIShellService);
+    } catch (e) { }
+    if (shell) {
+#ifdef DEBUG
+      var shouldCheck = false;
+#else
+      var shouldCheck = shell.shouldCheckDefaultBrowser;
+#endif
+      var willRecoverSession = false;
+      try {
+        var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
+                 getService(Ci.nsISessionStartup);
+        willRecoverSession =
+          (ss.sessionType == Ci.nsISessionStartup.RECOVER_SESSION);
+      }
+      catch (ex) { /* never mind; suppose SessionStore is broken */ }
+      if (shouldCheck && !shell.isDefaultBrowser(true) && !willRecoverSession) {
+        Services.tm.mainThread.dispatch(function() {
+          var brandBundle = win.document.getElementById("bundle_brand");
+          var shellBundle = win.document.getElementById("bundle_shell");
+  
+          var brandShortName = brandBundle.getString("brandShortName");
+          var promptTitle = shellBundle.getString("setDefaultBrowserTitle");
+          var promptMessage = shellBundle.getFormattedString("setDefaultBrowserMessage",
+                                                             [brandShortName]);
+          var checkboxLabel = shellBundle.getFormattedString("setDefaultBrowserDontAsk",
+                                                             [brandShortName]);
+          var checkEveryTime = { value: shouldCheck };
+          var ps = Services.prompt;
+          var rv = ps.confirmEx(win, promptTitle, promptMessage,
+                                ps.STD_YES_NO_BUTTONS,
+                                null, null, null, checkboxLabel, checkEveryTime);
+          if (rv == 0)
+            shell.setDefaultBrowser(true, false);
+          shell.shouldCheckDefaultBrowser = checkEveryTime.value;
+        }, Ci.nsIThread.DISPATCH_NORMAL);
+      }
+    }
   },
 
   _onQuitRequest: function BG__onQuitRequest(aCancelQuit, aQuitType) {
     // If user has already dismissed quit request, then do nothing
     if ((aCancelQuit instanceof Ci.nsISupportsPRBool) && aCancelQuit.data)
       return;
 
     // There are several cases where we won't show a dialog here:
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -162,120 +162,87 @@ PlacesViewBase.prototype = {
 
   destroyContextMenu: function PVB_destroyContextMenu(aPopup) {
     this._contextMenuShown = false;
     if (window.content)
       window.content.focus();
   },
 
   _cleanPopup: function PVB_cleanPopup(aPopup) {
-    // Remove places popup children and update markers to keep track of
-    // their indices.
-    let start = aPopup._startMarker != -1 ? aPopup._startMarker + 1 : 0;
-    let end = aPopup._endMarker != -1 ? aPopup._endMarker :
-                                        aPopup.childNodes.length;
-    let items = [];
-
-    // Automatically adjust the start and the end markers.
-    let firstNonStaticNodeFound = false;
-    for (let i = start; i < end; ++i) {
-      let item = aPopup.childNodes[i];
-      if (item.getAttribute("builder") == "end") {
-        // we need to do this for menus that have static content at the end but
-        // are initially empty, eg. the history menu, we need to know where to
-        // start inserting new items.
-        aPopup._endMarker = i;
-        break;
-      }
-
-      if (item._placesNode) {
-        items.push(item);
-        firstNonStaticNodeFound = true;
-      }
-      else {
-        // This is static content.
-        if (!firstNonStaticNodeFound) {
-          // We are at the beginning of the popup, in static content.
-          // The markers are initialized in menu.xml, in the base binding.
-          aPopup._startMarker++;
-        }
-        else {
-          // We are at the end of the popup, after places nodes
-          aPopup._endMarker = i;
-          break;
-        }
-      }
-    }
-
-    for (let i = 0; i < items.length; ++i) {
-      aPopup.removeChild(items[i]);
-      if (aPopup._endMarker != -1)
-        aPopup._endMarker--;
+    // Remove Places nodes from the popup.
+    let child = aPopup._startMarker;
+    while (child.nextSibling != aPopup._endMarker) {
+      if (child.nextSibling._placesNode)
+        aPopup.removeChild(child.nextSibling);
+      else
+        child = child.nextSibling;
     }
   },
 
   _rebuildPopup: function PVB__rebuildPopup(aPopup) {
-    this._cleanPopup(aPopup);
-
     let resultNode = aPopup._placesNode;
     if (!resultNode.containerOpen)
       return;
 
     if (resultNode._feedURI) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem) {
-        aPopup._emptyMenuItem.hidden = true;
-      }
+      this._setEmptyPopupStatus(aPopup, false);
       aPopup._built = true;
       this._populateLivemarkPopup(aPopup);
       return;
     }
 
+    this._cleanPopup(aPopup);
+
     let cc = resultNode.childCount;
     if (cc > 0) {
-      aPopup.removeAttribute("emptyplacesresult");
-      if (aPopup._emptyMenuItem)
-        aPopup._emptyMenuItem.hidden = true;
+      this._setEmptyPopupStatus(aPopup, false);
 
       for (let i = 0; i < cc; ++i) {
         let child = resultNode.getChild(i);
         this._insertNewItemToPopup(child, aPopup, null);
       }
     }
     else {
-      aPopup.setAttribute("emptyplacesresult", "true");
-      // This menu is empty.  If there is no static content, add
-      // an element to show it is empty.
-      if (aPopup._startMarker == -1 && aPopup._endMarker == -1)
-        this._showEmptyMenuItem(aPopup);
+      this._setEmptyPopupStatus(aPopup, true);
     }
     aPopup._built = true;
   },
 
   _removeChild: function PVB__removeChild(aChild) {
     // If document.popupNode pointed to this child, null it out,
     // otherwise controller's command-updating may rely on the removed
     // item still being "selected".
     if (document.popupNode == aChild)
       document.popupNode = null;
 
     aChild.parentNode.removeChild(aChild);
   },
 
-  _showEmptyMenuItem: function PVB__showEmptyMenuItem(aPopup) {
-    if (aPopup._emptyMenuItem) {
-      aPopup._emptyMenuItem.hidden = false;
-      return;
+  _setEmptyPopupStatus:
+  function PVB__setEmptyPopupStatus(aPopup, aEmpty) {
+    if (!aPopup._emptyMenuitem) {
+      let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
+      aPopup._emptyMenuitem = document.createElement("menuitem");
+      aPopup._emptyMenuitem.setAttribute("label", label);
+      aPopup._emptyMenuitem.setAttribute("disabled", true);
     }
 
-    let label = PlacesUIUtils.getString("bookmarksMenuEmptyFolder");
-    aPopup._emptyMenuItem = document.createElement("menuitem");
-    aPopup._emptyMenuItem.setAttribute("label", label);
-    aPopup._emptyMenuItem.setAttribute("disabled", true);
-    aPopup.appendChild(aPopup._emptyMenuItem);
+    if (aEmpty) {
+      aPopup.setAttribute("emptyplacesresult", "true");
+      // Don't add the menuitem if there is static content.
+      if (!aPopup._startMarker.previousSibling &&
+          !aPopup._endMarker.nextSibling)
+        aPopup.insertBefore(aPopup._emptyMenuitem, aPopup._endMarker);
+    }
+    else {
+      aPopup.removeAttribute("emptyplacesresult");
+      try {
+        aPopup.removeChild(aPopup._emptyMenuitem);
+      } catch (ex) {}
+    }
   },
 
   _createMenuItemForPlacesNode:
   function PVB__createMenuItemForPlacesNode(aPlacesNode) {
     delete aPlacesNode._DOMElement;
     let element;
     let type = aPlacesNode.type;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
@@ -303,33 +270,38 @@ PlacesViewBase.prototype = {
             element.setAttribute("hostContainer", "true");
         }
         else if (itemId != -1) {
           PlacesUtils.livemarks.getLivemark(
             { id: itemId },
             function (aStatus, aLivemark) {
               if (Components.isSuccessCode(aStatus)) {
                 element.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+                // OS X native menubar doesn't track list-style-images since
+                // it doesn't have a frame (bug 733415).  Thus enforce updating.
+                element.setAttribute("image", "");
+                element.removeAttribute("image");
+#endif
                 // Set an expando on the node, controller will use it to build
                 // its metadata.
                 aPlacesNode._feedURI = aLivemark.feedURI;
                 aPlacesNode._siteURI = aLivemark.siteURI;
               }
             }
           );
         }
 
         let popup = document.createElement("menupopup");
         popup._placesNode = PlacesUtils.asContainer(aPlacesNode);
-        if (this._nativeView) {
-          popup._startMarker = -1;
-          popup._endMarker = -1;
+
+        if (!this._nativeView) {
+          popup.setAttribute("placespopup", "true");
         }
-        else
-          popup.setAttribute("placespopup", "true");
+
 #ifdef XP_MACOSX
         // No context menu on mac.
         popup.setAttribute("context", "placesContext");
 #endif
         element.appendChild(popup);
         element.className = "menu-iconic bookmark-item";
 
         aPlacesNode._DOMElement = popup;
@@ -349,50 +321,30 @@ PlacesViewBase.prototype = {
       aPlacesNode._DOMElement = element;
 
     return element;
   },
 
   _insertNewItemToPopup:
   function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) {
     let element = this._createMenuItemForPlacesNode(aNewChild);
-
-    if (aBefore) {
-      aPopup.insertBefore(element, aBefore);
-    }
-    else {
-      // Add the new element to the menu.  If there is static content at
-      // the end of the menu, add the element before that.  Otherwise,
-      // just add to the end.
-      if (aPopup._endMarker != -1) {
-        let lastElt = aPopup.childNodes[aPopup._endMarker];
-        aPopup.insertBefore(element, lastElt);
-      }
-      else {
-        aPopup.appendChild(element);
-      }
-    }
-
-    if (aPopup._endMarker != -1)
-      aPopup._endMarker++;
-
+    let before = aBefore || aPopup._endMarker;
+    aPopup.insertBefore(element, before);
     return element;
   },
 
   _setLivemarkSiteURIMenuItem:
   function PVB__setLivemarkSiteURIMenuItem(aPopup) {
     let siteUrl = aPopup._placesNode._siteURI ? aPopup._placesNode._siteURI.spec
                                               : null;
     if (!siteUrl && aPopup._siteURIMenuitem) {
       aPopup.removeChild(aPopup._siteURIMenuitem);
       aPopup._siteURIMenuitem = null;
-      aPopup._startMarker--;
       aPopup.removeChild(aPopup._siteURIMenuseparator);
       aPopup._siteURIMenuseparator = null;
-      aPopup._startMarker--;
     }
     else if (siteUrl && !aPopup._siteURIMenuitem) {
       // Add "Open (Feed Name)" menuitem.
       aPopup._siteURIMenuitem = document.createElement("menuitem");
       aPopup._siteURIMenuitem.className = "openlivemarksite-menuitem";
       aPopup._siteURIMenuitem.setAttribute("targetURI", siteUrl);
       aPopup._siteURIMenuitem.setAttribute("oncommand",
         "openUILink(this.getAttribute('targetURI'), event);");
@@ -402,65 +354,58 @@ PlacesViewBase.prototype = {
       // Note: stopPropagation is needed to avoid serving middle-click
       // with BT_onClick that would open all items in tabs.
       aPopup._siteURIMenuitem.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       let label =
         PlacesUIUtils.getFormattedString("menuOpenLivemarkOrigin.label",
                                          [aPopup.parentNode.getAttribute("label")])
       aPopup._siteURIMenuitem.setAttribute("label", label);
-      aPopup.insertBefore(aPopup._siteURIMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuitem, aPopup._startMarker);
 
       aPopup._siteURIMenuseparator = document.createElement("menuseparator");
-      aPopup.insertBefore(aPopup._siteURIMenuseparator,
-                         aPopup.childNodes.item(aPopup._startMarker + 1));
-      aPopup._startMarker++;
+      aPopup.insertBefore(aPopup._siteURIMenuseparator, aPopup._startMarker);
     }
   },
 
   /**
    * Add, update or remove the livemark status menuitem.
    * @param aPopup
    *        The livemark container popup
    * @param aStatus
    *        The livemark status
    */
   _setLivemarkStatusMenuItem:
   function PVB_setLivemarkStatusMenuItem(aPopup, aStatus) {
-    let itemId = aPopup._placesNode.itemId;
     let statusMenuitem = aPopup._statusMenuitem;
     let stringId = "";
     if (aStatus == Ci.mozILivemark.STATUS_LOADING)
       stringId = "bookmarksLivemarkLoading";
     else if (aStatus == Ci.mozILivemark.STATUS_FAILED)
       stringId = "bookmarksLivemarkFailed";
 
     if (stringId && !statusMenuitem) {
       // Create the status menuitem and cache it in the popup object.
       statusMenuitem = document.createElement("menuitem");
       statusMenuitem.setAttribute("livemarkStatus", stringId);
+      statusMenuitem.className = "livemarkstatus-menuitem";
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
       statusMenuitem.setAttribute("disabled", true);
-      aPopup.insertBefore(statusMenuitem,
-                          aPopup.childNodes.item(aPopup._startMarker + 1));
+      aPopup.insertBefore(statusMenuitem, aPopup._startMarker.nextSibling);
       aPopup._statusMenuitem = statusMenuitem;
-      aPopup._startMarker++;
     }
     else if (stringId &&
              statusMenuitem.getAttribute("livemarkStatus") != stringId) {
       // Status has changed, update the cached status menuitem.
       statusMenuitem.setAttribute("label", PlacesUIUtils.getString(stringId));
     }
     else if (!stringId && statusMenuitem) {
       // The livemark has finished loading.
       aPopup.removeChild(aPopup._statusMenuitem);
       aPopup._statusMenuitem = null;
-      aPopup._startMarker--;
     }
   },
 
   toggleCutNode: function PVB_toggleCutNode(aNode, aValue) {
     let elt = aNode._DOMElement;
     if (elt) {
       // We may get the popup for menus, but we need the menu itself.
       if (elt.localName == "menupopup")
@@ -512,16 +457,22 @@ PlacesViewBase.prototype = {
       throw "aPlacesNode must have _DOMElement set";
 
     // All livemarks have a feedURI, so use it as our indicator of a livemark
     // being modified.
     if (aAnno == PlacesUtils.LMANNO_FEEDURI) {
       let menu = elt.parentNode;
       if (!menu.hasAttribute("livemark")) {
         menu.setAttribute("livemark", "true");
+#ifdef XP_MACOSX
+        // OS X native menubar doesn't track list-style-images since
+        // it doesn't have a frame (bug 733415).  Thus enforce updating.
+        menu.setAttribute("image", "");
+        menu.removeAttribute("image");
+#endif
       }
 
       PlacesUtils.livemarks.getLivemark(
         { id: aPlacesNode.itemId },
         (function (aStatus, aLivemark) {
           if (Components.isSuccessCode(aStatus)) {
             // Set an expando on the node, controller will use it to build
             // its metadata.
@@ -575,23 +526,18 @@ PlacesViewBase.prototype = {
       elt = elt.parentNode;
 
     if (parentElt._built) {
       parentElt.removeChild(elt);
 
       // Figure out if we need to show the "<Empty>" menu-item.
       // TODO Bug 517701: This doesn't seem to handle the case of an empty
       // root.
-      if (!parentElt.hasChildNodes() ||
-          (parentElt.childNodes.length == 1 &&
-          parentElt.firstChild == parentElt._emptyMenuItem))
-        this._showEmptyMenuItem(parentElt);
-
-      if (parentElt._endMarker != -1)
-        parentElt._endMarker--;
+      if (parentElt._startMarker.nextSibling == parentElt._endMarker)
+        this._setEmptyPopupStatus(parentElt, true);
     }
   },
 
   nodeReplaced:
   function PVB_nodeReplaced(aParentPlacesNode, aOldPlacesNode, aNewPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
@@ -615,18 +561,19 @@ PlacesViewBase.prototype = {
     }
   },
 
   nodeHistoryDetailsChanged:
   function PVB_nodeHistoryDetailsChanged(aPlacesNode, aTime, aCount) {
     if (aPlacesNode.parent && aPlacesNode.parent._feedURI) {
       // Find the node in the parent.
       let popup = aPlacesNode.parent._DOMElement;
-      for (let i = popup._startMarker; i < popup.childNodes.length; i++) {
-        let child = popup.childNodes[i];
+      for (let child = popup._startMarker.nextSibling;
+           child != popup._endMarker;
+           child = child.nextSibling) {
         if (child._placesNode && child._placesNode.uri == aPlacesNode.uri) {
           if (aCount)
             child.setAttribute("visited", "true");
           else
             child.removeAttribute("visited");
           break;
         }
       }
@@ -644,21 +591,21 @@ PlacesViewBase.prototype = {
   function PVB_nodeInserted(aParentPlacesNode, aPlacesNode, aIndex) {
     let parentElt = aParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aParentPlacesNode node must have _DOMElement set";
 
     if (!parentElt._built)
       return;
 
-    let index = parentElt._startMarker + 1 + aIndex;
+    let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                aIndex + 1;
     this._insertNewItemToPopup(aPlacesNode, parentElt,
                                parentElt.childNodes[index]);
-    if (parentElt._emptyMenuItem)
-      parentElt._emptyMenuItem.hidden = true;
+    this._setEmptyPopupStatus(parentElt, false);
   },
 
   nodeMoved:
   function PBV_nodeMoved(aPlacesNode,
                          aOldParentPlacesNode, aOldIndex,
                          aNewParentPlacesNode, aNewIndex) {
     // Note: the current implementation of moveItem does not actually
     // use this notification when the item in question is moved from one
@@ -679,17 +626,18 @@ PlacesViewBase.prototype = {
 
     let parentElt = aNewParentPlacesNode._DOMElement;
     if (!parentElt)
       throw "aNewParentPlacesNode node must have _DOMElement set";
 
     if (parentElt._built) {
       // Move the node.
       parentElt.removeChild(elt);
-      let index = parentElt._startMarker + 1 + aNewIndex;
+      let index = Array.indexOf(parentElt.childNodes, parentElt._startMarker) +
+                  aNewIndex + 1;
       parentElt.insertBefore(elt, parentElt.childNodes[index]);
     }
   },
 
   containerStateChanged:
   function PVB_containerStateChanged(aPlacesNode, aOldState, aNewState) {
     if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED ||
         aNewState == Ci.nsINavHistoryContainerResultNode.STATE_CLOSED) {
@@ -704,17 +652,19 @@ PlacesViewBase.prototype = {
         PlacesUtils.livemarks.getLivemark({ id: aPlacesNode.itemId },
           (function (aStatus, aLivemark) {
             if (Components.isSuccessCode(aStatus)) {
               let shouldInvalidate = !aPlacesNode._feedURI;
               aPlacesNode._feedURI = aLivemark.feedURI;
               aPlacesNode._siteURI = aLivemark.siteURI;
               if (aNewState == Ci.nsINavHistoryContainerResultNode.STATE_OPENED) {
                 aLivemark.registerForUpdates(aPlacesNode, this);
+                // Prioritize the current livemark.
                 aLivemark.reload();
+                PlacesUtils.livemarks.reloadLivemarks();
                 if (shouldInvalidate)
                   this.invalidateContainer(aPlacesNode);
               }
               else {
                 aLivemark.unregisterForUpdates(aPlacesNode);
               }
             }
           }).bind(this)
@@ -816,48 +766,83 @@ PlacesViewBase.prototype = {
       hasMultipleURIs = numURINodes > 1;
     }
 
     if (!hasMultipleURIs) {
       // We don't have to show any option.
       if (aPopup._endOptOpenAllInTabs) {
         aPopup.removeChild(aPopup._endOptOpenAllInTabs);
         aPopup._endOptOpenAllInTabs = null;
-        aPopup._endMarker--;
 
         aPopup.removeChild(aPopup._endOptSeparator);
         aPopup._endOptSeparator = null;
-        aPopup._endMarker--;
       }
     }
     else if (!aPopup._endOptOpenAllInTabs) {
       // Create a separator before options.
       aPopup._endOptSeparator = document.createElement("menuseparator");
       aPopup._endOptSeparator.className = "bookmarks-actions-menuseparator";
       aPopup.appendChild(aPopup._endOptSeparator);
-      aPopup._endMarker++;
 
       // Add the "Open All in Tabs" menuitem.
       aPopup._endOptOpenAllInTabs = document.createElement("menuitem");
       aPopup._endOptOpenAllInTabs.className = "openintabs-menuitem";
       aPopup._endOptOpenAllInTabs.setAttribute("oncommand",
         "PlacesUIUtils.openContainerNodeInTabs(this.parentNode._placesNode, event, " +
                                                "PlacesUIUtils.getViewForNode(this));");
       aPopup._endOptOpenAllInTabs.setAttribute("onclick",
         "checkForMiddleClick(this, event); event.stopPropagation();");
       aPopup._endOptOpenAllInTabs.setAttribute("label",
         gNavigatorBundle.getString("menuOpenAllInTabs.label"));
       aPopup.appendChild(aPopup._endOptOpenAllInTabs);
-      aPopup._endMarker++;
+    }
+  },
+
+  _ensureMarkers: function PVB__ensureMarkers(aPopup) {
+    if (aPopup._startMarker)
+      return;
+
+    // _startMarker is an hidden menuseparator that lives before places nodes.
+    aPopup._startMarker = document.createElement("menuseparator");
+    aPopup._startMarker.hidden = true;
+    aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
+
+    // _endMarker is an hidden menuseparator that lives after places nodes.
+    aPopup._endMarker = document.createElement("menuseparator");
+    aPopup._endMarker.hidden = true;
+    aPopup.appendChild(aPopup._endMarker);
+
+    // Move the markers to the right position.
+    let firstNonStaticNodeFound = false;
+    for (let i = 0; i < aPopup.childNodes.length; i++) {
+      let child = aPopup.childNodes[i];
+      // Menus that have static content at the end, but are initially empty,
+      // use a special "builder" attribute to figure out where to start
+      // inserting places nodes.
+      if (child.getAttribute("builder") == "end") {
+        aPopup.insertBefore(aPopup._endMarker, child);
+        break;
+      }
+
+      if (child._placesNode && !firstNonStaticNodeFound) {
+        firstNonStaticNodeFound = true;
+        aPopup.insertBefore(aPopup._startMarker, child);
+      }
+    }
+    if (!firstNonStaticNodeFound) {
+      aPopup.insertBefore(aPopup._startMarker, aPopup._endMarker);
     }
   },
 
   _onPopupShowing: function PVB__onPopupShowing(aEvent) {
     // Avoid handling popupshowing of inner views.
     let popup = aEvent.originalTarget;
+
+    this._ensureMarkers(popup);
+
     if (popup._placesNode && PlacesUIUtils.getViewForNode(popup) == this) {
       if (!popup._placesNode.containerOpen)
         popup._placesNode.containerOpen = true;
       if (!popup._built)
         this._rebuildPopup(popup);
 
       this._mayAddCommandsItems(popup);
     }
@@ -912,21 +897,16 @@ function PlacesToolbar(aPlace) {
   Services.telemetry.getHistogramById("FX_BOOKMARKS_TOOLBAR_INIT_MS")
                     .add(Date.now() - startTime);
 }
 
 PlacesToolbar.prototype = {
   __proto__: PlacesViewBase.prototype,
 
   _cbEvents: ["dragstart", "dragover", "dragexit", "dragend", "drop",
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-              "mousedown", "mouseup",
-#endif
-#endif
               "mousemove", "mouseover", "mouseout"],
 
   QueryInterface: function PT_QueryInterface(aIID) {
     if (aIID.equals(Ci.nsIDOMEventListener) ||
         aIID.equals(Ci.nsITimerCallback))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
@@ -1113,26 +1093,16 @@ PlacesToolbar.prototype = {
         this._onMouseOver(aEvent);
         break;
       case "mousemove":
         this._onMouseMove(aEvent);
         break;
       case "mouseout":
         this._onMouseOut(aEvent);
         break;
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-      case "mouseup":
-        this._onMouseUp(aEvent);
-        break;
-      case "mousedown":
-        this._onMouseDown(aEvent);
-        break;
-#endif
-#endif
       case "popupshowing":
         this._onPopupShowing(aEvent);
         break;
       case "popuphidden":
         this._onPopupHidden(aEvent);
         break;
       default:
         throw "Trying to handle unexpected event.";
@@ -1550,24 +1520,16 @@ PlacesToolbar.prototype = {
     let draggedElt = aEvent.target;
     if (draggedElt.parentNode != this._rootElt || !draggedElt._placesNode)
       return;
 
     if (draggedElt.localName == "toolbarbutton" &&
         draggedElt.getAttribute("type") == "menu") {
       // If the drag gesture on a container is toward down we open instead
       // of dragging.
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-      if (this._mouseDownTimer) {
-        this._mouseDownTimer.cancel();
-        this._mouseDownTimer = null;
-      }
-#endif
-#endif
       let translateY = this._cachedMouseMoveEvent.clientY - aEvent.clientY;
       let translateX = this._cachedMouseMoveEvent.clientX - aEvent.clientX;
       if ((translateY) >= Math.abs(translateX/2)) {
         // Don't start the drag.
         aEvent.preventDefault();
         // Open the menu.
         draggedElt.open = true;
         return;
@@ -1730,57 +1692,16 @@ PlacesToolbar.prototype = {
       // Clear the dragover attribute if present, if we are dragging into a
       // folder in the hierachy of current opened popup we don't clear
       // this attribute on clearOverFolder.  See Notify for closeTimer.
       if (parent.hasAttribute("dragover"))
         parent.removeAttribute("dragover");
     }
   },
 
-#ifdef XP_UNIX
-#ifndef XP_MACOSX
-  _onMouseDown: function PT__onMouseDown(aEvent) {
-    let target = aEvent.target;
-    if (aEvent.button == 0 &&
-        target.localName == "toolbarbutton" &&
-        target.getAttribute("type") == "menu") {
-      this._allowPopupShowing = false;
-      // On Linux we can open the popup only after a delay.
-      // Indeed as soon as the menupopup opens we are unable to start a
-      // drag aEvent.  See bug 500081 for details.
-      this._mouseDownTimer = Cc["@mozilla.org/timer;1"].
-                             createInstance(Ci.nsITimer);
-      let callback = {
-        _self: this,
-        _target: target,
-        notify: function(timer) {
-          this._target.open = true;
-          this._mouseDownTimer = null;
-        }
-      };
-
-      this._mouseDownTimer.initWithCallback(callback, 300,
-                                            Ci.nsITimer.TYPE_ONE_SHOT);
-    }
-  },
-
-  _onMouseUp: function PT__onMouseUp(aEvent) {
-    if (aEvent.button != 0)
-      return;
-
-    if (this._mouseDownTimer) {
-      // On a click (down/up), we should open the menu popup.
-      this._mouseDownTimer.cancel();
-      this._mouseDownTimer = null;
-      aEvent.target.open = true;
-    }
-  },
-#endif
-#endif
-
   _onMouseMove: function PT__onMouseMove(aEvent) {
     // Used in dragStart to prevent dragging folders when dragging down.
     this._cachedMouseMoveEvent = aEvent;
 
     if (this._openedMenuButton == null ||
         PlacesControllerDragHelper.getSession())
       return;
 
@@ -1803,18 +1724,16 @@ function PlacesMenu(aPopupShowingEvent, 
   this._viewElt = this._rootElt.parentNode;   // <menu>
   this._viewElt._placesView = this;
   this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
   this._addEventListeners(window, ["unload"], false);
 
 #ifdef XP_MACOSX
   if (this._viewElt.parentNode.localName == "menubar") {
     this._nativeView = true;
-    this._rootElt._startMarker = -1;
-    this._rootElt._endMarker = -1;
   }
 #endif
 
   PlacesViewBase.call(this, aPlace);
   this._onPopupShowing(aPopupShowingEvent);
 }
 
 PlacesMenu.prototype = {
@@ -1824,18 +1743,16 @@ PlacesMenu.prototype = {
     if (aIID.equals(Ci.nsIDOMEventListener))
       return this;
 
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
   },
 
   _removeChild: function PM_removeChild(aChild) {
     PlacesViewBase.prototype._removeChild.apply(this, arguments);
-    if (this._endMarker != -1)
-      this._endMarker--;
   },
 
   uninit: function PM_uninit() {
     this._removeEventListeners(this._rootElt, ["popupshowing", "popuphidden"],
                                true);
     this._removeEventListeners(window, ["unload"], false);
 
     PlacesViewBase.prototype.uninit.apply(this, arguments);
--- a/browser/components/places/content/editBookmarkOverlay.xul
+++ b/browser/components/places/content/editBookmarkOverlay.xul
@@ -85,29 +85,27 @@
         <row align="center" id="editBMPanel_feedLocationRow">
           <label value="&editBookmarkOverlay.feedLocation.label;"
                  class="editBMPanel_rowLabel"
                  accesskey="&editBookmarkOverlay.feedLocation.accesskey;"
                  control="editBMPanel_feedLocationField"
                  observes="paneElementsBroadcaster"/>
           <textbox id="editBMPanel_feedLocationField"
                    class="uri-element"
-                   onblur="gEditItemOverlay.onFeedLocationFieldBlur();"
                    observes="paneElementsBroadcaster"/>
         </row>
 
         <row align="center" id="editBMPanel_siteLocationRow">
           <label value="&editBookmarkOverlay.siteLocation.label;"
                  class="editBMPanel_rowLabel"
                  accesskey="&editBookmarkOverlay.siteLocation.accesskey;"
                  control="editBMPanel_siteLocationField"
                  observes="paneElementsBroadcaster"/>
           <textbox id="editBMPanel_siteLocationField"
                    class="uri-element"
-                   onblur="gEditItemOverlay.onSiteLocationFieldBlur();"
                    observes="paneElementsBroadcaster"/>
         </row>
 
         <row align="center" id="editBMPanel_folderRow">
           <label value="&editBookmarkOverlay.folder.label;"
                  class="editBMPanel_rowLabel"
                  control="editBMPanel_folderMenuList"
                  observes="paneElementsBroadcaster"/>
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -67,38 +67,33 @@
                                                 "menupopup-drop-indicator-bar");
       </field>
 
       <field name="_scrollBox">
         document.getAnonymousElementByAttribute(this, "class",
                                                 "popup-internal-box");
       </field>
 
-      <!-- markers for start and end of valid places items -->
-      <field name="_startMarker">-1</field>
-      <field name="_endMarker">-1</field>
-
       <!-- This is the view that manage the popup -->
       <field name="_rootView">PlacesUIUtils.getViewForNode(this);</field>
 
       <!-- Check if we should hide the drop indicator for the target -->
       <method name="_hideDropIndicator">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          var target = aEvent.target;
+          let target = aEvent.target;
 
-          // in some view we have _startMarker and _endMarker, we should not
-          // draw the drop indicator outside of them
-          var betweenMarkers = true;
-          if (this._startMarker != -1 &&
-              target.boxObject.y <= this.childNodes[this._startMarker].boxObject.y)
-            betweenMarkers = false;
-          if (this._endMarker != -1 &&
-              target.boxObject.y >= this.childNodes[this._endMarker].boxObject.y)
-            betweenMarkers = false;
+          // Don't draw the drop indicator outside of markers.
+          // The markers are hidden, since otherwise sometimes popups acquire
+          // scrollboxes on OS X, so we can't use them directly.
+          let firstChildTop = this._startMarker.nextSibling.boxObject.y;
+          let lastChildBottom = this._endMarker.previousSibling.boxObject.y +
+                                this._endMarker.previousSibling.boxObject.height;
+          let betweenMarkers = target.boxObject.y >= firstChildTop ||
+                               target.boxObject.y <= lastChildBottom;
 
           // Hide the dropmarker if current node is not a Places node.
           return !(target && target._placesNode && betweenMarkers);
         ]]></body>
       </method>
 
       <!-- This function returns information about where to drop when
            dragging over this popup insertion point -->
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -891,17 +891,19 @@ PlacesTreeView.prototype = {
 
       PlacesUtils.livemarks.getLivemark({ id: aNode.itemId },
         (function (aStatus, aLivemark) {
           if (Components.isSuccessCode(aStatus)) {
             let shouldInvalidate = !aNode._feedURI;
             aNode._feedURI = aLivemark.feedURI;
             if (aNewState == Components.interfaces.nsINavHistoryContainerResultNode.STATE_OPENED) {
               aLivemark.registerForUpdates(aNode, this);
+              // Prioritize the current livemark.
               aLivemark.reload();
+              PlacesUtils.livemarks.reloadLivemarks();
               if (shouldInvalidate)
                 this.invalidateContainer(aNode);
             }
             else {
               aLivemark.unregisterForUpdates(aNode);
             }
           }
         }).bind(this)
--- a/browser/devtools/debugger/DebuggerUI.jsm
+++ b/browser/devtools/debugger/DebuggerUI.jsm
@@ -19,16 +19,17 @@
  *   Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Dave Camp <dcamp@mozilla.com> (original author)
  *   Panos Astithas <past@mozilla.com>
  *   Victor Porof <vporof@mozilla.com>
+ *   Mihai Sucan <mihai.sucan@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -55,20 +56,48 @@ let EXPORTED_SYMBOLS = ["DebuggerUI"];
 
 /**
  * Creates a pane that will host the debugger UI.
  */
 function DebuggerPane(aTab) {
   this._tab = aTab;
   this._close = this.close.bind(this);
   this._debugTab = this.debugTab.bind(this);
+  this.breakpoints = {};
 }
 
 DebuggerPane.prototype = {
   /**
+   * Skip editor breakpoint change events.
+   *
+   * This property tells the source editor event handler to skip handling of
+   * the BREAKPOINT_CHANGE events. This is used when the debugger adds/removes
+   * breakpoints from the editor. Typically, the BREAKPOINT_CHANGE event handler
+   * adds/removes events from the debugger, but when breakpoints are added from
+   * the public debugger API, we need to do things in reverse.
+   *
+   * This implementation relies on the fact that the source editor fires the
+   * BREAKPOINT_CHANGE events synchronously.
+   *
+   * @private
+   * @type boolean
+   */
+  _skipEditorBreakpointChange: false,
+
+  /**
+   * The list of breakpoints in the debugger as tracked by the current
+   * DebuggerPane instance. This an object where the values are BreakpointActor
+   * objects received from the client, while the keys are actor names, for
+   * example "conn0.breakpoint3".
+   *
+   * @type object
+   */
+  breakpoints: null,
+
+  /**
    * Creates and initializes the widgets contained in the debugger UI.
    */
   create: function DP_create(gBrowser) {
     this._tab._scriptDebugger = this;
 
     this._nbox = gBrowser.getNotificationBox(this._tab.linkedBrowser);
     this._splitter = gBrowser.parentNode.ownerDocument.createElement("splitter");
     this._splitter.setAttribute("class", "hud-splitter");
@@ -82,44 +111,251 @@ DebuggerPane.prototype = {
 
     this.frame.addEventListener("DOMContentLoaded", function initPane(aEvent) {
       if (aEvent.target != self.frame.contentDocument) {
         return;
       }
       self.frame.removeEventListener("DOMContentLoaded", initPane, true);
       // Initialize the source editor.
       self.frame.contentWindow.editor = self.editor = new SourceEditor();
+      self.frame.contentWindow.updateEditorBreakpoints =
+        self._updateEditorBreakpoints.bind(self);
 
       let config = {
         mode: SourceEditor.MODES.JAVASCRIPT,
         showLineNumbers: true,
-        readOnly: true
+        readOnly: true,
+        showAnnotationRuler: true,
+        showOverviewRuler: true,
       };
 
       let editorPlaceholder = self.frame.contentDocument.getElementById("editor");
       self.editor.init(editorPlaceholder, config, self._onEditorLoad.bind(self));
     }, true);
     this.frame.addEventListener("DebuggerClose", this._close, true);
 
     this.frame.setAttribute("src", "chrome://browser/content/debugger.xul");
   },
 
   /**
    * The load event handler for the source editor. This method does post-load
    * editor initialization.
    */
   _onEditorLoad: function DP__onEditorLoad() {
+    this.editor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                 this._onEditorBreakpointChange.bind(this));
     // Connect to the debugger server.
     this.connect();
   },
 
   /**
+   * Event handler for breakpoint changes that happen in the editor. This
+   * function syncs the breakpoint changes in the editor to those in the
+   * debugger.
+   *
+   * @private
+   * @param object aEvent
+   *        The SourceEditor.EVENTS.BREAKPOINT_CHANGE event object.
+   */
+  _onEditorBreakpointChange: function DP__onEditorBreakpointChange(aEvent) {
+    if (this._skipEditorBreakpointChange) {
+      return;
+    }
+
+    aEvent.added.forEach(this._onEditorBreakpointAdd, this);
+    aEvent.removed.forEach(this._onEditorBreakpointRemove, this);
+  },
+
+  /**
+   * Retrieve the URL of the selected script in the debugger view.
+   *
+   * @private
+   * @return string
+   *         The URL of the selected script.
+   */
+  _selectedScript: function DP__selectedScript() {
+    return this.debuggerWindow ?
+           this.debuggerWindow.DebuggerView.Scripts.selected : null;
+  },
+
+  /**
+   * Event handler for new breakpoints that come from the editor.
+   *
+   * @private
+   * @param object aBreakpoint
+   *        The breakpoint object coming from the editor.
+   */
+  _onEditorBreakpointAdd: function DP__onEditorBreakpointAdd(aBreakpoint) {
+    let location = {
+      url: this._selectedScript(),
+      line: aBreakpoint.line + 1,
+    };
+
+    if (location.url) {
+      let callback = function (aClient, aError) {
+        if (aError) {
+          this._skipEditorBreakpointChange = true;
+          let result = this.editor.removeBreakpoint(aBreakpoint.line);
+          this._skipEditorBreakpointChange = false;
+        }
+      }.bind(this);
+      this.addBreakpoint(location, callback, true);
+    }
+  },
+
+  /**
+   * Event handler for breakpoints that are removed from the editor.
+   *
+   * @private
+   * @param object aBreakpoint
+   *        The breakpoint object that was removed from the editor.
+   */
+  _onEditorBreakpointRemove: function DP__onEditorBreakpointRemove(aBreakpoint) {
+    let url = this._selectedScript();
+    let line = aBreakpoint.line + 1;
+    if (!url) {
+      return;
+    }
+
+    let breakpoint = this.getBreakpoint(url, line);
+    if (breakpoint) {
+      this.removeBreakpoint(breakpoint, null, true);
+    }
+  },
+
+  /**
+   * Update the breakpoints in the editor view. This function takes the list of
+   * breakpoints in the debugger and adds them back into the editor view. This
+   * is invoked when the selected script is changed.
+   *
+   * @private
+   */
+  _updateEditorBreakpoints: function DP__updateEditorBreakpoints()
+  {
+    let url = this._selectedScript();
+    if (!url) {
+      return;
+    }
+
+    this._skipEditorBreakpointChange = true;
+    for each (let breakpoint in this.breakpoints) {
+      if (breakpoint.location.url == url) {
+        this.editor.addBreakpoint(breakpoint.location.line - 1);
+      }
+    }
+    this._skipEditorBreakpointChange = false;
+  },
+
+  /**
+   * Add a breakpoint.
+   *
+   * @param object aLocation
+   *        The location where you want the breakpoint. This object must have
+   *        two properties:
+   *          - url - the URL of the script.
+   *          - line - the line number (starting from 1).
+   * @param function [aCallback]
+   *        Optional function to invoke once the breakpoint is added. The
+   *        callback is invoked with two arguments:
+   *          - aBreakpointClient - the BreakpointActor client object, if the
+   *          breakpoint has been added successfully.
+   *          - aResponseError - if there was any error.
+   * @param boolean [aNoEditorUpdate=false]
+   *        Tells if you want to skip editor updates. Typically the editor is
+   *        updated to visually indicate that a breakpoint has been added.
+   */
+  addBreakpoint:
+  function DP_addBreakpoint(aLocation, aCallback, aNoEditorUpdate) {
+    let breakpoint = this.getBreakpoint(aLocation.url, aLocation.line);
+    if (breakpoint) {
+      aCallback && aCallback(breakpoint);
+      return;
+    }
+
+    this.activeThread.setBreakpoint(aLocation, function(aResponse, aBpClient) {
+      if (!aResponse.error) {
+        this.breakpoints[aBpClient.actor] = aBpClient;
+
+        if (!aNoEditorUpdate) {
+          let url = this._selectedScript();
+          if (url == aLocation.url) {
+            this._skipEditorBreakpointChange = true;
+            this.editor.addBreakpoint(aLocation.line - 1);
+            this._skipEditorBreakpointChange = false;
+          }
+        }
+      }
+
+      aCallback && aCallback(aBpClient, aResponse.error);
+    }.bind(this));
+  },
+
+  /**
+   * Remove a breakpoint.
+   *
+   * @param object aBreakpoint
+   *        The breakpoint you want to remove.
+   * @param function [aCallback]
+   *        Optional function to invoke once the breakpoint is removed. The
+   *        callback is invoked with one argument: the breakpoint location
+   *        object which holds the url and line properties.
+   * @param boolean [aNoEditorUpdate=false]
+   *        Tells if you want to skip editor updates. Typically the editor is
+   *        updated to visually indicate that a breakpoint has been removed.
+   */
+  removeBreakpoint:
+  function DP_removeBreakpoint(aBreakpoint, aCallback, aNoEditorUpdate) {
+    if (!(aBreakpoint.actor in this.breakpoints)) {
+      aCallback && aCallback(aBreakpoint.location);
+      return;
+    }
+
+    aBreakpoint.remove(function() {
+      delete this.breakpoints[aBreakpoint.actor];
+
+      if (!aNoEditorUpdate) {
+        let url = this._selectedScript();
+        if (url == aBreakpoint.location.url) {
+          this._skipEditorBreakpointChange = true;
+          this.editor.removeBreakpoint(aBreakpoint.location.line - 1);
+          this._skipEditorBreakpointChange = false;
+        }
+      }
+
+      aCallback && aCallback(aBreakpoint.location);
+    }.bind(this));
+  },
+
+  /**
+   * Get the breakpoint object at the given location.
+   *
+   * @param string aUrl
+   *        The URL of where the breakpoint is.
+   * @param number aLine
+   *        The line number where the breakpoint is.
+   * @return object
+   *         The BreakpointActor object.
+   */
+  getBreakpoint: function DP_getBreakpoint(aUrl, aLine) {
+    for each (let breakpoint in this.breakpoints) {
+      if (breakpoint.location.url == aUrl && breakpoint.location.line == aLine) {
+        return breakpoint;
+      }
+    }
+    return null;
+  },
+
+  /**
    * Closes the debugger UI removing child nodes and event listeners.
    */
   close: function DP_close() {
+    for each (let breakpoint in this.breakpoints) {
+      this.removeBreakpoint(breakpoint);
+    }
+
     if (this._tab) {
       this._tab._scriptDebugger = null;
       this._tab = null;
     }
     if (this.frame) {
       DebuggerUIPreferences.height = this.frame.height;
 
       this.frame.removeEventListener("unload", this._close, true);
@@ -187,17 +423,17 @@ DebuggerPane.prototype = {
             self.onConnected(self);
           }
         });
       });
     });
   },
 
   get debuggerWindow() {
-    return this.frame.contentWindow;
+    return this.frame ? this.frame.contentWindow : null;
   },
 
   get debuggerClient() {
     return this._client;
   },
 
   get activeThread() {
     try {
@@ -335,16 +571,17 @@ DebuggerUI.prototype = {
     let doc = dbg.frame.contentDocument;
     let scripts = doc.getElementById("scripts");
     let elt = scripts.getElementsByAttribute("value", aSourceUrl)[0];
     let script = elt.getUserData("sourceScript");
     script.loaded = true;
     script.text = aSourceText;
     script.contentType = aContentType;
     elt.setUserData("sourceScript", script, null);
+    dbg._updateEditorBreakpoints();
   }
 };
 
 /**
  * Various debugger UI preferences (currently just the pane height).
  */
 let DebuggerUIPreferences = {
 
--- a/browser/devtools/debugger/debugger-view.js
+++ b/browser/devtools/debugger/debugger-view.js
@@ -17,16 +17,17 @@
  *
  * The Initial Developer of the Original Code is
  *   Mozilla Foundation
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Victor Porof <vporof@mozilla.com> (original author)
+ *   Mihai Sucan <mihai.sucan@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -194,17 +195,16 @@ DebuggerView.Stackframes = {
    * @param boolean aSelect
    *        True if the frame should be selected, false otherwise.
    */
   highlightFrame: function DVF_highlightFrame(aDepth, aSelect) {
     let frame = document.getElementById("stackframe-" + aDepth);
 
     // the list item wasn't found in the stackframe container
     if (!frame) {
-      dump("The frame list item wasn't found in the stackframes container.");
       return;
     }
 
     // add the 'selected' css class if the frame isn't already selected
     if (aSelect && !frame.classList.contains("selected")) {
       frame.classList.add("selected");
 
     // remove the 'selected' css class if the frame is already selected
@@ -351,17 +351,16 @@ DebuggerView.Properties = {
     // compute the id of the element if not specified
     aId = aId || (aName.toLowerCase().trim().replace(" ", "-") + "-scope");
 
     // contains generic nodes and functionality
     let element = this._createPropertyElement(aName, aId, "scope", this._vars);
 
     // make sure the element was created successfully
     if (!element) {
-      dump("The debugger scope container wasn't created properly: " + aId);
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._addVar
      */
     element.addVar = this._addVar.bind(this, element);
 
@@ -393,17 +392,16 @@ DebuggerView.Properties = {
     aId = aId || (aScope.id + "->" + aName + "-variable");
 
     // contains generic nodes and functionality
     let element = this._createPropertyElement(aName, aId, "variable",
                                               aScope.querySelector(".details"));
 
     // make sure the element was created successfully
     if (!element) {
-      dump("The debugger variable container wasn't created properly: " + aId);
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._setGrip
      */
     element.setGrip = this._setGrip.bind(this, element);
 
@@ -461,17 +459,16 @@ DebuggerView.Properties = {
     if (!aVar) {
       return null;
     }
 
     let info = aVar.querySelector(".info") || aVar.target.info;
 
     // make sure the info node exists
     if (!info) {
-      dump("Could not set the grip for the corresponding variable: " + aVar.id);
       return null;
     }
 
     info.textContent = this._propertyString(aGrip);
     info.classList.add(this._propertyColor(aGrip));
 
     return aVar;
   },
@@ -564,17 +561,16 @@ DebuggerView.Properties = {
     aId = aId || (aVar.id + "->" + aProperty[0] + "-property");
 
     // contains generic nodes and functionality
     let element = this._createPropertyElement(aName, aId, "property",
                                               aVar.querySelector(".details"));
 
     // make sure the element was created successfully
     if (!element) {
-      dump("The debugger property container wasn't created properly.");
       return null;
     }
 
     /**
      * @see DebuggerView.Properties._setGrip
      */
     element.setGrip = this._setGrip.bind(this, element);
 
@@ -705,21 +701,19 @@ DebuggerView.Properties = {
    * @param object aParent
    *        The parent node which will contain the element.
    * @return object
    *         The newly created html node representing the generic elem.
    */
   _createPropertyElement: function DVP__createPropertyElement(aName, aId, aClass, aParent) {
     // make sure we don't duplicate anything and the parent exists
     if (document.getElementById(aId)) {
-      dump("Duplicating a property element id is not allowed.");
       return null;
     }
     if (!aParent) {
-      dump("A property element must have a valid parent node specified.");
       return null;
     }
 
     let element = document.createElement("div");
     let arrow = document.createElement("span");
     let name = document.createElement("span");
     let title = document.createElement("div");
     let details = document.createElement("div");
@@ -1122,16 +1116,25 @@ DebuggerView.Scripts = {
     for (let i = 0; i < this._scripts.itemCount; i++) {
       if (this._scripts.getItemAtIndex(i).value == aUrl) {
         this._scripts.selectedIndex = i;
         break;
       }
     }
   },
 
+   /**
+   	* Retrieve the URL of the selected script.
+   	* @return string|null
+   	*/
+   get selected() {
+    return this._scripts.selectedItem ?
+           this._scripts.selectedItem.value : null;
+   },
+
   /**
    * Adds a script to the scripts container.
    * If the script already exists (was previously added), null is returned.
    * Otherwise, the newly created element is returned.
    *
    * @param string aLabel
    *        The simplified script location to be shown.
    * @param string aScript
--- a/browser/devtools/debugger/debugger.js
+++ b/browser/devtools/debugger/debugger.js
@@ -73,17 +73,18 @@ function startDebuggingTab(aClient, aTab
 {
   gClient = aClient;
 
   gClient.attachTab(aTabGrip.actor, function(aResponse, aTabClient) {
     if (aTabClient) {
       gTabClient = aTabClient;
       gClient.attachThread(aResponse.threadActor, function(aResponse, aThreadClient) {
         if (!aThreadClient) {
-          dump("Couldn't attach to thread: "+aResponse.error+"\n");
+          Components.utils.reportError("Couldn't attach to thread: " +
+                                       aResponse.error);
           return;
         }
         ThreadState.connect(aThreadClient, function() {
           StackFrames.connect(aThreadClient, function() {
             SourceScripts.connect(aThreadClient, function() {
               aThreadClient.resume();
             });
           });
@@ -607,16 +608,17 @@ var SourceScripts = {
     if (!aScript.loaded) {
       // Notify the chrome code that we need to load a script file.
       var evt = document.createEvent("CustomEvent");
       evt.initCustomEvent("Debugger:LoadSource", true, false, aScript.url);
       document.documentElement.dispatchEvent(evt);
       window.editor.setText(DebuggerView.getStr("loadingText"));
     } else {
       window.editor.setText(aScript.text);
+      window.updateEditorBreakpoints();
     }
   }
 };
 
 SourceScripts.onPaused = SourceScripts.onPaused.bind(SourceScripts);
 SourceScripts.onScripts = SourceScripts.onScripts.bind(SourceScripts);
 SourceScripts.onNewScript = SourceScripts.onNewScript.bind(SourceScripts);
 SourceScripts.onScriptsCleared = SourceScripts.onScriptsCleared.bind(SourceScripts);
--- a/browser/devtools/debugger/test/Makefile.in
+++ b/browser/devtools/debugger/test/Makefile.in
@@ -68,16 +68,17 @@ include $(topsrcdir)/config/rules.mk
 	browser_dbg_stack-03.js \
 	browser_dbg_stack-04.js \
 	browser_dbg_location-changes.js \
 	browser_dbg_script-switching.js \
 	browser_dbg_pause-resume.js \
 	browser_dbg_update-editor-mode.js \
 	browser_dbg_select-line.js \
 	browser_dbg_clean-exit.js \
+	browser_dbg_bug723069_editor-breakpoints.js \
 	head.js \
 	$(NULL)
 
 _BROWSER_TEST_PAGES = \
 	browser_dbg_tab1.html \
 	browser_dbg_tab2.html \
 	browser_dbg_debuggerstatement.html \
 	browser_dbg_stack.html \
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug723069_editor-breakpoints.js
@@ -0,0 +1,274 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 723069: test the debugger breakpoint API and connection to the source
+ * editor.
+ */
+
+const TAB_URL = EXAMPLE_URL + "browser_dbg_script-switching.html";
+
+let gPane = null;
+let gTab = null;
+let gDebuggee = null;
+let gDebugger = null;
+let gScripts = null;
+let gEditor = null;
+let gBreakpoints = null;
+
+function test()
+{
+  let tempScope = {};
+  Cu.import("resource:///modules/source-editor.jsm", tempScope);
+  let SourceEditor = tempScope.SourceEditor;
+
+  debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
+    gTab = aTab;
+    gDebuggee = aDebuggee;
+    gPane = aPane;
+    gDebugger = gPane.debuggerWindow;
+
+    gPane.activeThread.addOneTimeListener("scriptsadded", function() {
+      Services.tm.currentThread.dispatch({ run: onScriptsAdded }, 0);
+    });
+    gDebuggee.firstCall();
+  });
+
+  function onScriptsAdded()
+  {
+    gScripts = gDebugger.DebuggerView.Scripts;
+
+    is(gDebugger.StackFrames.activeThread.state, "paused",
+      "Should only be getting stack frames while paused.");
+
+    is(gScripts._scripts.itemCount, 2, "Found the expected number of scripts.");
+
+    gEditor = gDebugger.editor;
+
+    isnot(gEditor.getText().indexOf("debugger"), -1,
+          "The correct script was loaded initially.");
+    isnot(gScripts.selected, gScripts.scriptLocations()[0],
+          "the correct sccript is selected");
+
+    gBreakpoints = gPane.breakpoints;
+    is(Object.keys(gBreakpoints), 0, "no breakpoints");
+    ok(!gPane.getBreakpoint("foo", 3), "getBreakpoint('foo', 3) returns falsey");
+
+    is(gEditor.getBreakpoints().length, 0, "no breakpoints in the editor");
+
+
+    info("add the first breakpoint");
+    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                             onEditorBreakpointAddFirst);
+    let location = {url: gScripts.selected, line: 6};
+    executeSoon(function() {
+      gPane.addBreakpoint(location, onBreakpointAddFirst);
+    });
+  }
+
+  let breakpointsAdded = 0;
+  let breakpointsRemoved = 0;
+  let editorBreakpointChanges = 0;
+
+  function onEditorBreakpointAddFirst(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointAddFirst);
+    editorBreakpointChanges++;
+
+    ok(aEvent, "breakpoint1 added to the editor");
+    is(aEvent.added.length, 1, "one breakpoint added to the editor");
+    is(aEvent.removed.length, 0, "no breakpoint was removed from the editor");
+    is(aEvent.added[0].line, 5, "editor breakpoint line is correct");
+
+    is(gEditor.getBreakpoints().length, 1,
+       "editor.getBreakpoints().length is correct");
+  }
+
+  function onBreakpointAddFirst(aBreakpointClient, aResponseError)
+  {
+    breakpointsAdded++;
+
+    ok(aBreakpointClient, "breakpoint1 added, client received");
+    ok(!aResponseError, "breakpoint1 added without errors");
+    is(aBreakpointClient.location.url, gScripts.selected,
+       "breakpoint1 client url is correct");
+    is(aBreakpointClient.location.line, 6,
+       "breakpoint1 client line is correct");
+
+    executeSoon(function() {
+      ok(aBreakpointClient.actor in gBreakpoints,
+         "breakpoint1 client found in the list of debugger breakpoints");
+      is(Object.keys(gBreakpoints).length, 1,
+         "the list of debugger breakpoints holds only one breakpoint");
+      is(gPane.getBreakpoint(gScripts.selected, 6), aBreakpointClient,
+         "getBreakpoint(selectedScript, 2) returns the correct breakpoint");
+
+      info("remove the first breakpoint");
+      gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                               onEditorBreakpointRemoveFirst);
+      gPane.removeBreakpoint(aBreakpointClient, onBreakpointRemoveFirst);
+    });
+  }
+
+  function onBreakpointRemoveFirst(aLocation)
+  {
+    breakpointsRemoved++;
+
+    ok(aLocation, "breakpoint1 removed");
+    is(aLocation.url, gScripts.selected, "breakpoint1 remove: url is correct");
+    is(aLocation.line, 6, "breakpoint1 remove: line is correct");
+
+    executeSoon(testBreakpointAddBackground);
+  }
+
+  function onEditorBreakpointRemoveFirst(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointRemoveFirst);
+    editorBreakpointChanges++;
+
+    ok(aEvent, "breakpoint1 removed from the editor");
+    is(aEvent.added.length, 0, "no breakpoint was added to the editor");
+    is(aEvent.removed.length, 1, "one breakpoint was removed from the editor");
+    is(aEvent.removed[0].line, 5, "editor breakpoint line is correct");
+
+    is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct");
+  }
+
+  function testBreakpointAddBackground()
+  {
+    info("add a breakpoint to the second script which is not selected");
+
+    is(Object.keys(gBreakpoints).length, 0, "no breakpoints in the debugger");
+    ok(!gPane.getBreakpoint(gScripts.selected, 6),
+       "getBreakpoint(selectedScript, 6) returns no breakpoint");
+
+    let script0 = gScripts.scriptLocations()[0];
+    isnot(script0, gScripts.selected,
+          "first script location is not the currently selected script");
+
+    let location = {url: script0, line: 5};
+    gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                             onEditorBreakpointAddBackgroundTrap);
+    gPane.addBreakpoint(location, onBreakpointAddBackground);
+  }
+
+  function onEditorBreakpointAddBackgroundTrap(aEvent)
+  {
+    // trap listener: no breakpoint must be added to the editor when a breakpoint
+    // is added to a script that is not currently selected.
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointAddBackgroundTrap);
+    editorBreakpointChanges++;
+    ok(false, "breakpoint2 must not be added to the editor");
+  }
+
+  function onBreakpointAddBackground(aBreakpointClient, aResponseError)
+  {
+    breakpointsAdded++;
+
+    ok(aBreakpointClient, "breakpoint2 added, client received");
+    ok(!aResponseError, "breakpoint2 added without errors");
+    is(aBreakpointClient.location.url, gScripts.scriptLocations()[0],
+       "breakpoint2 client url is correct");
+    is(aBreakpointClient.location.line, 5,
+       "breakpoint2 client line is correct");
+
+    executeSoon(function() {
+      ok(aBreakpointClient.actor in gBreakpoints,
+         "breakpoint2 client found in the list of debugger breakpoints");
+      is(Object.keys(gBreakpoints).length, 1, "one breakpoint in the debugger");
+      is(gPane.getBreakpoint(gScripts.scriptLocations()[0], 5), aBreakpointClient,
+         "getBreakpoint(scriptLocations[0], 5) returns the correct breakpoint");
+
+      // remove the trap listener
+      gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                  onEditorBreakpointAddBackgroundTrap);
+
+      gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                               onEditorBreakpointAddSwitch);
+      gEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                               onEditorTextChanged);
+
+      info("switch to the second script");
+
+      gScripts._scripts.selectedIndex = 0;
+      gDebugger.SourceScripts.onChange({ target: gScripts._scripts });
+    });
+  }
+
+  function onEditorBreakpointAddSwitch(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointAddSwitch);
+    editorBreakpointChanges++;
+
+    ok(aEvent, "breakpoint2 added to the editor");
+    is(aEvent.added.length, 1, "one breakpoint added to the editor");
+    is(aEvent.removed.length, 0, "no breakpoint was removed from the editor");
+    is(aEvent.added[0].line, 4, "editor breakpoint line is correct");
+
+    is(gEditor.getBreakpoints().length, 1,
+       "editor.getBreakpoints().length is correct");
+  }
+
+  function onEditorTextChanged()
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED,
+                                onEditorTextChanged);
+
+    is(gEditor.getText().indexOf("debugger"), -1,
+       "The second script is no longer displayed.");
+
+    isnot(gEditor.getText().indexOf("firstCall"), -1,
+          "The first script is displayed.");
+
+    executeSoon(function() {
+      info("remove the second breakpoint using the mouse");
+
+      gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                               onEditorBreakpointRemoveSecond);
+
+      let testWin = gEditor.editorElement.ownerDocument.defaultView;
+      EventUtils.synthesizeMouse(gEditor.editorElement, 10, 70, {}, testWin);
+    });
+
+  }
+
+  function onEditorBreakpointRemoveSecond(aEvent)
+  {
+    gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE,
+                                onEditorBreakpointRemoveSecond);
+    editorBreakpointChanges++;
+
+    ok(aEvent, "breakpoint2 removed from the editor");
+    is(aEvent.added.length, 0, "no breakpoint was added to the editor");
+    is(aEvent.removed.length, 1, "one breakpoint was removed from the editor");
+    is(aEvent.removed[0].line, 4, "editor breakpoint line is correct");
+
+    is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct");
+
+    executeSoon(function() {
+      gDebugger.StackFrames.activeThread.resume(finish);
+    });
+  }
+
+  registerCleanupFunction(function() {
+    is(Object.keys(gBreakpoints).length, 0, "no breakpoint in the debugger");
+    ok(!gPane.getBreakpoint(gScripts.scriptLocations()[0], 5),
+       "getBreakpoint(scriptLocations[0], 5) returns no breakpoint");
+
+    removeTab(gTab);
+    is(breakpointsAdded, 2, "correct number of breakpoints have been added");
+    is(breakpointsRemoved, 1, "correct number of breakpoints have been removed");
+    is(editorBreakpointChanges, 4, "correct number of editor breakpoint changes");
+    gPane = null;
+    gTab = null;
+    gDebuggee = null;
+    gDebugger = null;
+    gScripts = null;
+    gEditor = null;
+    gBreakpoints = null;
+  });
+}
--- a/browser/devtools/debugger/test/browser_dbg_clean-exit.js
+++ b/browser/devtools/debugger/test/browser_dbg_clean-exit.js
@@ -2,39 +2,41 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 // Test that closing a tab with the debugger in a paused state exits cleanly.
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 const DEBUGGER_TAB_URL = EXAMPLE_URL + "browser_dbg_debuggerstatement.html";
 
 function test() {
   debug_tab_pane(DEBUGGER_TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testCleanExit();
   });
 }
 
 function testCleanExit() {
   gPane.activeThread.addOneTimeListener("framesadded", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       is(gDebugger.StackFrames.activeThread.paused, true,
         "Should be paused after the debugger statement.");
 
-      gPane._client.addOneTimeListener("tabDetached", function () {
-        finish();
-      });
-      removeTab(gTab);
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   gTab.linkedBrowser.contentWindow.wrappedJSObject.runDebuggerStatement();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_location-changes.js
+++ b/browser/devtools/debugger/test/browser_dbg_location-changes.js
@@ -50,15 +50,22 @@ function testSimpleCall() {
 function testLocationChange()
 {
   gDebugger.StackFrames.activeThread.resume(function() {
     gPane._client.addOneTimeListener("tabNavigated", function(aEvent, aPacket) {
       ok(true, "tabNavigated event was fired.");
       gPane._client.addOneTimeListener("tabAttached", function(aEvent, aPacket) {
         ok(true, "Successfully reattached to the tab again.");
 
-        removeTab(gTab);
-        finish();
+        closeDebuggerAndFinish(gTab);
       });
     });
     content.location = TAB1_URL;
   });
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_pause-resume.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-resume.js
@@ -1,23 +1,21 @@
 /* vim:set ts=2 sw=2 sts=2 et: */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testPause();
   });
 }
 
 function testPause() {
@@ -58,17 +56,22 @@ function testResume() {
 
       is(gDebugger.StackFrames.activeThread.paused, false,
         "Should be paused after an interrupt request.");
 
       let button = gDebugger.document.getElementById("resume");
       is(button.label, gDebugger.DebuggerView.getStr("pauseLabel"),
         "Button label should be pause when running.");
 
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   EventUtils.sendMouseEvent({ type: "click" },
     gDebugger.document.getElementById("resume"),
     gDebugger);
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-01.js
@@ -129,14 +129,20 @@ function resumeAndFinish() {
       ok(gDebugger.DebuggerView.Scripts.containsLabel("x/script.js"),
         "Script (4) label is incorrect.");
       ok(gDebugger.DebuggerView.Scripts.containsLabel("x/y/script.js"),
         "Script (5) label is incorrect.");
 
       is(vs._scripts.itemCount, 6,
         "Got too many script items in the list!");
 
-
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     });
   });
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-02.js
@@ -111,21 +111,24 @@ function testSimpleCall() {
 
       EventUtils.sendMouseEvent({ type: "click" },
         testScope.querySelector(".title"),
         gDebugger);
 
       ok(!testScope.expanded,
         "Clicking again the testScope tilte should collapse it.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-03.js
@@ -113,21 +113,24 @@ function testSimpleCall() {
 
       testScope.remove();
       is(removeCallbackSender, testScope,
         "The removeCallback wasn't called as it should.");
 
       is(gDebugger.DebuggerView.Properties._vars.childNodes.length, 4,
         "The scope should have been removed from the parent container tree.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-04.js
@@ -68,21 +68,24 @@ function testSimpleCall() {
       is(testVar.querySelector(".details").childNodes.length, 0,
         "The var should remove all it's details container tree children.");
 
       testVar.remove();
 
       is(testScope.querySelector(".details").childNodes.length, 0,
         "The var should have been removed from the parent container tree.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-05.js
@@ -76,21 +76,24 @@ function testSimpleCall() {
       is(testVar.querySelector(".details").childNodes.length, 0,
         "The var should remove all it's details container tree children.");
 
       testVar.remove();
 
       is(testScope.querySelector(".details").childNodes.length, 0,
         "The var should have been removed from the parent container tree.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-06.js
@@ -112,22 +112,24 @@ function testSimpleCall() {
         "The grip information for the localVar3 wasn't set correctly.");
 
       is(localVar4.querySelector(".info").textContent, "null",
         "The grip information for the localVar4 wasn't set correctly.");
 
       is(localVar5.querySelector(".info").textContent, "[object Object]",
         "The grip information for the localVar5 wasn't set correctly.");
 
-
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-07.js
@@ -5,24 +5,22 @@
 /**
  * Make sure that the property view displays function parameters.
  */
 
 const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
@@ -82,15 +80,21 @@ function testFrameParameters()
 function resumeAndFinish() {
   gPane.activeThread.addOneTimeListener("framescleared", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       var frames = gDebugger.DebuggerView.Stackframes._frames;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames.");
 
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   gDebugger.StackFrames.activeThread.resume();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
+++ b/browser/devtools/debugger/test/browser_dbg_propertyview-08.js
@@ -5,24 +5,22 @@
 /**
  * Make sure that the property view displays the properties of objects.
  */
 
 const TAB_URL = EXAMPLE_URL + "browser_dbg_frame-parameters.html";
 
 var gPane = null;
 var gTab = null;
-var gDebuggee = null;
 var gDebugger = null;
 
 function test()
 {
   debug_tab_pane(TAB_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
-    gDebuggee = aDebuggee;
     gPane = aPane;
     gDebugger = gPane.debuggerWindow;
 
     testFrameParameters();
   });
 }
 
 function testFrameParameters()
@@ -98,15 +96,21 @@ function testFrameParameters()
 function resumeAndFinish() {
   gPane.activeThread.addOneTimeListener("framescleared", function() {
     Services.tm.currentThread.dispatch({ run: function() {
       var frames = gDebugger.DebuggerView.Stackframes._frames;
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames.");
 
-      removeTab(gTab);
-      finish();
+      closeDebuggerAndFinish(gTab);
     }}, 0);
   });
 
   gDebugger.StackFrames.activeThread.resume();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_script-switching.js
+++ b/browser/devtools/debugger/test/browser_dbg_script-switching.js
@@ -98,11 +98,18 @@ function testSwitchPaused()
 function testSwitchRunning()
 {
   ok(gDebugger.editor.getText().search(/debugger/) != -1,
     "The second script is displayed again.");
 
   ok(gDebugger.editor.getText().search(/firstCall/) == -1,
     "The first script is no longer displayed.");
 
+  closeDebuggerAndFinish(gTab);
+}
+
+registerCleanupFunction(function() {
   removeTab(gTab);
-  finish();
-}
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_select-line.js
+++ b/browser/devtools/debugger/test/browser_dbg_select-line.js
@@ -63,23 +63,39 @@ function testSelectLine() {
           // Yield control back to the event loop so that the debugger has a
           // chance to highlight the proper line.
           executeSoon(function(){
             // getCaretPosition is 0-based.
             is(gDebugger.editor.getCaretPosition().line, 4,
                "The correct line is selected.");
 
             gDebugger.StackFrames.activeThread.resume(function() {
-              removeTab(gTab);
-              finish();
+              closeDebuggerAndFinish(gTab);
             });
           });
         });
 
+        // Scroll all the way down to ensure stackframe-3 is visible.
+        let stackframes = gDebugger.document.getElementById("stackframes");
+        stackframes.scrollTop = stackframes.scrollHeight;
+
         // Click the oldest stack frame.
+        let frames = gDebugger.DebuggerView.Stackframes._frames;
+        is(frames.querySelectorAll(".dbg-stackframe").length, 4,
+          "Should have four frames.");
+
         let element = gDebugger.document.getElementById("stackframe-3");
+        isnot(element, null, "Found the third stack frame.");
         EventUtils.synthesizeMouseAtCenter(element, {}, gDebugger);
       });
     }}, 0);
   });
 
   gDebuggee.firstCall();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-01.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-01.js
@@ -31,21 +31,24 @@ function testSimpleCall() {
         "Should only be getting stack frames while paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 1,
         "Should have only one frame.");
 
       is(childNodes.length, frames.querySelectorAll(".dbg-stackframe").length,
         "All children should be frames.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.simpleCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-02.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-02.js
@@ -62,22 +62,24 @@ function testEvalCall() {
         gDebugger);
 
       ok(frames.querySelector("#stackframe-0").classList.contains("selected"),
          "First frame should be selected after click inside the first frame.");
 
       ok(!frames.querySelector("#stackframe-1").classList.contains("selected"),
          "Second frame should not be selected after click inside the first frame.");
 
-      resumeAndFinish();
+      gDebugger.StackFrames.activeThread.resume(function() {
+        closeDebuggerAndFinish(gTab);
+      });
     }}, 0);
   });
 
   gDebuggee.evalCall();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
-
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-03.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-03.js
@@ -42,27 +42,30 @@ function testRecurse() {
 
         is(frames.querySelectorAll(".dbg-stackframe").length, pageSize * 2,
           "Should now have twice the max limit of frames.");
 
         gPane.activeThread.addOneTimeListener("framesadded", function() {
           is(frames.querySelectorAll(".dbg-stackframe").length, recurseLimit,
             "Should have reached the recurse limit.");
 
-          resumeAndFinish();
+          gDebugger.StackFrames.activeThread.resume(function() {
+            closeDebuggerAndFinish(gTab);
+          });
         });
 
         frames.scrollTop = frames.scrollHeight;
       });
 
       frames.scrollTop = frames.scrollHeight;
     }}, 0);
   });
 
   gDebuggee.recurse();
 }
 
-function resumeAndFinish() {
-  gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
-  });
-}
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_stack-04.js
+++ b/browser/devtools/debugger/test/browser_dbg_stack-04.js
@@ -43,18 +43,25 @@ function testEvalCallResume() {
           "Should have no frames after resume");
 
         is(childNodes.length, 1,
           "Should only have one child.");
 
         is(frames.querySelectorAll(".empty").length, 1,
            "Should have the empty list explanation.");
 
-        removeTab(gTab);
-        finish();
+        closeDebuggerAndFinish(gTab);
       });
 
       gPane.activeThread.resume();
     }}, 0);
   });
 
   gDebuggee.evalCall();
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+});
--- a/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
+++ b/browser/devtools/debugger/test/browser_dbg_update-editor-mode.js
@@ -67,12 +67,20 @@ function testSwitchPaused()
 
   ok(gDebugger.editor.getText().search(/firstCall/) != -1,
     "The first script is displayed.");
 
   is(gDebugger.editor.getMode(), SourceEditor.MODES.JAVASCRIPT,
      "Found the expected editor mode.");
 
   gDebugger.StackFrames.activeThread.resume(function() {
-    removeTab(gTab);
-    finish();
+    closeDebuggerAndFinish(gTab);
   });
 }
+
+registerCleanupFunction(function() {
+  removeTab(gTab);
+  gPane = null;
+  gTab = null;
+  gDebuggee = null;
+  gDebugger = null;
+  gScripts = null;
+});
--- a/browser/devtools/debugger/test/head.js
+++ b/browser/devtools/debugger/test/head.js
@@ -44,16 +44,24 @@ function addTab(aURL, aOnload)
 
   return tab;
 }
 
 function removeTab(aTab) {
   gBrowser.removeTab(aTab);
 }
 
+function closeDebuggerAndFinish(aTab) {
+  DebuggerUI.aWindow.addEventListener("Debugger:Shutdown", function cleanup() {
+    DebuggerUI.aWindow.removeEventListener("Debugger:Shutdown", cleanup, false);
+    finish();
+  }, false);
+  DebuggerUI.getDebugger(aTab).close();
+}
+
 function get_tab_actor_for_url(aClient, aURL, aCallback) {
   aClient.listTabs(function(aResponse) {
     for each (let tab in aResponse.tabs) {
       if (tab.url == aURL) {
         aCallback(tab);
         return;
       }
     }
--- a/browser/devtools/sourceeditor/source-editor-orion.jsm
+++ b/browser/devtools/sourceeditor/source-editor-orion.jsm
@@ -18,16 +18,17 @@
  * The Mozilla Foundation.
  * Portions created by the Initial Developer are Copyright (C) 2011
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Mihai Sucan <mihai.sucan@gmail.com> (original author)
  *   Kenny Heaton <kennyheaton@gmail.com>
  *   Spyros Livathinos <livathinos.spyros@gmail.com>
+ *   Allen Eubank <adeubank@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -119,16 +120,28 @@ const DEFAULT_KEYBINDINGS = [
     accel: true,
     shift: true,
   },
   {
     action: "Unindent Lines",
     code: Ci.nsIDOMKeyEvent.DOM_VK_TAB,
     shift: true,
   },
+  {
+    action: "Move Lines Up",
+    code: Ci.nsIDOMKeyEvent.DOM_VK_UP,
+    ctrl: Services.appinfo.OS == "Darwin",
+    alt: true,
+  },
+  {
+    action: "Move Lines Down",
+    code: Ci.nsIDOMKeyEvent.DOM_VK_DOWN,
+    ctrl: Services.appinfo.OS == "Darwin",
+    alt: true,
+  },
 ];
 
 var EXPORTED_SYMBOLS = ["SourceEditor"];
 
 /**
  * The SourceEditor object constructor. The SourceEditor component allows you to
  * provide users with an editor tailored to the specific needs of editing source
  * code, aimed primarily at web developers.
@@ -362,26 +375,35 @@ SourceEditor.prototype = {
       "redo": [this.redo, this],
       "tab": [this._doTab, this],
       "Unindent Lines": [this._doUnindentLines, this],
       "enter": [this._doEnter, this],
       "Find...": [this.ui.find, this.ui],
       "Find Next Occurrence": [this.ui.findNext, this.ui],
       "Find Previous Occurrence": [this.ui.findPrevious, this.ui],
       "Goto Line...": [this.ui.gotoLine, this.ui],
+      "Move Lines Down": [this._moveLines, this],
     };
 
     for (let name in actions) {
       let action = actions[name];
       this._view.setAction(name, action[0].bind(action[1]));
     }
 
+    this._view.setAction("Move Lines Up", this._moveLines.bind(this, true));
+
     let keys = (config.keys || []).concat(DEFAULT_KEYBINDINGS);
     keys.forEach(function(aKey) {
-      let binding = new KeyBinding(aKey.code, aKey.accel, aKey.shift, aKey.alt);
+      // In Orion mod1 refers to Cmd on Macs and Ctrl on Windows and Linux.
+      // So, if ctrl is in aKey we use it on Windows and Linux, otherwise
+      // we use aKey.accel for mod1.
+      let mod1 = Services.appinfo.OS != "Darwin" &&
+                 "ctrl" in aKey ? aKey.ctrl : aKey.accel;
+      let binding = new KeyBinding(aKey.code, mod1, aKey.shift, aKey.alt,
+                                  aKey.ctrl);
       this._view.setKeyBinding(binding, aKey.action);
 
       if (aKey.callback) {
         this._view.setAction(aKey.action, aKey.callback);
       }
     }, this);
 
     this._initEventTarget();
@@ -574,16 +596,88 @@ SourceEditor.prototype = {
     }
 
     this.setText(this.getLineDelimiter() + prefix, selection.start,
                  selection.end);
     return true;
   },
 
   /**
+   * Move lines upwards or downwards, relative to the current caret location.
+   *
+   * @private
+   * @param boolean aLineAbove
+   *        True if moving lines up, false to move lines down.
+   */
+  _moveLines: function SE__moveLines(aLineAbove)
+  {
+    if (this.readOnly) {
+      return false;
+    }
+
+    let model = this._model;
+    let selection = this.getSelection();
+    let firstLine = model.getLineAtOffset(selection.start);
+    if (firstLine == 0 && aLineAbove) {
+      return true;
+    }
+
+    let lastLine = model.getLineAtOffset(selection.end);
+    let firstLineStart = model.getLineStart(firstLine);
+    let lastLineStart = model.getLineStart(lastLine);
+    if (selection.start != selection.end && lastLineStart == selection.end) {
+      lastLine--;
+    }
+    if (!aLineAbove && (lastLine + 1) == this.getLineCount()) {
+      return true;
+    }
+
+    let lastLineEnd = model.getLineEnd(lastLine, true);
+    let text = this.getText(firstLineStart, lastLineEnd);
+
+    if (aLineAbove) {
+      let aboveLine = firstLine - 1;
+      let aboveLineStart = model.getLineStart(aboveLine);
+
+      this.startCompoundChange();
+      if (lastLine == (this.getLineCount() - 1)) {
+        let delimiterStart = model.getLineEnd(aboveLine);
+        let delimiterEnd = model.getLineEnd(aboveLine, true);
+        let lineDelimiter = this.getText(delimiterStart, delimiterEnd);
+        text += lineDelimiter;
+        this.setText("", firstLineStart - lineDelimiter.length, lastLineEnd);
+      } else {
+        this.setText("", firstLineStart, lastLineEnd);
+      }
+      this.setText(text, aboveLineStart, aboveLineStart);
+      this.endCompoundChange();
+      this.setSelection(aboveLineStart, aboveLineStart + text.length);
+    } else {
+      let belowLine = lastLine + 1;
+      let belowLineEnd = model.getLineEnd(belowLine, true);
+
+      let insertAt = belowLineEnd - lastLineEnd + firstLineStart;
+      let lineDelimiter = "";
+      if (belowLine == this.getLineCount() - 1) {
+        let delimiterStart = model.getLineEnd(lastLine);
+        lineDelimiter = this.getText(delimiterStart, lastLineEnd);
+        text = lineDelimiter + text.substr(0, text.length -
+                                              lineDelimiter.length);
+      }
+      this.startCompoundChange();
+      this.setText("", firstLineStart, lastLineEnd);
+      this.setText(text, insertAt, insertAt);
+      this.endCompoundChange();
+      this.setSelection(insertAt + lineDelimiter.length,
+                        insertAt + text.length);
+    }
+    return true;
+  },
+
+  /**
    * The Orion Selection event handler. The current caret line is
    * highlighted and for Linux users the selected text is copied into the X11
    * PRIMARY buffer.
    *
    * @private
    * @param object aEvent
    *        The Orion Selection event object.
    */
--- a/browser/devtools/sourceeditor/source-editor.jsm
+++ b/browser/devtools/sourceeditor/source-editor.jsm
@@ -187,16 +187,17 @@ SourceEditor.DEFAULTS = {
   highlightCurrentLine: true,
 
   /**
    * An array of objects that allows you to define custom editor keyboard
    * bindings. Each object can have:
    *   - action - name of the editor action to invoke.
    *   - code - keyCode for the shortcut.
    *   - accel - boolean for the Accel key (Cmd on Macs, Ctrl on Linux/Windows).
+   *   - ctrl - boolean for the Control key
    *   - shift - boolean for the Shift key.
    *   - alt - boolean for the Alt key.
    *   - callback - optional function to invoke, if the action is not predefined
    *   in the editor.
    * @type array
    */
   keys: null,
 
--- a/browser/devtools/sourceeditor/test/Makefile.in
+++ b/browser/devtools/sourceeditor/test/Makefile.in
@@ -53,13 +53,14 @@ include $(topsrcdir)/config/rules.mk
 		browser_bug684546_reset_undo.js \
 		browser_bug695035_middle_click_paste.js \
 		browser_bug687160_line_api.js \
 		browser_bug650345_find.js \
 		browser_bug703692_focus_blur.js \
 		browser_bug725388_mouse_events.js \
 		browser_bug707987_debugger_breakpoints.js \
 		browser_bug712982_line_ruler_click.js \
+		browser_bug725618_moveLines_shortcut.js \
 		browser_bug700893_dirty_state.js \
 		head.js \
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/sourceeditor/test/browser_bug725618_moveLines_shortcut.js
@@ -0,0 +1,117 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+let tempScope = {};
+Cu.import("resource:///modules/source-editor.jsm", tempScope);
+let SourceEditor = tempScope.SourceEditor;
+
+let editor;
+let testWin;
+
+function test()
+{
+  waitForExplicitFinish();
+
+  const windowUrl = "data:application/vnd.mozilla.xul+xml,<?xml version='1.0'?>" +
+    "<window xmlns='http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'" +
+    " title='test for bug 725618 - moveLines shortcut' width='300' height='500'>" +
+    "<box flex='1'/></window>";
+  const windowFeatures = "chrome,titlebar,toolbar,centerscreen,resizable,dialog=no";
+
+  testWin = Services.ww.openWindow(null, windowUrl, "_blank", windowFeatures, null);
+  testWin.addEventListener("load", function onWindowLoad() {
+    testWin.removeEventListener("load", onWindowLoad, false);
+    waitForFocus(initEditor, testWin);
+  }, false);
+}
+
+function initEditor()
+{
+  let box = testWin.document.querySelector("box");
+
+  let text = "target\nfoo\nbar"
+  let config = {
+    initialText: text,
+  };
+
+  editor = new SourceEditor();
+  editor.init(box, config, editorLoaded);
+}
+
+function editorLoaded()
+{
+  editor.focus();
+
+  editor.setCaretOffset(0);
+
+  let modifiers = {altKey: true, ctrlKey: Services.appinfo.OS == "Darwin"};
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "foo\ntarget\nbar", "Move lines down works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "foo\nbar\ntarget", "Move lines down works");
+  is(editor.getSelectedText(), "target", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "foo\nbar\ntarget", "Check for bottom of editor works");
+  is(editor.getSelectedText(), "target", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "foo\ntarget\nbar", "Move lines up works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar", "Move lines up works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar", "Check for top of editor works");
+  is(editor.getSelectedText(), "target\n", "selection is correct");
+
+  editor.setSelection(0, 10);
+  info("text within selection =" + editor.getSelectedText());
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "bar\ntarget\nfoo", "Multiple line move down works");
+  is(editor.getSelectedText(), "target\nfoo", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "bar\ntarget\nfoo",
+      "Check for bottom of editor works with multiple line selection");
+  is(editor.getSelectedText(), "target\nfoo", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar", "Multiple line move up works");
+  is(editor.getSelectedText(), "target\nfoo\n", "selection is correct");
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar",
+      "Check for top of editor works with multiple line selection");
+  is(editor.getSelectedText(), "target\nfoo\n", "selection is correct");
+
+  editor.readOnly = true;
+
+  editor.setCaretOffset(0);
+
+  EventUtils.synthesizeKey("VK_UP", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar",
+     "Check for readOnly mode works with move lines up");
+
+  EventUtils.synthesizeKey("VK_DOWN", modifiers, testWin);
+  is(editor.getText(), "target\nfoo\nbar",
+     "Check for readOnly mode works with move lines down");
+
+  finish();
+}
+
+registerCleanupFunction(function()
+{
+  editor.destroy();
+  testWin.close();
+  testWin = editor = null;
+});
--- a/browser/devtools/styleinspector/CssLogic.jsm
+++ b/browser/devtools/styleinspector/CssLogic.jsm
@@ -1193,36 +1193,51 @@ CssSheet.prototype = {
  * argument must point to the element.
  * @constructor
  */
 function CssRule(aCssSheet, aDomRule, aElement)
 {
   this._cssSheet = aCssSheet;
   this._domRule = aDomRule;
 
+  let parentRule = aDomRule.parentRule;
+  if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
+    this.mediaText = parentRule.media.mediaText;
+  }
+
   if (this._cssSheet) {
     // parse _domRule.selectorText on call to this.selectors
     this._selectors = null;
     this.line = this._cssSheet._cssLogic.domUtils.getRuleLine(this._domRule);
     this.source = this._cssSheet.shortSource + ":" + this.line;
+    if (this.mediaText) {
+      this.source += " @media " + this.mediaText;
+    }
     this.href = this._cssSheet.href;
     this.contentRule = this._cssSheet.contentSheet;
   } else if (aElement) {
     this._selectors = [ new CssSelector(this, "@element.style") ];
     this.line = -1;
     this.source = CssLogic.l10n("rule.sourceElement");
     this.href = "#";
     this.contentRule = true;
     this.sourceElement = aElement;
   }
 }
 
 CssRule.prototype = {
   _passId: null,
 
+  mediaText: "",
+
+  get isMediaRule()
+  {
+    return !!this.mediaText;
+  },
+
   /**
    * Check if the parent stylesheet is allowed by the CssLogic.sourceFilter.
    *
    * @return {boolean} true if the parent stylesheet is allowed by the current
    * sourceFilter, or false otherwise.
    */
   get sheetAllowed()
   {
--- a/browser/devtools/styleinspector/CssRuleView.jsm
+++ b/browser/devtools/styleinspector/CssRuleView.jsm
@@ -351,20 +351,30 @@ ElementStyle.prototype = {
  */
 function Rule(aElementStyle, aOptions)
 {
   this.elementStyle = aElementStyle;
   this.domRule = aOptions.domRule || null;
   this.style = aOptions.style || this.domRule.style;
   this.selectorText = aOptions.selectorText || this.domRule.selectorText;
   this.inherited = aOptions.inherited || null;
+
+  if (this.domRule) {
+    let parentRule = this.domRule.parentRule;
+    if (parentRule && parentRule.type == Ci.nsIDOMCSSRule.MEDIA_RULE) {
+      this.mediaText = parentRule.media.mediaText;
+    }
+  }
+
   this._getTextProperties();
 }
 
 Rule.prototype = {
+  mediaText: "",
+
   get title()
   {
     if (this._title) {
       return this._title;
     }
     this._title = CssLogic.shortSource(this.sheet);
     if (this.domRule) {
       this._title += ":" + this.ruleLine;
@@ -375,17 +385,17 @@ Rule.prototype = {
       if (this.inherited.id) {
         eltText += "#" + this.inherited.id;
       }
       let args = [eltText, this._title];
       this._title = CssLogic._strings.formatStringFromName("rule.inheritedSource",
                                                            args, args.length);
     }
 
-    return this._title;
+    return this._title + (this.mediaText ? " @media " + this.mediaText : "");
   },
 
   /**
    * The rule's stylesheet.
    */
   get sheet()
   {
     return this.domRule ? this.domRule.parentStyleSheet : null;
@@ -717,18 +727,36 @@ CssRuleView.prototype = {
     }
 
     this._elementStyle = new ElementStyle(aElement, this.store);
     this._elementStyle.onChanged = function() {
       this._changed();
     }.bind(this);
 
     this._createEditors();
+
+    // When creating a new property, we fake the normal property
+    // editor behavior (focusing a property's value after entering its
+    // name) by responding to the name's blur event, creating the
+    // value editor, and grabbing focus to the value editor.  But if
+    // focus has already moved to another document, we won't be able
+    // to move focus to the new editor.
+    // Create a focusable item at the end of the editors to catch these
+    // cases.
+    this._focusBackstop = createChild(this.element, "div", {
+      tabindex: 0,
+    });
+    this._backstopHandler = function() {
+      // If this item is actually focused long enough to get the focus
+      // event, allow focus to move on out of this document.
+      moveFocus(this.doc.defaultView, FOCUS_FORWARD);
+    }.bind(this);
+    this._focusBackstop.addEventListener("focus", this._backstopHandler, false);
   },
-  
+
   /**
    * Update the rules for the currently highlighted element.
    */
   nodeChanged: function CssRuleView_nodeChanged()
   {
     this._clearRules();
     this._elementStyle.populate();
     this._createEditors();
@@ -747,16 +775,22 @@ CssRuleView.prototype = {
   /**
    * Clear the rule view.
    */
   clear: function CssRuleView_clear()
   {
     this._clearRules();
     this._viewedElement = null;
     this._elementStyle = null;
+
+    if (this._focusBackstop) {
+      this._focusBackstop.removeEventListener("focus", this._backstopHandler, false);
+      this._backstopHandler = null;
+      this._focusBackstop = null;
+    }
   },
 
   /**
    * Called when the user has made changes to the ElementStyle.
    * Emits an event that clients can listen to.
    */
   _changed: function CssRuleView_changed()
   {
@@ -830,17 +864,16 @@ RuleEditor.prototype = {
 
     let selectors = createChild(header, "span", {
       class: "ruleview-selector",
       textContent: this.rule.selectorText
     });
 
     this.openBrace = createChild(header, "span", {
       class: "ruleview-ruleopen",
-      tabindex: "0",
       textContent: " {"
     });
 
     this.openBrace.addEventListener("click", function() {
       this.newProperty();
     }.bind(this), true);
 
     this.propertyList = createChild(code, "ul", {
--- a/browser/devtools/styleinspector/test/Makefile.in
+++ b/browser/devtools/styleinspector/test/Makefile.in
@@ -55,29 +55,33 @@ include $(topsrcdir)/config/rules.mk
   browser_bug_692400_element_style.js \
   browser_csslogic_inherited.js \
   browser_ruleview_editor.js \
   browser_ruleview_editor_changedvalues.js \
   browser_ruleview_inherit.js \
   browser_ruleview_manipulation.js \
   browser_ruleview_override.js \
   browser_ruleview_ui.js \
+  browser_ruleview_focus.js \
   browser_bug705707_is_content_stylesheet.js \
+  browser_bug722196_property_view_media_queries.js \
+  browser_bug722196_rule_view_media_queries.js \
   browser_bug_592743_specificity.js \
   head.js \
   $(NULL)
 
 _BROWSER_TEST_PAGES = \
   browser_bug683672.html \
   browser_bug705707_is_content_stylesheet.html \
   browser_bug705707_is_content_stylesheet_imported.css \
   browser_bug705707_is_content_stylesheet_imported2.css \
   browser_bug705707_is_content_stylesheet_linked.css \
   browser_bug705707_is_content_stylesheet_script.css \
   browser_bug705707_is_content_stylesheet.xul \
   browser_bug705707_is_content_stylesheet_xul.css \
+  browser_bug722196_identify_media_queries.html \
   $(NULL)
 
 libs:: $(_BROWSER_TEST_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
 
 libs:: $(_BROWSER_TEST_PAGES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug722196_identify_media_queries.html
@@ -0,0 +1,24 @@
+<html>
+<head>
+  <title>test</title>
+  <script type="application/javascript;version=1.7">
+
+  </script>
+  <style>
+    div {
+      width: 1000px;
+      height: 100px;
+      background-color: #f00;
+    }
+
+    @media screen and (min-width: 1px) {
+      div {
+        width: 200px;
+      }
+    }
+  </style>
+</head>
+<body>
+<div></div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js
@@ -0,0 +1,68 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that we correctly display appropriate media query titles in the
+// property view.
+
+let doc;
+let stylePanel;
+
+const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
+  "test/browser_bug722196_identify_media_queries.html";
+
+function test()
+{
+  waitForExplicitFinish();
+  addTab(TEST_URI);
+  browser.addEventListener("load", docLoaded, true);
+}
+
+function docLoaded()
+{
+  browser.removeEventListener("load", docLoaded, true);
+  doc = content.document;
+  stylePanel = new StyleInspector(window);
+  Services.obs.addObserver(checkSheets, "StyleInspector-opened", false);
+  stylePanel.createPanel(false, function() {
+    stylePanel.open(doc.body);
+  });
+}
+
+function checkSheets()
+{
+  Services.obs.removeObserver(checkSheets, "StyleInspector-opened", false);
+
+  ok(stylePanel.isOpen(), "style inspector is open");
+
+  var div = doc.querySelector("div");
+  ok(div, "captain, we have the div");
+
+  stylePanel.selectNode(div);
+
+  let cssLogic = stylePanel.cssLogic;
+  cssLogic.processMatchedSelectors();
+
+  let _strings = Services.strings
+    .createBundle("chrome://browser/locale/devtools/styleinspector.properties");
+
+  let inline = _strings.GetStringFromName("rule.sourceInline");
+
+  let source1 = inline + ":8";
+  let source2 = inline + ":15 @media screen and (min-width: 1px)";
+  is(cssLogic._matchedRules[0][0].source, source1,
+    "rule.source gives correct output for rule 1");
+  is(cssLogic._matchedRules[1][0].source, source2,
+    "rule.source gives correct output for rule 2");
+
+  Services.obs.addObserver(finishUp, "StyleInspector-closed", false);
+  stylePanel.close();
+}
+
+function finishUp()
+{
+  Services.obs.removeObserver(finishUp, "StyleInspector-closed", false);
+  doc = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_bug722196_rule_view_media_queries.js
@@ -0,0 +1,55 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests that we correctly display appropriate media query titles in the
+// rule view.
+
+let tempScope = {};
+Cu.import("resource:///modules/devtools/CssRuleView.jsm", tempScope);
+let _ElementStyle = tempScope._ElementStyle;
+let doc;
+
+const TEST_URI = "http://example.com/browser/browser/devtools/styleinspector/" +
+  "test/browser_bug722196_identify_media_queries.html";
+
+function test()
+{
+  waitForExplicitFinish();
+  addTab(TEST_URI);
+  browser.addEventListener("load", docLoaded, true);
+}
+
+function docLoaded()
+{
+  browser.removeEventListener("load", docLoaded, true);
+  doc = content.document;
+  checkSheets();
+}
+
+function checkSheets()
+{
+  var div = doc.querySelector("div");
+  ok(div, "captain, we have the div");
+
+  let elementStyle = new _ElementStyle(div);
+  is(elementStyle.rules.length, 3, "Should have 3 rules.");
+
+  let _strings = Services.strings
+    .createBundle("chrome://browser/locale/devtools/styleinspector.properties");
+
+  let inline = _strings.GetStringFromName("rule.sourceInline");
+
+  is(elementStyle.rules[0].title, inline, "check rule 0 title");
+  is(elementStyle.rules[1].title, inline +
+    ":15 @media screen and (min-width: 1px)", "check rule 1 title");
+  is(elementStyle.rules[2].title, inline + ":8", "check rule 2 title");
+  finishUp();
+}
+
+function finishUp()
+{
+  doc = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_focus.js
@@ -0,0 +1,106 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test that focus doesn't leave the style editor when adding a property
+// (bug 719916)
+
+let doc;
+let stylePanel;
+
+function waitForRuleView(aCallback)
+{
+  if (InspectorUI.ruleView) {
+    aCallback();
+    return;
+  }
+
+  let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
+  ruleViewFrame.addEventListener("load", function(evt) {
+    ruleViewFrame.removeEventListener(evt.type, arguments.callee, true);
+    executeSoon(function() {
+      aCallback();
+    });
+  }, true);
+}
+
+function waitForEditorFocus(aParent, aCallback)
+{
+  aParent.addEventListener("focus", function onFocus(evt) {
+    if (evt.target.inplaceEditor) {
+      aParent.removeEventListener("focus", onFocus, true);
+      let editor = evt.target.inplaceEditor;
+      executeSoon(function() {
+        aCallback(editor);
+      });
+    }
+  }, true);
+}
+
+function openRuleView()
+{
+  Services.obs.addObserver(function onOpened() {
+    Services.obs.removeObserver(onOpened,
+    InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+
+    // Highlight a node.
+    let node = content.document.getElementsByTagName("h1")[0];
+    InspectorUI.inspectNode(node);
+    InspectorUI.stopInspecting();
+
+    // Open the rule view sidebar.
+    waitForRuleView(testFocus);
+
+    InspectorUI.showSidebar();
+    InspectorUI.ruleButton.click();
+
+    testFocus();
+  }, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
+  InspectorUI.openInspectorUI();
+}
+
+function testFocus()
+{
+  let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject);
+  let brace = ruleViewFrame.contentDocument.querySelectorAll(".ruleview-ruleclose")[0];
+  waitForEditorFocus(brace.parentNode, function onNewElement(aEditor) {
+    aEditor.input.value = "color";
+    waitForEditorFocus(brace.parentNode, function onEditingValue(aEditor) {
+      // If we actually get this focus we're ok.
+      ok(true, "We got focus.");
+      aEditor.input.value = "green";
+
+      // If we've retained focus, pressing return will start a new editor.
+      // If not, we'll wait here until we time out.
+      waitForEditorFocus(brace.parentNode, function onNewEditor(aEditor) {
+        aEditor.input.blur();
+        finishTest();
+      });
+      EventUtils.sendKey("return");
+    });
+    EventUtils.sendKey("return");
+  });
+
+  brace.focus();
+}
+
+function finishUp()
+{
+  doc = stylePanel = null;
+  gBrowser.removeCurrentTab();
+  finish();
+}
+
+function test()
+{
+  waitForExplicitFinish();
+  gBrowser.selectedTab = gBrowser.addTab();
+  gBrowser.selectedBrowser.addEventListener("load", function(evt) {
+    gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, true);
+    doc = content.document;
+    doc.title = "Rule View Test";
+    waitForFocus(openRuleView, content);
+  }, true);
+
+  content.location = "data:text/html,<h1>Some header text</h1>";
+}
--- a/browser/devtools/webconsole/GcliCommands.jsm
+++ b/browser/devtools/webconsole/GcliCommands.jsm
@@ -15,16 +15,17 @@
  *
  * 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):
  *   Joe Walker <jwalker@mozilla.com> (original author)
+ *   Mihai Sucan <mihai.sucan@gmail.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -155,18 +156,16 @@ gcli.addCommand({
    ],
    exec: function(args, context) {
      let hud = HUDService.getHudReferenceById(context.environment.hudId);
      let StyleEditor = hud.gcliterm.document.defaultView.StyleEditor;
      StyleEditor.openChrome(args.resource.element, args.line);
    }
 });
 
-let breakpoints = [];
-
 /**
  * 'break' command
  */
 gcli.addCommand({
   name: "break",
   description: gcli.lookup("breakDesc"),
   manual: gcli.lookup("breakManual")
 });
@@ -175,27 +174,35 @@ gcli.addCommand({
 /**
  * 'break list' command
  */
 gcli.addCommand({
   name: "break list",
   description: gcli.lookup("breaklistDesc"),
   returnType: "html",
   exec: function(args, context) {
-    if (breakpoints.length === 0) {
+    let win = HUDService.currentContext();
+    let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+    if (!dbg) {
+      return gcli.lookup("breakaddDebuggerStopped");
+    }
+    let breakpoints = dbg.breakpoints;
+
+    if (Object.keys(breakpoints).length === 0) {
       return gcli.lookup("breaklistNone");
     }
 
     let reply = gcli.lookup("breaklistIntro");
     reply += "<ol>";
-    breakpoints.forEach(function(breakpoint) {
+    for each (let breakpoint in breakpoints) {
       let text = gcli.lookupFormat("breaklistLineEntry",
-                                   [breakpoint.file, breakpoint.line]);
+                                   [breakpoint.location.url,
+                                    breakpoint.location.line]);
       reply += "<li>" + text + "</li>";
-    });
+    };
     reply += "</ol>";
     return reply;
   }
 });
 
 
 /**
  * 'break add' command
@@ -243,24 +250,21 @@ gcli.addCommand({
     args.type = "line";
     let win = HUDService.currentContext();
     let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
     if (!dbg) {
       return gcli.lookup("breakaddDebuggerStopped");
     }
     var promise = context.createPromise();
     let position = { url: args.file, line: args.line };
-    dbg.activeThread.setBreakpoint(position, function(aResponse, aBpClient) {
-      if (aResponse.error) {
-        promise.resolve(gcli.lookupFormat("breakaddFailed",
-                        [ aResponse.error ]));
+    dbg.addBreakpoint(position, function(aBreakpoint, aError) {
+      if (aError) {
+        promise.resolve(gcli.lookupFormat("breakaddFailed", [aError]));
         return;
       }
-      args.client = aBpClient;
-      breakpoints.push(args);
       promise.resolve(gcli.lookup("breakaddAdded"));
     });
     return promise;
   }
 });
 
 
 /**
@@ -270,28 +274,46 @@ gcli.addCommand({
   name: "break del",
   description: gcli.lookup("breakdelDesc"),
   params: [
     {
       name: "breakid",
       type: {
         name: "number",
         min: 0,
-        max: function() { return breakpoints.length - 1; }
+        max: function() {
+          let win = HUDService.currentContext();
+          let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+          if (!dbg) {
+            return gcli.lookup("breakaddDebuggerStopped");
+          }
+          return Object.keys(dbg.breakpoints).length - 1;
+        },
       },
       description: gcli.lookup("breakdelBreakidDesc")
     }
   ],
   returnType: "html",
   exec: function(args, context) {
-    let breakpoint = breakpoints.splice(args.breakid, 1)[0];
-    var promise = context.createPromise();
+    let win = HUDService.currentContext();
+    let dbg = win.DebuggerUI.getDebugger(win.gBrowser.selectedTab);
+    if (!dbg) {
+      return gcli.lookup("breakaddDebuggerStopped");
+    }
+
+    let breakpoints = dbg.breakpoints;
+    let id = Object.keys(dbg.breakpoints)[args.breakid];
+    if (!id || !(id in breakpoints)) {
+      return gcli.lookup("breakNotFound");
+    }
+
+    let promise = context.createPromise();
     try {
-      breakpoint.client.remove(function(aResponse) {
-                                 promise.resolve(gcli.lookup("breakdelRemoved"));
-                               });
+      dbg.removeBreakpoint(breakpoints[id], function() {
+        promise.resolve(gcli.lookup("breakdelRemoved"));
+      });
     } catch (ex) {
       // If the debugger has been closed already, don't scare the user.
       promise.resolve(gcli.lookup("breakdelRemoved"));
     }
     return promise;
   }
 });
--- a/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/gclicommands.properties
@@ -250,16 +250,20 @@ breakdelDesc=Remove a breakpoint
 # LOCALIZATION NOTE (breakdelBreakidDesc) A very short string used to describe
 # the function of the index parameter in the 'break del' command.
 breakdelBreakidDesc=Index of breakpoint
 
 # LOCALIZATION NOTE (breakdelRemoved) Used in the output of the 'break del'
 # command to explain that a breakpoint was removed.
 breakdelRemoved=Breakpoint removed
 
+# LOCALIZATION NOTE (breakNotFound) Used in the output of the 'break del'
+# command to explain that the breakpoint was not found.
+breakNotFound=Breakpoint was not found
+
 # LOCALIZATION NOTE (consolecloseDesc) A very short description of the
 # 'console close' command. This string is designed to be shown in a menu
 # alongside the command name, which is why it should be as short as possible.
 consolecloseDesc=Close the console
 
 # LOCALIZATION NOTE (editDesc) A very short description of the 'edit'
 # command. See editManual for a fuller description of what it does. This
 # string is designed to be shown in a menu alongside the command name, which
--- a/browser/makefiles.sh
+++ b/browser/makefiles.sh
@@ -134,16 +134,17 @@ if [ "$ENABLE_TESTS" ]; then
     browser/components/certerror/test/Makefile
     browser/components/dirprovider/tests/Makefile
     browser/components/preferences/tests/Makefile
     browser/components/search/test/Makefile
     browser/components/sessionstore/test/Makefile
     browser/components/shell/test/Makefile
     browser/components/feeds/test/Makefile
     browser/components/feeds/test/chrome/Makefile
+    browser/components/migration/tests/Makefile
     browser/components/places/tests/Makefile
     browser/components/places/tests/chrome/Makefile
     browser/components/places/tests/browser/Makefile
     browser/components/privatebrowsing/test/Makefile
     browser/components/privatebrowsing/test/browser/Makefile
     browser/components/tabview/test/Makefile
     browser/components/test/Makefile
     browser/components/thumbnails/test/Makefile
--- a/browser/modules/NewTabUtils.jsm
+++ b/browser/modules/NewTabUtils.jsm
@@ -116,18 +116,16 @@ let Storage = {
       // Reset to normal DOM storage.
       this.currentStorage = this.domStorage;
 
       // When switching back from private browsing we need to reset the
       // grid and re-read its values from the underlying storage. We don't
       // want any data from private browsing to show up.
       PinnedLinks.resetCache();
       BlockedLinks.resetCache();
-
-      Pages.update();
     }
   },
 
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference])
 };
 
 /**
@@ -183,37 +181,27 @@ PrivateBrowsingStorage.prototype = {
  */
 let AllPages = {
   /**
    * The array containing all active pages.
    */
   _pages: [],
 
   /**
-   * Tells whether we already added a preference observer.
-   */
-  _observing: false,
-
-  /**
    * Cached value that tells whether the New Tab Page feature is enabled.
    */
   _enabled: null,
 
   /**
    * Adds a page to the internal list of pages.
    * @param aPage The page to register.
    */
   register: function AllPages_register(aPage) {
     this._pages.push(aPage);
-
-    // Add the preference observer if we haven't already.
-    if (!this._observing) {
-      this._observing = true;
-      Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true);
-    }
+    this._addObserver();
   },
 
   /**
    * Removes a page from the internal list of pages.
    * @param aPage The page to unregister.
    */
   unregister: function AllPages_unregister(aPage) {
     let index = this._pages.indexOf(aPage);
@@ -234,16 +222,24 @@ let AllPages = {
    * Enables or disables the 'New Tab Page' feature.
    */
   set enabled(aEnabled) {
     if (this.enabled != aEnabled)
       Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, !!aEnabled);
   },
 
   /**
+   * Returns the number of registered New Tab Pages (i.e. the number of open
+   * about:newtab instances).
+   */
+  get length() {
+    return this._pages.length;
+  },
+
+  /**
    * Updates all currently active pages but the given one.
    * @param aExceptPage The page to exclude from updating.
    */
   update: function AllPages_update(aExceptPage) {
     this._pages.forEach(function (aPage) {
       if (aExceptPage != aPage)
         aPage.update();
     });
@@ -259,16 +255,25 @@ let AllPages = {
 
     let args = Array.slice(arguments);
 
     this._pages.forEach(function (aPage) {
       aPage.observe.apply(aPage, args);
     }, this);
   },
 
+  /**
+   * Adds a preference observer and turns itself into a no-op after the first
+   * invokation.
+   */
+  _addObserver: function AllPages_addObserver() {
+    Services.prefs.addObserver(PREF_NEWTAB_ENABLED, this, true);
+    this._addObserver = function () {};
+  },
+
   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
                                          Ci.nsISupportsWeakReference])
 };
 
 /**
  * Singleton that keeps track of all pinned links and their positions in the
  * grid.
  */
@@ -507,16 +512,18 @@ let Links = {
 
     if (this._links && !aForce) {
       executeCallbacks();
     } else {
       this._provider.getLinks(function (aLinks) {
         this._links = aLinks;
         executeCallbacks();
       }.bind(this));
+
+      this._addObserver();
     }
   },
 
   /**
    * Gets the current set of links contained in the grid.
    * @return The links in the grid.
    */
   getLinks: function Links_getLinks() {
@@ -539,17 +546,42 @@ let Links = {
     return pinnedLinks;
   },
 
   /**
    * Resets the links cache.
    */
   resetCache: function Links_resetCache() {
     this._links = [];
-  }
+  },
+
+  /**
+   * Implements the nsIObserver interface to get notified about browser history
+   * sanitization.
+   */
+  observe: function Links_observe(aSubject, aTopic, aData) {
+    // Make sure to update open about:newtab instances. If there are no opened
+    // pages we can just wait for the next new tab to populate the cache again.
+    if (AllPages.length && AllPages.enabled)
+      this.populateCache(function () { AllPages.update() }, true);
+    else
+      this._links = null;
+  },
+
+  /**
+   * Adds a sanitization observer and turns itself into a no-op after the first
+   * invokation.
+   */
+  _addObserver: function Links_addObserver() {
+    Services.obs.addObserver(this, "browser:purge-session-history", true);
+    this._addObserver = function () {};
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                         Ci.nsISupportsWeakReference])
 };
 
 /**
  * Singleton that provides the public API of this JSM.
  */
 let NewTabUtils = {
   /**
    * Resets the NewTabUtils module, its links and its storage.
--- a/browser/themes/gnomestripe/aboutCertError.css
+++ b/browser/themes/gnomestripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  background-position: right 0;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+body[dir="rtl"] .expander > button {
+  background-position: right center;
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
+
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
+}
rename from browser/themes/gnomestripe/section_collapsed-rtl.png
rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/gnomestripe/section_collapsed.png
rename to browser/themes/gnomestripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/gnomestripe/section_expanded.png
rename to browser/themes/gnomestripe/aboutCertError_sectionExpanded.png
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -194,17 +194,17 @@ menuitem.bookmark-item {
 }
 
 /* Bookmarks toolbar */
 #PlacesToolbarDropIndicator {
   list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
 }
 
 /* Bookmark items */
-.bookmark-item:not([container])  {
+.bookmark-item {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .bookmark-item[container] {
   list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
 }
 
 .bookmark-item[container][livemark] { 
--- a/browser/themes/gnomestripe/devtools/csshtmltree.css
+++ b/browser/themes/gnomestripe/devtools/csshtmltree.css
@@ -141,16 +141,17 @@
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
   direction: ltr;
   padding: 0;
   -moz-padding-start: 20px;
+  vertical-align: text-bottom;
 }
 
 .bestmatch {
   color: black;
 }
 .matched {
   text-decoration: line-through;
 }
deleted file mode 100644
--- a/browser/themes/gnomestripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/gnomestripe/jar.mn
+++ b/browser/themes/gnomestripe/jar.mn
@@ -1,40 +1,39 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
 % override chrome://global/skin/icons/warning-16.png moz-icon://stock/gtk-dialog-warning?size=menu
   skin/classic/browser/sanitizeDialog.css             (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css        (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
-  skin/classic/browser/aboutCertError.css             (aboutCertError.css)
+  skin/classic/browser/aboutCertError.css
+  skin/classic/browser/aboutCertError_sectionCollapsed.png
+  skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+  skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                    (browser.css)
 * skin/classic/browser/engineManager.css              (engineManager.css)
-  skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/Go-arrow.png
   skin/classic/browser/identity.png
   skin/classic/browser/Info.png
   skin/classic/browser/KUI-close.png
   skin/classic/browser/monitor.png
   skin/classic/browser/monitor_16-10.png
 * skin/classic/browser/pageInfo.css
   skin/classic/browser/pageInfo.png
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/searchbar.css                  (searchbar.css)
-  skin/classic/browser/section_collapsed.png
-  skin/classic/browser/section_collapsed-rtl.png
-  skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure.png
   skin/classic/browser/Security-broken.png
   skin/classic/browser/setDesktopBackground.css
   skin/classic/browser/Toolbar.png
   skin/classic/browser/Toolbar-small.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/feeds/feedIcon.png             (feeds/feedIcon.png)
   skin/classic/browser/feeds/feedIcon16.png           (feeds/feedIcon16.png)
--- a/browser/themes/pinstripe/aboutCertError.css
+++ b/browser/themes/pinstripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left 0 no-repeat;
-}
-
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
-  background-position: right 0;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+body[dir="rtl"] .expander > button {
+  background-position: right center;
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
+
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
+}
rename from browser/themes/pinstripe/section_collapsed-rtl.png
rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/pinstripe/section_collapsed.png
rename to browser/themes/pinstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/pinstripe/section_expanded.png
rename to browser/themes/pinstripe/aboutCertError_sectionExpanded.png
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -224,25 +224,30 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item > .toolbarbutton-icon[type="menu"] {
   -moz-margin-end: 5px;
 }
 
 .bookmark-item[container] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.query-item[container] {
-  list-style-image: url("chrome://browser/skin/places/history.png");
-}
-
-.bookmark-item[livemark] {
+.bookmark-item[container][livemark] {
   list-style-image: url("chrome://browser/skin/page-livemarks.png");
 }
 
-.bookmark-item[query] {
+.bookmark-item[container][livemark] .bookmark-item {
+  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited] {
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.bookmark-item[container][query] {
   list-style-image: url("chrome://browser/skin/places/query.png");
 }
 
 .bookmark-item[query][tagContainer] {
   list-style-image: url("chrome://browser/skin/places/tag.png");
 }
 
 .bookmark-item[query][dayContainer] {
@@ -252,56 +257,49 @@ toolbarbutton.bookmark-item > menupopup 
 .bookmark-item[query][hostContainer] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
 .bookmark-item[query][hostContainer][open] {
   list-style-image: url("chrome://global/skin/tree/folder.png");
 }
 
-.bookmark-item[livemark] .menuitem-iconic {
-  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
-  -moz-image-region: rect(0px, 16px, 16px, 0px);
-}
-
-.bookmark-item[livemark] .menuitem-iconic[visited] {
-  -moz-image-region: rect(0px, 32px, 16px, 16px);
-}
-
-.bookmark-item menuitem[openInTabs],
-.bookmark-item menuitem[siteURI] {
+/* Workaround for native menubar inheritance */
+.openintabs-menuitem,
+.openlivemarksite-menuitem,
+.livemarkstatus-menuitem {
   list-style-image: none;
 }
 
+.bookmark-item[cutting] > .toolbarbutton-icon,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
+  opacity: 0.5;
+}
+
+.bookmark-item[cutting] > .toolbarbutton-text,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
+  opacity: 0.7;
+}
+
 #wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
   background: url("chrome://browser/skin/places/bookmarksToolbar.png") no-repeat center;
 }
 
 .bookmarks-toolbar-customize {
   max-width: 15em !important;
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png") !important;
 }
 
 /* ----- BOOKMARK MENUS ----- */
 
 .bookmark-item > .menu-iconic-left > .menu-iconic-icon {
   width: 16px;
   height: 16px;
 }
 
-.bookmark-item[cutting] > .toolbarbutton-icon,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
-  opacity: 0.5;
-}
-
-.bookmark-item[cutting] > .toolbarbutton-text,
-.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
-  opacity: 0.7;
-}
-
 #bookmarksToolbarFolderMenu,
 #BMB_bookmarksToolbar {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
 }
 
 #BMB_unsortedBookmarks {
   list-style-image: url("chrome://browser/skin/places/unfiledBookmarks.png");
 }
--- a/browser/themes/pinstripe/devtools/csshtmltree.css
+++ b/browser/themes/pinstripe/devtools/csshtmltree.css
@@ -143,16 +143,17 @@
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
   direction: ltr;
   padding: 0;
   -moz-padding-start: 20px;
+  vertical-align: text-bottom;
 }
 
 .bestmatch {
   color: black;
 }
 .matched {
   text-decoration: line-through;
 }
deleted file mode 100644
--- a/browser/themes/pinstripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  left: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -1,22 +1,24 @@
 browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/
   skin/classic/browser/sanitizeDialog.css                   (sanitizeDialog.css)
 * skin/classic/browser/aboutPrivateBrowsing.css             (aboutPrivateBrowsing.css)
 * skin/classic/browser/aboutSessionRestore.css              (aboutSessionRestore.css)
   skin/classic/browser/aboutSessionRestore-window-icon.png
-  skin/classic/browser/aboutCertError.css                   (aboutCertError.css)
+  skin/classic/browser/aboutCertError.css
+  skin/classic/browser/aboutCertError_sectionCollapsed.png
+  skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+  skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
   skin/classic/browser/aboutSyncTabs.css
 #endif
   skin/classic/browser/actionicon-tab.png
 * skin/classic/browser/browser.css                          (browser.css)
 * skin/classic/browser/engineManager.css                    (engineManager.css)
-  skin/classic/browser/fullscreen-video.css
   skin/classic/browser/Geolocation-16.png
   skin/classic/browser/Geolocation-64.png
   skin/classic/browser/home.png
   skin/classic/browser/hud-style-check-box-checked.png
   skin/classic/browser/hud-style-check-box-empty.png
   skin/classic/browser/hud-style-dropmarker-double-arrows.png
   skin/classic/browser/hud-style-expander-closed.png
   skin/classic/browser/hud-style-expander-open.png
@@ -31,19 +33,16 @@ browser.jar:
   skin/classic/browser/page-livemarks.png
   skin/classic/browser/pageInfo.css
   skin/classic/browser/Privacy-16.png
   skin/classic/browser/Privacy-48.png
   skin/classic/browser/reload-stop-go.png
   skin/classic/browser/searchbar-dropmarker.png
   skin/classic/browser/searchbar.css
   skin/classic/browser/Search.png
-  skin/classic/browser/section_collapsed.png
-  skin/classic/browser/section_collapsed-rtl.png
-  skin/classic/browser/section_expanded.png
   skin/classic/browser/Secure-Glyph-White.png
   skin/classic/browser/keyhole-circle.png
   skin/classic/browser/Toolbar.png
   skin/classic/browser/toolbarbutton-dropmarker.png
   skin/classic/browser/urlbar-history-dropmarker.png
   skin/classic/browser/urlbar-arrow.png
   skin/classic/browser/urlbar-popup-blocked.png
   skin/classic/browser/feeds/subscribe.css                  (feeds/subscribe.css)
--- a/browser/themes/winstripe/aboutCertError.css
+++ b/browser/themes/winstripe/aboutCertError.css
@@ -81,26 +81,29 @@ body[dir="rtl"] #errorPageContainer {
 #errorTitle {
   -moz-margin-start: 80px;
 }
 
 #errorLongContent {
   -moz-margin-start: 80px;
 }
 
-#technicalContent > h2, #expertContent > h2 {
-  background : url("chrome://browser/skin/section_expanded.png") left center no-repeat;
+.expander > button {
+  -moz-padding-start: 20px;
+  -moz-margin-start: -20px;
+  background: url("chrome://browser/skin/aboutCertError_sectionExpanded.png") left center no-repeat;
+  border: none;
+  font: inherit;
+  color: inherit;
+  cursor: pointer;
 }
 
-body[dir="rtl"] #technicalContent > h2,
-body[dir="rtl"] #expertContent > h2 {
+body[dir="rtl"] .expander > button {
   background-position: right center;
 }
 
-#technicalContent[collapsed] > h2,
-#expertContent[collapsed] > h2{
-  background-image: url("chrome://browser/skin/section_collapsed.png");
+.expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed.png");
 }
 
-body[dir="rtl"] #technicalContent[collapsed] > h2,
-body[dir="rtl"] #expertContent[collapsed] > h2 {
-  background-image: url("chrome://browser/skin/section_collapsed-rtl.png");
+body[dir="rtl"] .expander[collapsed] > button {
+  background-image: url("chrome://browser/skin/aboutCertError_sectionCollapsed-rtl.png");
 }
rename from browser/themes/winstripe/section_collapsed-rtl.png
rename to browser/themes/winstripe/aboutCertError_sectionCollapsed-rtl.png
rename from browser/themes/winstripe/section_collapsed.png
rename to browser/themes/winstripe/aboutCertError_sectionCollapsed.png
rename from browser/themes/winstripe/section_expanded.png
rename to browser/themes/winstripe/aboutCertError_sectionExpanded.png
--- a/browser/themes/winstripe/devtools/csshtmltree.css
+++ b/browser/themes/winstripe/devtools/csshtmltree.css
@@ -141,16 +141,17 @@
   cursor: pointer;
 }
 
 /* This rule is necessary because Templater.jsm breaks LTR TDs in RTL docs */
 .rule-text {
   direction: ltr;
   padding: 0;
   -moz-padding-start: 20px;
+  vertical-align: text-bottom;
 }
 
 .bestmatch {
   color: black;
 }
 .matched {
   text-decoration: line-through;
 }
deleted file mode 100644
--- a/browser/themes/winstripe/fullscreen-video.css
+++ /dev/null
@@ -1,8 +0,0 @@
-#close {
-  position: absolute;
-  top: 0;
-  right: 0;
-  width: 32px;
-  height: 32px;
-  background: url(KUI-close.png) center center no-repeat;
-}
--- a/browser/themes/winstripe/jar.mn
+++ b/browser/themes/winstripe/jar.mn
@@ -2,26 +2,28 @@ browser.jar:
 % skin browser classic/1.0 %skin/classic/browser/ os=WINNT osversion<6
 % skin browser classic/1.0 %skin/classic/browser/ os!=WINNT
 # NOTE: If you add a new file here, you'll need to add it to the aero
 # section at the bottom of this file
         skin/classic/browser/sanitizeDialog.css                      (sanitizeDialog.css)
 *       skin/classic/browser/aboutPrivateBrowsing.css                (aboutPrivateBrowsing.css)
 *       skin/classic/browser/aboutSessionRestore.css                 (aboutSessionRestore.css)
         skin/classic/browser/aboutSessionRestore-window-icon.png     (preferences/application.png)
-        skin/classic/browser/aboutCertError.css                      (aboutCertError.css)
+        skin/classic/browser/aboutCertError.css
+        skin/classic/browser/aboutCertError_sectionCollapsed.png
+        skin/classic/browser/aboutCertError_sectionCollapsed-rtl.png
+        skin/classic/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/browser/aboutSyncTabs.css
 #endif
         skin/classic/browser/actionicon-tab.png
         skin/classic/browser/appmenu-icons.png
         skin/classic/browser/appmenu-dropmarker.png
 *       skin/classic/browser/browser.css                             (browser.css)
 *       skin/classic/browser/engineManager.css                       (engineManager.css)
-        skin/classic/browser/fullscreen-video.css
         skin/classic/browser/Geolocation-16.png
         skin/classic/browser/Geolocation-64.png
         skin/classic/browser/Info.png                                (Info.png)
         skin/classic/browser/identity.png                            (identity.png)
         skin/classic/browser/keyhole-forward-mask.svg
         skin/classic/browser/KUI-background.png
         skin/classic/browser/KUI-close.png
         skin/classic/browser/pageInfo.css
@@ -33,19 +35,16 @@ browser.jar:
         skin/classic/browser/reload-stop-go.png
         skin/classic/browser/Secure24.png                            (Secure24.png)
         skin/classic/browser/Toolbar.png                             (Toolbar.png)
         skin/classic/browser/Toolbar-inverted.png
         skin/classic/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/browser/toolbarbutton-dropdown-arrow-inverted.png
 *       skin/classic/browser/searchbar.css                           (searchbar.css)
         skin/classic/browser/searchbar-dropdown-arrow.png
-        skin/classic/browser/section_collapsed.png
-        skin/classic/browser/section_collapsed-rtl.png
-        skin/classic/browser/section_expanded.png
         skin/classic/browser/setDesktopBackground.css
         skin/classic/browser/menu-back.png                           (menu-back.png)
         skin/classic/browser/menu-forward.png                        (menu-forward.png)
         skin/classic/browser/monitor.png
         skin/classic/browser/monitor_16-10.png
         skin/classic/browser/urlbar-arrow.png
         skin/classic/browser/urlbar-popup-blocked.png
         skin/classic/browser/urlbar-history-dropmarker.png
@@ -172,26 +171,28 @@ browser.jar:
 
 #ifdef XP_WIN
 browser.jar:
 % skin browser classic/1.0 %skin/classic/aero/browser/ os=WINNT osversion>=6
         skin/classic/aero/browser/sanitizeDialog.css                       (sanitizeDialog.css)
 *       skin/classic/aero/browser/aboutPrivateBrowsing.css           (aboutPrivateBrowsing.css)
 *       skin/classic/aero/browser/aboutSessionRestore.css            (aboutSessionRestore.css)
         skin/classic/aero/browser/aboutSessionRestore-window-icon.png (aboutSessionRestore-window-icon-aero.png)
-        skin/classic/aero/browser/aboutCertError.css                 (aboutCertError.css)
+        skin/classic/aero/browser/aboutCertError.css
+        skin/classic/aero/browser/aboutCertError_sectionCollapsed.png
+        skin/classic/aero/browser/aboutCertError_sectionCollapsed-rtl.png
+        skin/classic/aero/browser/aboutCertError_sectionExpanded.png
 #ifdef MOZ_SERVICES_SYNC
         skin/classic/aero/browser/aboutSyncTabs.css
 #endif
         skin/classic/aero/browser/actionicon-tab.png                 (actionicon-tab.png)
         skin/classic/aero/browser/appmenu-dropmarker.png
         skin/classic/aero/browser/appmenu-icons.png
 *       skin/classic/aero/browser/browser.css                        (browser-aero.css)
 *       skin/classic/aero/browser/engineManager.css                  (engineManager.css)
-        skin/classic/aero/browser/fullscreen-video.css
         skin/classic/aero/browser/Geolocation-16.png
         skin/classic/aero/browser/Geolocation-64.png
         skin/classic/aero/browser/Info.png                           (Info-aero.png)
         skin/classic/aero/browser/identity.png                       (identity-aero.png)
         skin/classic/aero/browser/keyhole-forward-mask.svg
         skin/classic/aero/browser/KUI-background.png
         skin/classic/aero/browser/KUI-close.png
         skin/classic/aero/browser/pageInfo.css
@@ -203,19 +204,16 @@ browser.jar:
         skin/classic/aero/browser/reload-stop-go.png
         skin/classic/aero/browser/Secure24.png                       (Secure24-aero.png)
         skin/classic/aero/browser/Toolbar.png
         skin/classic/aero/browser/Toolbar-inverted.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow.png
         skin/classic/aero/browser/toolbarbutton-dropdown-arrow-inverted.png
 *       skin/classic/aero/browser/searchbar.css                      (searchbar.css)
         skin/classic/aero/browser/searchbar-dropdown-arrow.png       (searchbar-dropdown-arrow-aero.png)
-        skin/classic/aero/browser/section_collapsed.png
-        skin/classic/aero/browser/section_collapsed-rtl.png
-        skin/classic/aero/browser/section_expanded.png
         skin/classic/aero/browser/setDesktopBackground.css
         skin/classic/aero/browser/menu-back.png                      (menu-back-aero.png)
         skin/classic/aero/browser/menu-forward.png                   (menu-forward-aero.png)
         skin/classic/aero/browser/monitor.png
         skin/classic/aero/browser/monitor_16-10.png
         skin/classic/aero/browser/urlbar-arrow.png
         skin/classic/aero/browser/urlbar-popup-blocked.png
         skin/classic/aero/browser/urlbar-history-dropmarker.png
rename from build/unix/check_debug_ranges.py
rename to build/autoconf/check_debug_ranges.py
--- a/build/autoconf/compiler-opts.m4
+++ b/build/autoconf/compiler-opts.m4
@@ -4,10 +4,81 @@ AC_DEFUN([MOZ_COMPILER_OPTS],
 [
 if test "$CLANG_CXX"; then
     ## We disable return-type-c-linkage because jsval is defined as a C++ type but is
     ## returned by C functions. This is possible because we use knowledge about the ABI
     ## to typedef it to a C type with the same layout when the headers are included
     ## from C.
     _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage"
 fi
+
+if test "$GNU_CC"; then
+    CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
+    CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
+fi
+
+dnl ========================================================
+dnl = Identical Code Folding
+dnl ========================================================
+
+MOZ_ARG_DISABLE_BOOL(icf,
+[  --disable-icf          Disable Identical Code Folding],
+    MOZ_DISABLE_ICF=1,
+    MOZ_DISABLE_ICF= )
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF"; then
+    AC_CACHE_CHECK([whether the linker supports Identical Code Folding],
+        LD_SUPPORTS_ICF,
+        [echo 'int foo() {return 42;}' \
+              'int bar() {return 42;}' \
+              'int main() {return foo() - bar();}' > conftest.${ac_ext}
+        # If the linker supports ICF, foo and bar symbols will have
+        # the same address
+        if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} &&
+           objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then
+            LD_SUPPORTS_ICF=yes
+        else
+            LD_SUPPORTS_ICF=no
+        fi
+        rm -rf conftest*])
+    if test "$LD_SUPPORTS_ICF" = yes; then
+        _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe"
+        LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections"
+        AC_TRY_LINK([], [],
+                    [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections],
+                    [LD_PRINT_ICF_SECTIONS=])
+        AC_SUBST([LD_PRINT_ICF_SECTIONS])
+        LDFLAGS="$_SAVE_LDFLAGS"
+    fi
+fi
+
+dnl ========================================================
+dnl = Automatically remove dead symbols
+dnl ========================================================
+
+if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -n "$MOZ_DEBUG_FLAGS"; then
+   dnl See bug 670659
+   AC_CACHE_CHECK([whether removing dead symbols breaks debugging],
+       GC_SECTIONS_BREAKS_DEBUG_RANGES,
+       [echo 'int foo() {return 42;}' \
+             'int bar() {return 1;}' \
+             'int main() {return foo();}' > conftest.${ac_ext}
+        if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) &&
+           AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) &&
+           test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then
+            if test "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest.${ac_objext} conftest.${ac_ext}`" = \
+                    "`$PYTHON "$_topsrcdir"/build/autoconf/check_debug_ranges.py conftest${ac_exeext} conftest.${ac_ext}`"; then
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=no
+            else
+                GC_SECTIONS_BREAKS_DEBUG_RANGES=yes
+            fi
+        else
+             dnl We really don't expect to get here, but just in case
+             GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way"
+        fi
+        rm -rf conftest*])
+    if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then
+        DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections"
+    fi
+fi
+
 ])
-
new file mode 100644
--- /dev/null
+++ b/build/autoconf/expandlibs.m4
@@ -0,0 +1,56 @@
+AC_DEFUN([MOZ_EXPAND_LIBS],
+[
+dnl ========================================================
+dnl =
+dnl = Check what kind of list files are supported by the
+dnl = linker
+dnl =
+dnl ========================================================
+
+AC_CACHE_CHECK(what kind of list files are supported by the linker,
+    EXPAND_LIBS_LIST_STYLE,
+    [echo "int main() {return 0;}" > conftest.${ac_ext}
+     if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then
+         echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list
+         if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+             EXPAND_LIBS_LIST_STYLE=linkerscript
+         else
+             echo "conftest.${OBJ_SUFFIX}" > conftest.list
+             if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then
+                 EXPAND_LIBS_LIST_STYLE=list
+             else
+                 EXPAND_LIBS_LIST_STYLE=none
+             fi
+         fi
+     else
+         dnl We really don't expect to get here, but just in case
+         AC_ERROR([couldn't compile a simple C file])
+     fi
+     rm -rf conftest*])
+
+LIBS_DESC_SUFFIX=desc
+AC_SUBST(LIBS_DESC_SUFFIX)
+AC_SUBST(EXPAND_LIBS_LIST_STYLE)
+
+if test "$GCC_USE_GNU_LD"; then
+    AC_CACHE_CHECK(what kind of ordering can be done with the linker,
+        EXPAND_LIBS_ORDER_STYLE,
+        [> conftest.order
+         _SAVE_LDFLAGS="$LDFLAGS"
+         LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order"
+         AC_TRY_LINK([], [],
+             EXPAND_LIBS_ORDER_STYLE=section-ordering-file,
+             EXPAND_LIBS_ORDER_STYLE=)
+         LDFLAGS="$_SAVE_LDFLAGS"
+         if test -z "$EXPAND_LIBS_ORDER_STYLE"; then
+             if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then
+                 EXPAND_LIBS_ORDER_STYLE=linkerscript
+             else
+                 EXPAND_LIBS_ORDER_STYLE=none
+             fi
+             rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX}
+         fi])
+fi
+AC_SUBST(EXPAND_LIBS_ORDER_STYLE)
+
+])
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -390,16 +390,17 @@ user_pref("camino.warn_when_closing", fa
 // Make url-classifier updates so rare that they won't affect tests
 user_pref("urlclassifier.updateinterval", 172800);
 // Point the url-classifier to the local testing server for fast failures
 user_pref("browser.safebrowsing.provider.0.gethashURL", "http://%(server)s/safebrowsing-dummy/gethash");
 user_pref("browser.safebrowsing.provider.0.keyURL", "http://%(server)s/safebrowsing-dummy/newkey");
 user_pref("browser.safebrowsing.provider.0.updateURL", "http://%(server)s/safebrowsing-dummy/update");
 // Point update checks to the local testing server for fast failures
 user_pref("extensions.update.url", "http://%(server)s/extensions-dummy/updateURL");
+user_pref("extensions.update.background.url", "http://%(server)s/extensions-dummy/updateBackgroundURL");
 user_pref("extensions.blocklist.url", "http://%(server)s/extensions-dummy/blocklistURL");
 user_pref("extensions.hotfix.url", "http://%(server)s/extensions-dummy/hotfixURL");
 // Make sure opening about:addons won't hit the network
 user_pref("extensions.webservice.discoverURL", "http://%(server)s/extensions-dummy/discoveryURL");
 // Make sure AddonRepository won't hit the network
 user_pref("extensions.getAddons.maxResults", 0);
 user_pref("extensions.getAddons.get.url", "http://%(server)s/extensions-dummy/repositoryGetURL");
 user_pref("extensions.getAddons.getWithPerformance.url", "http://%(server)s/extensions-dummy/repositoryGetWithPerformanceURL");
@@ -732,18 +733,21 @@ user_pref("camino.use_system_proxy_setti
         self.log.info("Failed to read image from %s", imgoutput)
 
     import base64
     encoded = base64.b64encode(image)
     self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
 
   def killAndGetStack(self, proc, utilityPath, debuggerInfo):
     """Kill the process, preferrably in a way that gets us a stack trace."""
-    if not debuggerInfo and not self.haveDumpedScreen:
-      self.dumpScreen(utilityPath)
+    if not debuggerInfo:
+      if self.haveDumpedScreen:
+        self.log.info("Not taking screenshot here: see the one that was previously logged")
+      else:
+        self.dumpScreen(utilityPath)
 
     if self.CRASHREPORTER and not debuggerInfo:
       if self.UNIXISH:
         # ABRT will get picked up by Breakpad's signal handler
         os.kill(proc.pid, signal.SIGABRT)
         return
       elif self.IS_WIN32:
         # We should have a "crashinject" program in our utility path
@@ -790,18 +794,21 @@ user_pref("camino.use_system_proxy_setti
       while line != "" and not didTimeout:
         if logger:
           logger.log(line)
         if "TEST-START" in line and "|" in line:
           self.lastTestSeen = line.split("|")[1].strip()
         if stackFixerFunction:
           line = stackFixerFunction(line)
         self.log.info(line.rstrip().decode("UTF-8", "ignore"))
-        if not debuggerInfo and not self.haveDumpedScreen and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
-          self.dumpScreen(utilityPath)
+        if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
+          if self.haveDumpedScreen:
+            self.log.info("Not taking screenshot here: see the one that was previously logged")
+          else:
+            self.dumpScreen(utilityPath)
 
         (line, didTimeout) = self.readWithTimeout(logsource, timeout)
         if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
           # Kill the application, but continue reading from stack fixer so as not to deadlock on stackFixerProcess.wait().
           hitMaxTime = True
           self.log.info("TEST-UNEXPECTED-FAIL | %s | application ran for longer than allowed maximum time of %d seconds", self.lastTestSeen, int(maxTime))
           self.killAndGetStack(proc, utilityPath, debuggerInfo)
       if didTimeout:
--- a/build/automationutils.py
+++ b/build/automationutils.py
@@ -454,17 +454,17 @@ def wrapCommand(cmd):
   return cmd
 
 class ShutdownLeakLogger(object):
   """
   Parses the mochitest run log when running a debug build, assigns all leaked
   DOM windows (that are still around after test suite shutdown, despite running
   the GC) to the tests that created them and prints leak statistics.
   """
-  MAX_LEAK_COUNT = 130
+  MAX_LEAK_COUNT = 120
 
   def __init__(self, logger):
     self.logger = logger
     self.tests = []
     self.leakedWindows = {}
     self.leakedDocShells = set()
     self.currentTest = None
     self.seenShutdown = False
--- a/build/mobile/robocop/Driver.java.in
+++ b/build/mobile/robocop/Driver.java.in
@@ -72,10 +72,10 @@ public interface Driver {
     void startCheckerboardRecording();
     float stopCheckerboardRecording();
 
     /**
      * Get a copy of the painted content region.
      * @return A 2-D array of pixels (indexed by y, then x). The pixels
      * are in ARGB-8888 format.
      */
-    int[][] getPaintedSurface();
+    PaintedSurface getPaintedSurface();
 }
--- a/build/mobile/robocop/FennecNativeDriver.java.in
+++ b/build/mobile/robocop/FennecNativeDriver.java.in
@@ -43,16 +43,18 @@ import java.io.BufferedReader;
 import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
 import java.nio.IntBuffer;
 import java.util.HashMap;
 import java.util.List;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
 
 import java.lang.Class;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Proxy;
 import java.lang.reflect.InvocationHandler;
 import java.lang.Long;
 
@@ -286,17 +288,17 @@ public class FennecNativeDriver implemen
         for (View v : mSolo.getCurrentViews()) {
             if (v instanceof GLSurfaceView) {
                 return (GLSurfaceView)v;
             }
         }
         return null;
     }
 
-    public int[][] getPaintedSurface() {
+    public PaintedSurface getPaintedSurface() {
         GLSurfaceView view = getSurfaceView();
         if (view == null) {
             return null;
         }
         IntBuffer pixelBuffer;
         try {
             pixelBuffer = (IntBuffer)_getPixels.invoke(view);
         } catch (Exception e) {
@@ -304,24 +306,44 @@ public class FennecNativeDriver implemen
             return null;
         }
 
         // now we need to (1) flip the image, because GL likes to do things up-side-down,
         // and (2) rearrange the bits from AGBR-8888 to ARGB-8888.
         int w = view.getWidth();
         int h = view.getHeight();
         pixelBuffer.position(0);
-        int[][] pixels = new int[h][w];
-        for (int y = h - 1; y >= 0; y--) {
-            for (int x = 0; x < w; x++) {
-                int agbr = pixelBuffer.get();
-                pixels[y][x] = (agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000);
+        String mapFile = "/mnt/sdcard/pixels.map";
+
+        FileOutputStream fos = null;
+        DataOutputStream dos = null;
+        try {
+            fos = new FileOutputStream(mapFile);
+            dos = new DataOutputStream(fos);
+
+            for (int y = h - 1; y >= 0; y--) {
+                for (int x = 0; x < w; x++) {
+                    int agbr = pixelBuffer.get();
+                    dos.writeInt((agbr & 0xFF00FF00) | ((agbr >> 16) & 0x000000FF) | ((agbr << 16) & 0x00FF0000));
+                }
+            }
+            return new PaintedSurface(mapFile, w, h);
+        } catch (IOException e) {
+            throw new RoboCopException("exception with pixel writer on file: " + mapFile);
+        } finally {
+            try {
+                if (dos != null && fos != null) {
+                    dos.flush();
+                    dos.close();
+                    fos.close();
+                }
+            } catch (IOException e) {
+                throw new RoboCopException("exception closing pixel writer on file: " + mapFile);
             }
         }
-        return pixels;
     }
 
     public int mHeight=0;
     public int mScrollHeight=0;
     public int mPageHeight=10;
 
     class scrollHandler implements InvocationHandler {
         public scrollHandler(){};
--- a/build/mobile/robocop/Makefile.in
+++ b/build/mobile/robocop/Makefile.in
@@ -56,21 +56,25 @@ JAVAFILES = \
   Driver.java \
   Element.java \
   FennecNativeActions.java \
   FennecMochitestAssert.java \
   FennecTalosAssert.java \
   FennecNativeDriver.java \
   FennecNativeElement.java \
   RoboCopException.java \
+  PaintedSurface.java \
   $(NULL)
 
 _JAVA_TESTS = $(patsubst $(TESTPATH)/%.in,%,$(wildcard $(TESTPATH)/*.java.in))
 
-_TEST_FILES = $(wildcard $(TESTPATH)/*.html)
+_TEST_FILES = \
+  $(wildcard $(TESTPATH)/*.html) \
+  $(wildcard $(TESTPATH)/*.sjs) \
+  $(NULL)
 
 _ROBOCOP_TOOLS = \
   $(TESTPATH)/robocop.ini \
   parse_ids.py \
   $(NULL)
 
 GARBAGE += \
   AndroidManifest.xml \
new file mode 100644
--- /dev/null
+++ b/build/mobile/robocop/PaintedSurface.java.in
@@ -0,0 +1,62 @@
+#filter substitution
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package @ANDROID_PACKAGE_NAME@;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.nio.MappedByteBuffer;
+import java.nio.channels.FileChannel;
+
+public class PaintedSurface {
+    private String mFileName = null;
+    private int mWidth = -1;
+    private int mHeight = -1;
+    private MappedByteBuffer mPixelBuffer = null;
+
+    public PaintedSurface(String filename, int width, int height) {
+        mFileName = filename;
+        mWidth = width;
+        mHeight = height;
+        
+        try {
+            File f = new File(filename);
+            int pixelSize = (int)f.length();
+            
+            FileInputStream pixelFile = new FileInputStream(filename);
+            mPixelBuffer = pixelFile.getChannel().map(FileChannel.MapMode.READ_ONLY, 0, pixelSize);
+        } catch (java.io.FileNotFoundException e) {
+            e.printStackTrace();
+        } catch (java.io.IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public final int getPixelAt(int x, int y) {
+        if (mPixelBuffer == null) {
+            throw new RoboCopException("Trying to access PaintedSurface with no active PixelBuffer");
+        }
+
+        if (x >= mWidth || x < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid x value");
+        }
+
+        if (y >= mHeight || y < 0) {
+            throw new RoboCopException("Trying to access PaintedSurface with invalid y value");
+        }
+
+        // The rows are reversed so row 0 is at the end and we start with the last row.
+        // This is why we do mHeight-y;
+        int index = (x + ((mHeight - y - 1) * mWidth)) * 4;
+        int b1 = mPixelBuffer.get(index) & 0xFF;
+        int b2 = mPixelBuffer.get(index + 1) & 0xFF;
+        int b3 = mPixelBuffer.get(index + 2) & 0xFF;
+        int b4 = mPixelBuffer.get(index + 3) & 0xFF;
+        int value = (b1 << 24) + (b2 << 16) + (b3 << 8) + (b4 << 0);
+        return value;
+    }
+}
+
--- a/caps/idl/nsIPrincipal.idl
+++ b/caps/idl/nsIPrincipal.idl
@@ -47,17 +47,17 @@ struct JSPrincipals;
 %}
 
 interface nsIURI;
 interface nsIContentSecurityPolicy;
 
 [ptr] native JSContext(JSContext);
 [ptr] native JSPrincipals(JSPrincipals);
 
-[scriptable, uuid(1f83b0e0-6b63-4bdc-a50a-b9afe256bd25)]
+[scriptable, uuid(f8c4c89a-d726-421b-8415-3e34b241175b)]
 interface nsIPrincipal : nsISerializable
 {
     /**
      * Values of capabilities for each principal. Order is
      * significant: if an operation is performed on a set
      * of capabilities, the minimum is computed.
      */
     const short ENABLE_DENIED                = 1;
@@ -92,22 +92,16 @@ interface nsIPrincipal : nsISerializable
     boolean equalsIgnoringDomain(in nsIPrincipal other);
 
     /**
      * Returns a hash value for the principal.
      */
     [noscript] readonly attribute unsigned long hashValue;
 
     /**
-     * Returns the JS equivalent of the principal.
-     * @see JSPrincipals.h
-     */
-    [noscript] JSPrincipals getJSPrincipals(in JSContext cx);
-
-    /**
      * The domain security policy of the principal.
      */
     // XXXcaa should this be here?  The script security manager is the only
     // thing that should care about this.  Wouldn't storing this data in one
     // of the hashtables in nsScriptSecurityManager be better?
     // XXXbz why is this writable?  Who should have write access to this?  What
     // happens if this principal is in our hashtable and we pass it out of the
     // security manager and someone writes to this field?  Especially if they
--- a/caps/include/nsJSPrincipals.h
+++ b/caps/include/nsJSPrincipals.h
@@ -38,19 +38,52 @@
 
 #ifndef nsJSPrincipals_h__
 #define nsJSPrincipals_h__
 #include "jsapi.h"
 #include "nsIPrincipal.h"
 
 class nsCString;
 
-struct nsJSPrincipals : JSPrincipals
+struct nsJSPrincipals : nsIPrincipal, JSPrincipals
 {
-  static nsresult Startup();
-  nsJSPrincipals();
-  nsresult Init(nsIPrincipal* aPrincipal, const nsCString& aCodebase);
-  ~nsJSPrincipals(void);
+  static JSBool Subsume(JSPrincipals *jsprin, JSPrincipals *other);
+  static void Destroy(JSPrincipals *jsprin);
+  static JSBool Transcode(JSXDRState *xdr, JSPrincipals **jsprinp);
+
+  /*
+   * Get a weak reference to nsIPrincipal associated with the given JS
+   * principal.
+   */
+  static nsJSPrincipals* get(JSPrincipals *principals) {
+    nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principals);
+    MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
+    return self;
+  }
+  
+  static nsJSPrincipals* get(nsIPrincipal *principal) {
+    nsJSPrincipals *self = static_cast<nsJSPrincipals *>(principal);
+    MOZ_ASSERT_IF(self, self->debugToken == DEBUG_TOKEN);
+    return self;
+  }
 
-  nsIPrincipal *nsIPrincipalPtr; // [WEAK] it owns us.
+  nsJSPrincipals() {
+    refcount = 0;
+    setDebugToken(DEBUG_TOKEN);
+  }
+
+  virtual ~nsJSPrincipals() {
+    setDebugToken(0);
+  }
+
+  /**
+   * Return a string that can be used as JS script filename in error reports.
+   */
+  virtual void GetScriptLocation(nsACString &aStr) = 0;
+
+#ifdef DEBUG
+  virtual void dumpImpl() = 0;
+#endif
+
+  static const uint32_t DEBUG_TOKEN = 0x0bf41760;
 };
 
 #endif /* nsJSPrincipals_h__ */
--- a/caps/include/nsNullPrincipal.h
+++ b/caps/include/nsNullPrincipal.h
@@ -54,33 +54,38 @@ class nsIURI;
 #define NS_NULLPRINCIPAL_CLASSNAME "nullprincipal"
 #define NS_NULLPRINCIPAL_CID \
 { 0xdd156d62, 0xd26f, 0x4441, \
  { 0x9c, 0xdb, 0xe8, 0xf0, 0x91, 0x07, 0xc2, 0x73 } }
 #define NS_NULLPRINCIPAL_CONTRACTID "@mozilla.org/nullprincipal;1"
 
 #define NS_NULLPRINCIPAL_SCHEME "moz-nullprincipal"
 
-class nsNullPrincipal : public nsIPrincipal
+class nsNullPrincipal : public nsJSPrincipals
 {
 public:
   nsNullPrincipal();
   
-  // Our refcount is managed by mJSPrincipals.  Use this macro to avoid an
+  // Our refcount is managed by nsJSPrincipals.  Use this macro to avoid an
   // extra refcount member.
 
   // FIXME: bug 327245 -- I sorta wish there were a clean way to share the
-  // mJSPrincipals munging code between the various principal classes without
+  // nsJSPrincipals munging code between the various principal classes without
   // giving up the NS_DECL_NSIPRINCIPAL goodness.
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIPRINCIPAL
   NS_DECL_NSISERIALIZABLE
 
   nsresult Init();
 
-protected:
+  virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
+
+#ifdef DEBUG
+  virtual void dumpImpl() MOZ_OVERRIDE;
+#endif 
+
+ protected:
   virtual ~nsNullPrincipal();
 
-  nsJSPrincipals mJSPrincipals;
   nsCOMPtr<nsIURI> mURI;
 };
 
 #endif // nsNullPrincipal_h__
--- a/caps/include/nsPrincipal.h
+++ b/caps/include/nsPrincipal.h
@@ -46,26 +46,26 @@
 #include "nsHashtable.h"
 #include "nsJSPrincipals.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 
 class nsIObjectInputStream;
 class nsIObjectOutputStream;
 
-class nsPrincipal : public nsIPrincipal
+class nsPrincipal : public nsJSPrincipals
 {
 public:
   nsPrincipal();
 
 protected:
   virtual ~nsPrincipal();
 
 public:
-  // Our refcount is managed by mJSPrincipals.  Use this macro to avoid
+  // Our refcount is managed by nsJSPrincipals.  Use this macro to avoid
   // an extra refcount member.
   NS_DECL_ISUPPORTS_INHERITED
 public:
 
   NS_DECL_NSIPRINCIPAL
   NS_DECL_NSISERIALIZABLE
 
   // Either Init() or InitFromPersistent() must be called before
@@ -95,18 +95,23 @@ public:
   enum AnnotationValue { AnnotationEnabled=1, AnnotationDisabled };
 
   void SetURI(nsIURI *aURI);
   nsresult SetCapability(const char *capability, void **annotation, 
                          AnnotationValue value);
 
   static const char sInvalid[];
 
+  virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
+
+#ifdef DEBUG
+  virtual void dumpImpl() MOZ_OVERRIDE;
+#endif 
+
 protected:
-  nsJSPrincipals mJSPrincipals;
   nsTArray< nsAutoPtr<nsHashtable> > mAnnotations;
   nsHashtable* mCapabilities;
   nsCString mPrefName;
   static PRInt32 sCapabilitiesOrdinal;
 
   // XXXcaa This is a semi-hack.  The best solution here is to keep
   // a reference to an interface here, except there is no interface
   // that we can use yet.
--- a/caps/include/nsScriptSecurityManager.h
+++ b/caps/include/nsScriptSecurityManager.h
@@ -423,16 +423,19 @@ private:
     nsScriptSecurityManager();
     virtual ~nsScriptSecurityManager();
 
     static JSBool
     CheckObjectAccess(JSContext *cx, JSObject *obj,
                       jsid id, JSAccessMode mode,
                       jsval *vp);
 
+    static JSPrincipals *
+    ObjectPrincipalFinder(JSObject *obj);
+    
     // Decides, based on CSP, whether or not eval() and stuff can be executed.
     static JSBool
     ContentSecurityPolicyPermitsJSAction(JSContext *cx);
 
     // Returns null if a principal cannot be found; generally callers
     // should error out at that point.
     static nsIPrincipal*
     doGetObjectPrincipal(JSObject *obj
--- a/caps/include/nsSystemPrincipal.h
+++ b/caps/include/nsSystemPrincipal.h
@@ -45,30 +45,33 @@
 
 #define NS_SYSTEMPRINCIPAL_CLASSNAME "systemprincipal"
 #define NS_SYSTEMPRINCIPAL_CID \
 { 0x4a6212db, 0xaccb, 0x11d3, \
 { 0xb7, 0x65, 0x0, 0x60, 0xb0, 0xb6, 0xce, 0xcb }}
 #define NS_SYSTEMPRINCIPAL_CONTRACTID "@mozilla.org/systemprincipal;1"
 
 
-class nsSystemPrincipal : public nsIPrincipal
+class nsSystemPrincipal : public nsJSPrincipals
 {
 public:
-    // Our refcount is managed by mJSPrincipals.  Use this macro to avoid
+    // Our refcount is managed by nsJSPrincipals.  Use this macro to avoid
     // an extra refcount member.
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIPRINCIPAL
     NS_DECL_NSISERIALIZABLE
 
-    nsresult Init(JSPrincipals **jsprin);
+    nsSystemPrincipal();
+
+    virtual void GetScriptLocation(nsACString &aStr) MOZ_OVERRIDE;
 
-    nsSystemPrincipal();
+#ifdef DEBUG
+    virtual void dumpImpl() MOZ_OVERRIDE;
+#endif 
 
 protected:
     virtual ~nsSystemPrincipal(void);
 
-    nsJSPrincipals mJSPrincipals;
     // XXX Probably unnecessary.  See bug 143559.
     NS_DECL_OWNINGTHREAD
 };
 
 #endif // nsSystemPrincipal_h__
--- a/caps/src/nsJSPrincipals.cpp
+++ b/caps/src/nsJSPrincipals.cpp
@@ -45,56 +45,54 @@
 #include "nsCOMPtr.h"
 #include "jsapi.h"
 #include "jsxdrapi.h"
 #include "nsIJSRuntimeService.h"
 #include "nsIServiceManager.h"
 #include "nsMemory.h"
 #include "nsStringBuffer.h"
 
-static JSBool
-nsJSPrincipalsSubsume(JSPrincipals *jsprin, JSPrincipals *other)
+// for mozilla::dom::workers::kJSPrincipalsDebugToken
+#include "mozilla/dom/workers/Workers.h"
+
+/* static */ JSBool
+nsJSPrincipals::Subsume(JSPrincipals *jsprin, JSPrincipals *other)
 {
-    nsJSPrincipals *nsjsprin = static_cast<nsJSPrincipals *>(jsprin);
-    nsJSPrincipals *nsother  = static_cast<nsJSPrincipals *>(other);
-
     bool result;
-    nsresult rv = nsjsprin->nsIPrincipalPtr->Subsumes(nsother->nsIPrincipalPtr,
-                                                      &result);
+    nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(nsJSPrincipals::get(other), &result);
     return NS_SUCCEEDED(rv) && result;
 }
 
-static void
-nsDestroyJSPrincipals(JSContext *cx, struct JSPrincipals *jsprin)
+/* static */ void
+nsJSPrincipals::Destroy(JSPrincipals *jsprin)
 {
-    nsJSPrincipals *nsjsprin = static_cast<nsJSPrincipals *>(jsprin);
+    // The JS runtime can call this method during the last GC when
+    // nsScriptSecurityManager is destroyed. So we must not assume here that
+    // the security manager still exists.
+
+    nsJSPrincipals *nsjsprin = nsJSPrincipals::get(jsprin);
 
     // We need to destroy the nsIPrincipal. We'll do this by adding
     // to the refcount and calling release
 
-    // Note that we don't want to use NS_IF_RELEASE because it will try
-    // to set nsjsprin->nsIPrincipalPtr to nsnull *after* nsjsprin has
-    // already been destroyed.
 #ifdef NS_BUILD_REFCNT_LOGGING
     // The refcount logging considers AddRef-to-1 to indicate creation,
     // so trick it into thinking it's otherwise, but balance the
     // Release() we do below.
     nsjsprin->refcount++;
-    nsjsprin->nsIPrincipalPtr->AddRef();
+    nsjsprin->AddRef();
     nsjsprin->refcount--;
 #else
     nsjsprin->refcount++;
 #endif
-    nsjsprin->nsIPrincipalPtr->Release();
-    // The nsIPrincipal that we release owns the JSPrincipal struct,
-    // so we don't need to worry about "codebase"
+    nsjsprin->Release();
 }
 
-static JSBool
-nsTranscodeJSPrincipals(JSXDRState *xdr, JSPrincipals **jsprinp)
+/* static */ JSBool
+nsJSPrincipals::Transcode(JSXDRState *xdr, JSPrincipals **jsprinp)
 {
     nsresult rv;
 
     if (xdr->mode == JSXDR_ENCODE) {
         nsIObjectOutputStream *stream =
             reinterpret_cast<nsIObjectOutputStream*>(xdr->userdata);
 
         // Flush xdr'ed data to the underlying object output stream.
@@ -102,22 +100,17 @@ nsTranscodeJSPrincipals(JSXDRState *xdr,
         char *data = (char*) ::JS_XDRMemGetData(xdr, &size);
 
         rv = stream->Write32(size);
         if (NS_SUCCEEDED(rv)) {
             rv = stream->WriteBytes(data, size);
             if (NS_SUCCEEDED(rv)) {
                 ::JS_XDRMemResetData(xdr);
 
-                // Require that GetJSPrincipals has been called already by the
-                // code that compiled the script that owns the principals.
-                nsJSPrincipals *nsjsprin =
-                    static_cast<nsJSPrincipals*>(*jsprinp);
-
-                rv = stream->WriteObject(nsjsprin->nsIPrincipalPtr, true);
+                rv = stream->WriteObject(nsJSPrincipals::get(*jsprinp), true);
             }
         }
     } else {
         NS_ASSERTION(JS_XDRMemDataLeft(xdr) == 0, "XDR out of sync?!");
         nsIObjectInputStream *stream =
             reinterpret_cast<nsIObjectInputStream*>(xdr->userdata);
 
         nsCOMPtr<nsIPrincipal> prin;
@@ -136,89 +129,43 @@ nsTranscodeJSPrincipals(JSXDRState *xdr,
                     // Any decode-mode JSXDRState whose userdata points to an
                     // nsIObjectInputStream instance must use nsMemory to Alloc
                     // and Free its data buffer.  Swap the new buffer we just
                     // read for the old, exhausted data.
                     olddata = (char*) ::JS_XDRMemGetData(xdr, &oldsize);
                     nsMemory::Free(olddata);
                     ::JS_XDRMemSetData(xdr, data, size);
 
-                    prin->GetJSPrincipals(xdr->cx, jsprinp);
+                    *jsprinp = nsJSPrincipals::get(prin);
+                    JS_HoldPrincipals(*jsprinp);
                 }
             }
         }
     }
 
     if (NS_FAILED(rv)) {
         ::JS_ReportError(xdr->cx, "can't %scode principals (failure code %x)",
                          (xdr->mode == JSXDR_ENCODE) ? "en" : "de",
                          (unsigned int) rv);
         return JS_FALSE;
     }
     return JS_TRUE;
 }
 
-nsresult
-nsJSPrincipals::Startup()
-{
-    nsCOMPtr<nsIJSRuntimeService> rtsvc = nsXPConnect::GetXPConnect();
-    if (!rtsvc)
-        return NS_ERROR_FAILURE;
-
-    JSRuntime *rt;
-    rtsvc->GetRuntime(&rt);
-    NS_ASSERTION(rt != nsnull, "no JSRuntime?!");
-
-    JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(rt);
-    NS_ASSERTION(callbacks, "Need a callbacks struct by now!");
+#ifdef DEBUG
 
-    NS_ASSERTION(!callbacks->principalsTranscoder,
-                 "oops, JS_SetPrincipalsTranscoder wars!");
-
-    callbacks->principalsTranscoder = nsTranscodeJSPrincipals;
-    return NS_OK;
-}
-
-nsJSPrincipals::nsJSPrincipals()
+// Defined here so one can do principals->dump() in the debugger
+JS_EXPORT_API(void)
+JSPrincipals::dump()
 {
-    codebase = nsnull;
-    refcount = 0;
-    destroy = nsDestroyJSPrincipals;
-    subsume = nsJSPrincipalsSubsume;
-    nsIPrincipalPtr = nsnull;
+    if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
+        static_cast<nsJSPrincipals *>(this)->dumpImpl();
+    } else if (debugToken == mozilla::dom::workers::kJSPrincipalsDebugToken) {
+        fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
+    } else {
+        fprintf(stderr,
+                "!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
+                "actual=0x%x expected=0x%x\n",
+                this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
+    }
 }
 
-nsresult
-nsJSPrincipals::Init(nsIPrincipal *aPrincipal, const nsCString& aCodebase)
-{
-    if (nsIPrincipalPtr) {
-        NS_ERROR("Init called twice!");
-        return NS_ERROR_UNEXPECTED;
-    }
-
-    nsIPrincipalPtr = aPrincipal;
-    nsStringBuffer* buf = nsStringBuffer::FromString(aCodebase);
-    char* data;
-    if (buf) {
-        buf->AddRef();
-        data = static_cast<char*>(buf->Data());
-    } else {
-        PRUint32 len = aCodebase.Length();
-        buf = nsStringBuffer::Alloc(len + 1); // addrefs
-        if (!buf) {
-            return NS_ERROR_OUT_OF_MEMORY;
-        }
-        data = static_cast<char*>(buf->Data());
-        memcpy(data, aCodebase.get(), len);
-        data[len] = '\0';
-    }
-    
-    codebase = data;
-
-    return NS_OK;
-}
-
-nsJSPrincipals::~nsJSPrincipals()
-{
-    if (codebase) {
-        nsStringBuffer::FromData(codebase)->Release();
-    }
-}
+#endif 
--- a/caps/src/nsNullPrincipal.cpp
+++ b/caps/src/nsNullPrincipal.cpp
@@ -64,27 +64,27 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsNullPrinci
                             nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER2(nsNullPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt) 
 nsNullPrincipal::AddRef()
 {
-  NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
-  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt");
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount);
   NS_LOG_ADDREF(this, count, "nsNullPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsNullPrincipal::Release()
 {
-  NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(0 != refcount, "dup release");
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount);
   NS_LOG_RELEASE(this, count, "nsNullPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
@@ -128,19 +128,34 @@ nsNullPrincipal::Init()
   if (str.Length() != prefixLen + suffixLen) {
     NS_WARNING("Out of memory allocating null-principal URI");
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   mURI = new nsNullPrincipalURI(str);
   NS_ENSURE_TRUE(mURI, NS_ERROR_OUT_OF_MEMORY);
 
-  return mJSPrincipals.Init(this, str);
+  return NS_OK;
+}
+
+void
+nsNullPrincipal::GetScriptLocation(nsACString &aStr)
+{
+  mURI->GetSpec(aStr);
 }
 
+#ifdef DEBUG
+void nsNullPrincipal::dumpImpl()
+{
+  nsCAutoString str;
+  mURI->GetSpec(str);
+  fprintf(stderr, "nsNullPrincipal (%p) = %s\n", this, str.get());
+}
+#endif 
+
 /**
  * nsIPrincipal implementation
  */
 
 NS_IMETHODIMP
 nsNullPrincipal::GetPreferences(char** aPrefName, char** aID,
                                 char** aSubjectName,
                                 char** aGrantedList, char** aDeniedList,
@@ -175,27 +190,16 @@ nsNullPrincipal::EqualsIgnoringDomain(ns
 NS_IMETHODIMP
 nsNullPrincipal::GetHashValue(PRUint32 *aResult)
 {
   *aResult = (NS_PTR_TO_INT32(this) >> 2);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsNullPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **aJsprin)
-{
-  NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr,
-                  "mJSPrincipals is uninitalized!");
-
-  JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
-  *aJsprin = &mJSPrincipals;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsNullPrincipal::GetSecurityPolicy(void** aSecurityPolicy)
 {
   // We don't actually do security policy caching.  And it's not like anyone
   // can set a security policy for us anyway.
   *aSecurityPolicy = nsnull;
   return NS_OK;
 }
 
--- a/caps/src/nsPrincipal.cpp
+++ b/caps/src/nsPrincipal.cpp
@@ -88,28 +88,28 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsPrincipal,
                             nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER2(nsPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt)
 nsPrincipal::AddRef()
 {
-  NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
+  NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt");
   // XXXcaa does this need to be threadsafe?  See bug 143559.
-  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount);
   NS_LOG_ADDREF(this, count, "nsPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsPrincipal::Release()
 {
-  NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(0 != refcount, "dup release");
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount);
   NS_LOG_RELEASE(this, count, "nsPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
@@ -142,51 +142,46 @@ nsPrincipal::Init(const nsACString& aCer
   NS_ENSURE_STATE(!mInitialized);
   NS_ENSURE_ARG(!aCertFingerprint.IsEmpty() || aCodebase); // better have one of these.
 
   mInitialized = true;
 
   mCodebase = NS_TryToMakeImmutable(aCodebase);
   mCodebaseImmutable = URIIsImmutable(mCodebase);
 
-  nsresult rv;
-  if (!aCertFingerprint.IsEmpty()) {
-    rv = SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
-    if (NS_SUCCEEDED(rv)) {
-      rv = mJSPrincipals.Init(this, mCert->fingerprint);
-    }
-  }
-  else {
-    nsCAutoString spec;
-    rv = mCodebase->GetSpec(spec);
-    if (NS_SUCCEEDED(rv)) {
-      rv = mJSPrincipals.Init(this, spec);
-    }
-  }
+  if (aCertFingerprint.IsEmpty())
+    return NS_OK;
 
-  NS_ASSERTION(NS_SUCCEEDED(rv), "nsPrincipal::Init() failed");
-
-  return rv;
+  return SetCertificate(aCertFingerprint, aSubjectName, aPrettyName, aCert);
 }
 
 nsPrincipal::~nsPrincipal(void)
 {
   SetSecurityPolicy(nsnull); 
   delete mCapabilities;
 }
 
-NS_IMETHODIMP
-nsPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin)
+void
+nsPrincipal::GetScriptLocation(nsACString &aStr)
 {
-  NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!");
+  if (mCert) {
+    aStr.Assign(mCert->fingerprint);
+  } else {
+    mCodebase->GetSpec(aStr);
+  }
+}
 
-  JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
-  *jsprin = &mJSPrincipals;
-  return NS_OK;
+#ifdef DEBUG
+void nsPrincipal::dumpImpl()
+{
+  nsCAutoString str;
+  GetScriptLocation(str);
+  fprintf(stderr, "nsPrincipal (%p) = %s\n", this, str.get());
 }
+#endif 
 
 NS_IMETHODIMP
 nsPrincipal::GetOrigin(char **aOrigin)
 {
   *aOrigin = nsnull;
 
   nsCOMPtr<nsIURI> origin;
   if (mCodebase) {
@@ -878,19 +873,16 @@ nsPrincipal::InitFromPersistent(const ch
     }
 
     NS_TryToSetImmutable(mCodebase);
     mCodebaseImmutable = URIIsImmutable(mCodebase);
 
     mTrusted = aTrusted;
   }
 
-  rv = mJSPrincipals.Init(this, aToken);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   //-- Save the preference name
   mPrefName = aPrefName;
 
   const char* ordinalBegin = PL_strpbrk(aPrefName, "1234567890");
   if (ordinalBegin) {
     PRIntn n = atoi(ordinalBegin);
     if (sCapabilitiesOrdinal <= n) {
       sCapabilitiesOrdinal = n + 1;
--- a/caps/src/nsScriptSecurityManager.cpp
+++ b/caps/src/nsScriptSecurityManager.cpp
@@ -512,16 +512,23 @@ NS_IMPL_ISUPPORTS4(nsScriptSecurityManag
                    nsIChannelEventSink,
                    nsIObserver)
 
 ///////////////////////////////////////////////////
 // Methods implementing nsIScriptSecurityManager //
 ///////////////////////////////////////////////////
 
 ///////////////// Security Checks /////////////////
+
+/* static */ JSPrincipals *
+nsScriptSecurityManager::ObjectPrincipalFinder(JSObject *aObj)
+{
+    return nsJSPrincipals::get(doGetObjectPrincipal(aObj));
+}
+
 JSBool
 nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
 {
     // Get the security manager
     nsScriptSecurityManager *ssm =
         nsScriptSecurityManager::GetScriptSecurityManager();
 
     NS_ASSERTION(ssm, "Failed to get security manager service");
@@ -532,17 +539,17 @@ nsScriptSecurityManager::ContentSecurity
     nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
 
     NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
     if (NS_FAILED(rv))
         return JS_FALSE; // Not just absence of principal, but failure.
 
     if (!subjectPrincipal) {
         // See bug 553448 for discussion of this case.
-        NS_ASSERTION(!JS_GetSecurityCallbacks(cx)->findObjectPrincipals,
+        NS_ASSERTION(!JS_GetSecurityCallbacks(js::GetRuntime(cx))->findObjectPrincipals,
                      "CSP: Should have been able to find subject principal. "
                      "Reluctantly granting access.");
         return JS_TRUE;
     }
 
     nsCOMPtr<nsIContentSecurityPolicy> csp;
     rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
     NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
@@ -2174,21 +2181,17 @@ nsScriptSecurityManager::GetScriptPrinci
         return nsnull;
     }
     JSPrincipals *jsp = JS_GetScriptPrincipals(cx, script);
     if (!jsp) {
         *rv = NS_ERROR_FAILURE;
         NS_ERROR("Script compiled without principals!");
         return nsnull;
     }
-    nsJSPrincipals *nsJSPrin = static_cast<nsJSPrincipals *>(jsp);
-    nsIPrincipal* result = nsJSPrin->nsIPrincipalPtr;
-    if (!result)
-        *rv = NS_ERROR_FAILURE;
-    return result;
+    return nsJSPrincipals::get(jsp);
 }
 
 // static
 nsIPrincipal*
 nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
                                                     JSObject *obj,
                                                     JSStackFrame *fp,
                                                     nsresult *rv)
@@ -3325,17 +3328,16 @@ nsScriptSecurityManager::nsScriptSecurit
       mPolicyPrefsChanged(true)
 {
     NS_ASSERTION(sizeof(PRWord) == sizeof(void*),
                  "PRWord and void* have different lengths on this platform. "
                  "This may cause a security failure with the SecurityLevel union.");
     mPrincipals.Init(31);
 }
 
-
 nsresult nsScriptSecurityManager::Init()
 {
     nsXPConnect* xpconnect = nsXPConnect::GetXPConnect();
      if (!xpconnect)
         return NS_ERROR_FAILURE;
 
     NS_ADDREF(sXPConnect = xpconnect);
     NS_ADDREF(sJSContextStack = xpconnect);
@@ -3360,45 +3362,40 @@ nsresult nsScriptSecurityManager::Init()
 
     rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Create our system principal singleton
     nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
     NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
 
-    JSPrincipals *jsprin;
-    rv = system->Init(&jsprin);
-    NS_ENSURE_SUCCESS(rv, rv);
-
     mSystemPrincipal = system;
 
     //-- Register security check callback in the JS engine
     //   Currently this is used to control access to function.caller
     nsCOMPtr<nsIJSRuntimeService> runtimeService =
         do_QueryInterface(sXPConnect, &rv);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = runtimeService->GetRuntime(&sRuntime);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    static JSSecurityCallbacks securityCallbacks = {
+    static const JSSecurityCallbacks securityCallbacks = {
         CheckObjectAccess,
-        NULL,
-        NULL,
+        nsJSPrincipals::Subsume,
+        nsJSPrincipals::Transcode,
+        ObjectPrincipalFinder,
         ContentSecurityPolicyPermitsJSAction
     };
 
-#ifdef DEBUG
-    JSSecurityCallbacks *oldcallbacks =
-#endif
-    JS_SetRuntimeSecurityCallbacks(sRuntime, &securityCallbacks);
-    NS_ASSERTION(!oldcallbacks, "Someone else set security callbacks!");
-
-    JS_SetTrustedPrincipals(sRuntime, jsprin);
+    MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
+    JS_SetSecurityCallbacks(sRuntime, &securityCallbacks);
+    JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy);
+
+    JS_SetTrustedPrincipals(sRuntime, system);
 
     return NS_OK;
 }
 
 static nsScriptSecurityManager *gScriptSecMan = nsnull;
 
 jsid nsScriptSecurityManager::sEnabledID   = JSID_VOID;
 
@@ -3412,17 +3409,17 @@ nsScriptSecurityManager::~nsScriptSecuri
     delete mCapabilities;
     gScriptSecMan = nsnull;
 }
 
 void
 nsScriptSecurityManager::Shutdown()
 {
     if (sRuntime) {
-        JS_SetRuntimeSecurityCallbacks(sRuntime, NULL);
+        JS_SetSecurityCallbacks(sRuntime, NULL);
         JS_SetTrustedPrincipals(sRuntime, NULL);
         sRuntime = nsnull;
     }
     sEnabledID = JSID_VOID;
 
     NS_IF_RELEASE(sIOService);
     NS_IF_RELEASE(sXPConnect);
     NS_IF_RELEASE(sJSContextStack);
@@ -3440,23 +3437,16 @@ nsScriptSecurityManager::GetScriptSecuri
         nsresult rv;
         rv = ssManager->Init();
         NS_ASSERTION(NS_SUCCEEDED(rv), "Failed to initialize nsScriptSecurityManager");
         if (NS_FAILED(rv)) {
             delete ssManager;
             return nsnull;
         }
  
-        rv = nsJSPrincipals::Startup();
-        if (NS_FAILED(rv)) {
-            NS_WARNING("can't initialize JS engine security protocol glue!");
-            delete ssManager;
-            return nsnull;
-        }
- 
         rv = sXPConnect->SetDefaultSecurityManager(ssManager,
                                                    nsIXPCSecurityManager::HOOK_ALL);
         if (NS_FAILED(rv)) {
             NS_WARNING("Failed to install xpconnect security manager!");
             delete ssManager;
             return nsnull;
         }
 
--- a/caps/src/nsSystemPrincipal.cpp
+++ b/caps/src/nsSystemPrincipal.cpp
@@ -57,42 +57,55 @@ NS_IMPL_QUERY_INTERFACE2_CI(nsSystemPrin
                             nsISerializable)
 NS_IMPL_CI_INTERFACE_GETTER2(nsSystemPrincipal,
                              nsIPrincipal,
                              nsISerializable)
 
 NS_IMETHODIMP_(nsrefcnt) 
 nsSystemPrincipal::AddRef()
 {
-  NS_PRECONDITION(PRInt32(mJSPrincipals.refcount) >= 0, "illegal refcnt");
-  nsrefcnt count = PR_ATOMIC_INCREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(PRInt32(refcount) >= 0, "illegal refcnt");
+  nsrefcnt count = PR_ATOMIC_INCREMENT(&refcount);
   NS_LOG_ADDREF(this, count, "nsSystemPrincipal", sizeof(*this));
   return count;
 }
 
 NS_IMETHODIMP_(nsrefcnt)
 nsSystemPrincipal::Release()
 {
-  NS_PRECONDITION(0 != mJSPrincipals.refcount, "dup release");
-  nsrefcnt count = PR_ATOMIC_DECREMENT(&mJSPrincipals.refcount);
+  NS_PRECONDITION(0 != refcount, "dup release");
+  nsrefcnt count = PR_ATOMIC_DECREMENT(&refcount);
   NS_LOG_RELEASE(this, count, "nsSystemPrincipal");
   if (count == 0) {
     delete this;
   }
 
   return count;
 }
 
+static const char SYSTEM_PRINCIPAL_SPEC[] = "[System Principal]";
+
+void
+nsSystemPrincipal::GetScriptLocation(nsACString &aStr)
+{
+    aStr.Assign(SYSTEM_PRINCIPAL_SPEC);
+}
+
+#ifdef DEBUG
+void nsSystemPrincipal::dumpImpl()
+{
+  fprintf(stderr, "nsSystemPrincipal (%p)\n", this);
+}
+#endif 
+
 
 ///////////////////////////////////////
 // Methods implementing nsIPrincipal //
 ///////////////////////////////////////
 
-#define SYSTEM_PRINCIPAL_SPEC "[System Principal]"
-
 NS_IMETHODIMP
 nsSystemPrincipal::GetPreferences(char** aPrefName, char** aID,
                                   char** aSubjectName,
                                   char** aGrantedList, char** aDeniedList,
                                   bool* aIsTrusted)
 {
     // The system principal should never be streamed out
     *aPrefName = nsnull;
@@ -275,26 +288,16 @@ nsSystemPrincipal::GetSecurityPolicy(voi
 }
 
 NS_IMETHODIMP
 nsSystemPrincipal::SetSecurityPolicy(void* aSecurityPolicy)
 {
     return NS_OK;
 }
 
-NS_IMETHODIMP
-nsSystemPrincipal::GetJSPrincipals(JSContext *cx, JSPrincipals **jsprin)
-{
-    NS_PRECONDITION(mJSPrincipals.nsIPrincipalPtr, "mJSPrincipals is uninitialized!");
-
-    JSPRINCIPALS_HOLD(cx, &mJSPrincipals);
-    *jsprin = &mJSPrincipals;
-    return NS_OK;
-}
-
 
 //////////////////////////////////////////
 // Methods implementing nsISerializable //
 //////////////////////////////////////////
 
 NS_IMETHODIMP
 nsSystemPrincipal::Read(nsIObjectInputStream* aStream)
 {
@@ -312,29 +315,11 @@ nsSystemPrincipal::Write(nsIObjectOutput
 /////////////////////////////////////////////
 // Constructor, Destructor, initialization //
 /////////////////////////////////////////////
 
 nsSystemPrincipal::nsSystemPrincipal()
 {
 }
 
-nsresult
-nsSystemPrincipal::Init(JSPrincipals **jsprin)
-{
-    // Use an nsCString so we only do the allocation once here and then
-    // share with nsJSPrincipals
-    nsCString str(SYSTEM_PRINCIPAL_SPEC);
-    if (!str.EqualsLiteral(SYSTEM_PRINCIPAL_SPEC)) {
-        NS_WARNING("Out of memory initializing system principal");
-        return NS_ERROR_OUT_OF_MEMORY;
-    }
-
-    nsresult rv = mJSPrincipals.Init(this, str);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    *jsprin = &mJSPrincipals;
-    return NS_OK;
-}
-
-nsSystemPrincipal::~nsSystemPrincipal(void)
+nsSystemPrincipal::~nsSystemPrincipal()
 {
 }
--- a/config/autoconf.mk.in
+++ b/config/autoconf.mk.in
@@ -296,16 +296,20 @@ MOZ_GIO_CFLAGS = @MOZ_GIO_CFLAGS@
 MOZ_GIO_LIBS = @MOZ_GIO_LIBS@
 
 MOZ_NATIVE_NSPR = @MOZ_NATIVE_NSPR@
 MOZ_NATIVE_NSS = @MOZ_NATIVE_NSS@
 
 MOZ_B2G_RIL = @MOZ_B2G_RIL@
 MOZ_B2G_BT = @MOZ_B2G_BT@
 
+MOZ_ASAN = @MOZ_ASAN@
+MOZ_CFLAGS_NSS = @MOZ_CFLAGS_NSS@
+MOZ_NO_WLZDEFS = @MOZ_NO_WLZDEFS@
+
 BUILD_CTYPES = @BUILD_CTYPES@
 
 COMPILE_ENVIRONMENT = @COMPILE_ENVIRONMENT@
 CROSS_COMPILE   = @CROSS_COMPILE@
 
 WCHAR_CFLAGS	= @WCHAR_CFLAGS@
 
 OS_CPPFLAGS	= @CPPFLAGS@
--- a/config/config.mk
+++ b/config/config.mk
@@ -784,18 +784,22 @@ OPTIMIZE_JARS_CMD = $(PYTHON) $(call cor
 CREATE_PRECOMPLETE_CMD = $(PYTHON) $(call core_abspath,$(topsrcdir)/config/createprecomplete.py)
 
 EXPAND_LIBS = $(PYTHON) -I$(DEPTH)/config $(topsrcdir)/config/expandlibs.py
 EXPAND_LIBS_EXEC = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_exec.py
 EXPAND_LIBS_GEN = $(PYTHON) $(topsrcdir)/config/pythonpath.py -I$(DEPTH)/config $(topsrcdir)/config/expandlibs_gen.py
 EXPAND_AR = $(EXPAND_LIBS_EXEC) --extract -- $(AR)
 EXPAND_CC = $(EXPAND_LIBS_EXEC) --uselist -- $(CC)
 EXPAND_CCC = $(EXPAND_LIBS_EXEC) --uselist -- $(CCC)
-EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(LD)
-EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB)
+EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist -- $(LD)
+EXPAND_MKSHLIB_ARGS = --uselist
+ifdef SYMBOL_ORDER
+EXPAND_MKSHLIB_ARGS += --symbol-order $(SYMBOL_ORDER)
+endif
+EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) $(EXPAND_MKSHLIB_ARGS) -- $(MKSHLIB)
 
 ifdef STDCXX_COMPAT
 ifneq ($(OS_ARCH),Darwin)
 CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0
 endif
 endif
 
 # autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
--- a/config/expandlibs_config.py.in
+++ b/config/expandlibs_config.py.in
@@ -49,8 +49,10 @@ AR_EXTRACT = "@AR_EXTRACT@".replace('$(A
 DLL_PREFIX = "@DLL_PREFIX@"
 LIB_PREFIX = "@LIB_PREFIX@"
 OBJ_SUFFIX = normalize_suffix("@OBJ_SUFFIX@")
 LIB_SUFFIX = normalize_suffix("@LIB_SUFFIX@")
 DLL_SUFFIX = normalize_suffix("@DLL_SUFFIX@")
 IMPORT_LIB_SUFFIX = normalize_suffix("@IMPORT_LIB_SUFFIX@")
 LIBS_DESC_SUFFIX = normalize_suffix("@LIBS_DESC_SUFFIX@")
 EXPAND_LIBS_LIST_STYLE = "@EXPAND_LIBS_LIST_STYLE@"
+EXPAND_LIBS_ORDER_STYLE = "@EXPAND_LIBS_ORDER_STYLE@"
+LD_PRINT_ICF_SECTIONS = "@LD_PRINT_ICF_SECTIONS@"
--- a/config/expandlibs_exec.py
+++ b/config/expandlibs_exec.py
@@ -44,29 +44,41 @@ from static libraries (or use those list
 
 With the --uselist argument (useful for e.g. $(CC)), it replaces all object
 files with a list file. This can be used to avoid limitations in the length
 of a command line. The kind of list file format used depends on the
 EXPAND_LIBS_LIST_STYLE variable: 'list' for MSVC style lists (@file.list)
 or 'linkerscript' for GNU ld linker scripts.
 See https://bugzilla.mozilla.org/show_bug.cgi?id=584474#c59 for more details.
 
-With the --reorder argument, followed by a file name, it will reorder the
-object files from the command line according to the order given in the file.
-Implies --extract.
+With the --symbol-order argument, followed by a file name, it will add the
+relevant linker options to change the order in which the linker puts the
+symbols appear in the resulting binary. Only works for ELF targets.
 '''
 from __future__ import with_statement
 import sys
 import os
 from expandlibs import ExpandArgs, relativize, isObject
 import expandlibs_config as conf
 from optparse import OptionParser
 import subprocess
 import tempfile
 import shutil
+import subprocess
+import re
+
+# The are the insert points for a GNU ld linker script, assuming a more
+# or less "standard" default linker script. This is not a dict because
+# order is important.
+SECTION_INSERT_BEFORE = [
+  ('.text', '.fini'),
+  ('.rodata', '.rodata1'),
+  ('.data.rel.ro', '.dynamic'),
+  ('.data', '.data1'),
+]
 
 class ExpandArgsMore(ExpandArgs):
     ''' Meant to be used as 'with ExpandArgsMore(args) as ...: '''
     def __enter__(self):
         self.tmp = []
         return self
         
     def __exit__(self, type, value, tb):
@@ -114,61 +126,203 @@ class ExpandArgsMore(ExpandArgs):
         fd, tmp = tempfile.mkstemp(suffix=".list",dir=os.curdir)
         if conf.EXPAND_LIBS_LIST_STYLE == "linkerscript":
             content = ["INPUT(%s)\n" % obj for obj in objs]
             ref = tmp
         elif conf.EXPAND_LIBS_LIST_STYLE == "list":
             content = ["%s\n" % obj for obj in objs]
             ref = "@" + tmp
         else:
+            os.close(fd)
             os.remove(tmp)
             return
         self.tmp.append(tmp)
         f = os.fdopen(fd, "w")
         f.writelines(content)
         f.close()
         idx = self.index(objs[0])
         newlist = self[0:idx] + [ref] + [item for item in self[idx:] if item not in objs]
         self[0:] = newlist
 
-    def reorder(self, order_list):
-        '''Given a list of file names without OBJ_SUFFIX, rearrange self
-        so that the object file names it contains are ordered according to
-        that list.
-        '''
-        objs = [o for o in self if isObject(o)]
-        if not objs: return
-        idx = self.index(objs[0])
-        # Keep everything before the first object, then the ordered objects,
-        # then any other objects, then any non-objects after the first object
-        objnames = dict([(os.path.splitext(os.path.basename(o))[0], o) for o in objs])
-        self[0:] = self[0:idx] + [objnames[o] for o in order_list if o in objnames] + \
-                   [o for o in objs if os.path.splitext(os.path.basename(o))[0] not in order_list] + \
-                   [x for x in self[idx:] if not isObject(x)]
+    def _getFoldedSections(self):
+        '''Returns a dict about folded sections.
+        When section A and B are folded into section C, the dict contains:
+        { 'A': 'C',
+          'B': 'C',
+          'C': ['A', 'B'] }'''
+        if not conf.LD_PRINT_ICF_SECTIONS:
+            return {}
+
+        proc = subprocess.Popen(self + [conf.LD_PRINT_ICF_SECTIONS], stdout = subprocess.PIPE, stderr = subprocess.PIPE)
+        (stdout, stderr) = proc.communicate()
+        result = {}
+        # gold's --print-icf-sections output looks like the following:
+        # ld: ICF folding section '.section' in file 'file.o'into '.section' in file 'file.o'
+        # In terms of words, chances are this will change in the future,
+        # especially considering "into" is misplaced. Splitting on quotes
+        # seems safer.
+        for l in stderr.split('\n'):
+            quoted = l.split("'")
+            if len(quoted) > 5 and quoted[1] != quoted[5]:
+                result[quoted[1]] = quoted[5]
+                if quoted[5] in result:
+                    result[quoted[5]].append(quoted[1])
+                else:
+                    result[quoted[5]] = [quoted[1]]
+        return result
+
+    def _getOrderedSections(self, ordered_symbols):
+        '''Given an ordered list of symbols, returns the corresponding list
+        of sections following the order.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        finder = SectionFinder([arg for arg in self if isObject(arg) or os.path.splitext(arg)[1] == conf.LIB_SUFFIX])
+        folded = self._getFoldedSections()
+        sections = set()
+        ordered_sections = []
+        for symbol in ordered_symbols:
+            symbol_sections = finder.getSections(symbol)
+            all_symbol_sections = []
+            for section in symbol_sections:
+                if section in folded:
+                    if isinstance(folded[section], str):
+                        section = folded[section]
+                    all_symbol_sections.append(section)
+                    all_symbol_sections.extend(folded[section])
+                else:
+                    all_symbol_sections.append(section)
+            for section in all_symbol_sections:
+                if not section in sections:
+                    ordered_sections.append(section)
+                    sections.add(section)
+        return ordered_sections
+
+    def orderSymbols(self, order):
+        '''Given a file containing a list of symbols, adds the appropriate
+        argument to make the linker put the symbols in that order.'''
+        with open(order) as file:
+            sections = self._getOrderedSections([l.strip() for l in file.readlines() if l.strip()])
+        split_sections = {}
+        linked_sections = [s[0] for s in SECTION_INSERT_BEFORE]
+        for s in sections:
+            for linked_section in linked_sections:
+                if s.startswith(linked_section):
+                    if linked_section in split_sections:
+                        split_sections[linked_section].append(s)
+                    else:
+                        split_sections[linked_section] = [s]
+                    break
+        content = []
+        # Order is important
+        linked_sections = [s for s in linked_sections if s in split_sections]
 
+        if conf.EXPAND_LIBS_ORDER_STYLE == 'section-ordering-file':
+            option = '-Wl,--section-ordering-file,%s'
+            content = sections
+            for linked_section in linked_sections:
+                content.extend(split_sections[linked_section])
+                content.append('%s.*' % linked_section)
+                content.append(linked_section)
+
+        elif conf.EXPAND_LIBS_ORDER_STYLE == 'linkerscript':
+            option = '-Wl,-T,%s'
+            section_insert_before = dict(SECTION_INSERT_BEFORE)
+            for linked_section in linked_sections:
+                content.append('SECTIONS {')
+                content.append('  %s : {' % linked_section)
+                content.extend('    *(%s)' % s for s in split_sections[linked_section])
+                content.append('  }')
+                content.append('}')
+                content.append('INSERT BEFORE %s' % section_insert_before[linked_section])
+        else:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+
+        fd, tmp = tempfile.mkstemp(dir=os.curdir)
+        f = os.fdopen(fd, "w")
+        f.write('\n'.join(content)+'\n')
+        f.close()
+        self.tmp.append(tmp)
+        self.append(option % tmp)
+
+class SectionFinder(object):
+    '''Instances of this class allow to map symbol names to sections in
+    object files.'''
+
+    def __init__(self, objs):
+        '''Creates an instance, given a list of object files.'''
+        if not conf.EXPAND_LIBS_ORDER_STYLE in ['linkerscript', 'section-ordering-file']:
+            raise Exception('EXPAND_LIBS_ORDER_STYLE "%s" is not supported' % conf.EXPAND_LIBS_ORDER_STYLE)
+        self.mapping = {}
+        for obj in objs:
+            if not isObject(obj) and os.path.splitext(obj)[1] != conf.LIB_SUFFIX:
+                raise Exception('%s is not an object nor a static library' % obj)
+            for symbol, section in SectionFinder._getSymbols(obj):
+                sym = SectionFinder._normalize(symbol)
+                if sym in self.mapping:
+                    if not section in self.mapping[sym]:
+                        self.mapping[sym].append(section)
+                else:
+                    self.mapping[sym] = [section]