Merge mozilla-inbound and mozilla-central
authorMarco Bonardo <mbonardo@mozilla.com>
Fri, 08 Jul 2011 11:53:56 +0200
changeset 73324 5479a346b95b82162c72419a95cbb4022cbbfe4d
parent 73289 fa2c7d2c52b025ba231689f72aceed53807b82ab (current diff)
parent 73323 15546d781966aef892cb560bd58cde99a7e703da (diff)
child 73340 3660402d17a86e66471bfd754f40dd6cf4f10d53
child 73366 364d2debc2a7c95797d1e03db5076ad0b8684183
push id235
push userbzbarsky@mozilla.com
push dateTue, 27 Sep 2011 17:13:04 +0000
treeherdermozilla-beta@2d1e082d176a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone8.0a1
first release with
nightly win64
5479a346b95b / 8.0a1 / 20110708030240 / files
nightly linux32
nightly linux64
nightly mac
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly win64
Merge mozilla-inbound and mozilla-central
toolkit/components/console/hudservice/tests/browser/test-bug-595934-getselection.html
toolkit/components/console/hudservice/tests/browser/test-bug-595934-getselection.js
xpcom/base/nsMemoryReporterManager.cpp
--- a/browser/branding/aurora/pref/firefox-branding.js
+++ b/browser/branding/aurora/pref/firefox-branding.js
@@ -4,17 +4,17 @@ pref("startup.homepage_welcome_url","");
 // The time interval between checks for a new version (in seconds)
 // nightly=8 hours, official=24 hours
 pref("app.update.interval", 28800);
 // The time interval between the downloading of mar file chunks in the
 // background (in seconds)
 pref("app.update.download.backgroundInterval", 60);
 // URL user can browse to manually if for some reason all update installation
 // attempts fail.
-pref("app.update.url.manual", "http://nightly.mozilla.org/");
+pref("app.update.url.manual", "http://www.mozilla.com/firefox/channel/");
 // A default value for the "More information about this update" link
 // supplied in the "An update is available" page of the update wizard. 
 pref("app.update.url.details", "http://www.mozilla.org/projects/%APP%/");
 
 // Release notes and vendor URLs
 pref("app.releaseNotesURL", "http://www.mozilla.org/projects/%APP%/%VERSION%/releasenotes/");
 pref("app.vendorURL", "http://www.mozilla.org/projects/%APP%/");
 
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -34,16 +34,17 @@ class DeviceManagerADB(DeviceManager):
 
   # external function
   # returns:
   #  success: directory name
   #  failure: None
   def mkDir(self, name):
     try:
       self.checkCmd(["shell", "mkdir", name])
+      self.chmodDir(name)
       return name
     except:
       return None
 
   # make directory structure on the device
   # external function
   # returns:
   #  success: directory structure that we created
--- a/content/base/src/nsNodeUtils.cpp
+++ b/content/base/src/nsNodeUtils.cpp
@@ -510,30 +510,28 @@ nsNodeUtils::CloneAndAdopt(nsINode *aNod
     nodeInfo = newNodeInfo;
   }
 
   nsGenericElement *elem = aNode->IsElement() ?
                            static_cast<nsGenericElement*>(aNode) :
                            nsnull;
 
   nsCOMPtr<nsINode> clone;
-  PRBool isDeepDocumentClone = PR_FALSE;
   if (aClone) {
     rv = aNode->Clone(nodeInfo, getter_AddRefs(clone));
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (aParent) {
       // If we're cloning we need to insert the cloned children into the cloned
       // parent.
       rv = aParent->AppendChildTo(static_cast<nsIContent*>(clone.get()),
                                   PR_FALSE);
       NS_ENSURE_SUCCESS(rv, rv);
     }
     else if (aDeep && clone->IsNodeOfType(nsINode::eDOCUMENT)) {
-      isDeepDocumentClone = PR_TRUE;
       // After cloning the document itself, we want to clone the children into
       // the cloned document (somewhat like cloning and importing them into the
       // cloned document).
       nodeInfoManager = clone->mNodeInfo->NodeInfoManager();
     }
   }
   else if (nodeInfoManager) {
     nsIDocument* oldDoc = aNode->GetOwnerDoc();
--- a/content/base/test/Makefile.in
+++ b/content/base/test/Makefile.in
@@ -497,16 +497,17 @@ include $(topsrcdir)/config/rules.mk
 		forRemoval.resource^headers^ \
 		accesscontrol.resource \
 		accesscontrol.resource^headers^ \
 		invalid_accesscontrol.resource \
 		invalid_accesscontrol.resource^headers^ \
 		somedatas.resource \
 		somedatas.resource^headers^ \
 		delayedServerEvents.sjs \
+		test_bug664916.html \
 		test_bug666604.html \
 		$(NULL)
 
 _CHROME_FILES =	\
 		test_bug357450.js \
 		$(NULL)
 
 # This test fails on the Mac for some reason
new file mode 100644
--- /dev/null
+++ b/content/base/test/test_bug664916.html
@@ -0,0 +1,40 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=664916
+-->
+<head>
+  <title>Test for Bug 664916</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=664916">Mozilla Bug 664916</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+  
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+
+/** Test for Bug 664916 **/
+var div = document.createElement("div");
+var textNode = document.createTextNode("x")
+var tagNameGetter = div.__lookupGetter__("tagName");
+
+var tagName = "";
+try {
+  tagName = tagNameGetter.call(textNode);
+  ok(false, "Should throw when calling tagname getter on text node");
+} catch(e) {
+  ok(true, "Should throw when calling tagname getter on text node");
+}
+is(tagName, "", "Should not have changed tagName yet");
+tagName = tagNameGetter.call(div);
+is(tagName, "DIV", "Should get the right tag name");
+</script>
+</pre>
+</body>
+</html>
--- a/content/html/document/src/ImageDocument.cpp
+++ b/content/html/document/src/ImageDocument.cpp
@@ -644,42 +644,36 @@ ImageDocument::CreateSyntheticDocument()
   NS_ENSURE_SUCCESS(rv, rv);
 
   // We must declare the image as a block element. If we stay as
   // an inline element, our parent LineBox will be inline too and
   // ignore the available height during reflow.
   // This is bad during printing, it means tall image frames won't know
   // the size of the paper and cannot break into continuations along
   // multiple pages.
-  Element* head = GetHeadElement();
-  if (!head) {
-    NS_WARNING("no head on image document!");
+  Element* body = GetBodyElement();
+  if (!body) {
+    NS_WARNING("no body on image document!");
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsINodeInfo> nodeInfo;
   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::style, nsnull,
                                            kNameSpaceID_XHTML,
                                            nsIDOMNode::ELEMENT_NODE);
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
   nsRefPtr<nsGenericHTMLElement> styleContent = NS_NewHTMLStyleElement(nodeInfo.forget());
   if (!styleContent) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  styleContent->SetInnerHTML(NS_LITERAL_STRING("img { display: block; }"));
-  head->AppendChildTo(styleContent, PR_FALSE);
+  styleContent->SetTextContent(NS_LITERAL_STRING("img { display: block; }"));
+  body->AppendChildTo(styleContent, PR_FALSE);
 
   // Add the image element
-  Element* body = GetBodyElement();
-  if (!body) {
-    NS_WARNING("no body on image document!");
-    return NS_ERROR_FAILURE;
-  }
-
   nodeInfo = mNodeInfoManager->GetNodeInfo(nsGkAtoms::img, nsnull,
                                            kNameSpaceID_XHTML,
                                            nsIDOMNode::ELEMENT_NODE);
   NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
 
   mImageContent = NS_NewHTMLImageElement(nodeInfo.forget());
   if (!mImageContent) {
     return NS_ERROR_OUT_OF_MEMORY;
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2165,45 +2165,28 @@ nsHTMLDocument::GetEmbeds(nsIDOMHTMLColl
 
   *aEmbeds = mEmbeds;
   NS_ADDREF(*aEmbeds);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsHTMLDocument::GetSelection(nsAString& aReturn)
+nsHTMLDocument::GetSelection(nsISelection** aReturn)
 {
-  aReturn.Truncate();
-
-  nsCOMPtr<nsIJSContextStack> stack = do_GetService("@mozilla.org/js/xpc/ContextStack;1");
-  JSContext* ccx = nsnull;
-  if (stack && NS_SUCCEEDED(stack->Peek(&ccx)) && ccx) {
-    JS_ReportWarning(ccx, "Deprecated method document.getSelection() called.  Please use window.getSelection() instead.");
-  }
-
   nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(GetScopeObject());
   nsCOMPtr<nsPIDOMWindow> pwin = do_QueryInterface(window);
   NS_ENSURE_TRUE(pwin, NS_OK);
   NS_ASSERTION(pwin->IsInnerWindow(), "Should have inner window here!");
   NS_ENSURE_TRUE(pwin->GetOuterWindow() &&
                  pwin->GetOuterWindow()->GetCurrentInnerWindow() == pwin,
                  NS_OK);
 
-  nsCOMPtr<nsISelection> selection;
-  nsresult rv = window->GetSelection(getter_AddRefs(selection));
-  NS_ENSURE_TRUE(selection && NS_SUCCEEDED(rv), rv);
-
-  nsXPIDLString str;
-
-  rv = selection->ToString(getter_Copies(str));
-
-  aReturn.Assign(str);
-
-  return rv;
+  return window->GetSelection(aReturn);
+  
 }
 
 NS_IMETHODIMP
 nsHTMLDocument::CaptureEvents(PRInt32 aEventFlags)
 {
   ReportUseOfDeprecatedMethod(this, "UseOfCaptureEventsWarning");
   return NS_OK;
 }
--- a/content/svg/content/src/DOMSVGAnimatedLengthList.cpp
+++ b/content/svg/content/src/DOMSVGAnimatedLengthList.cpp
@@ -119,17 +119,17 @@ DOMSVGAnimatedLengthList::InternalBaseVa
   // we MUST keep our baseVal in sync. If we don't, script will either see a
   // list that is too short and be unable to access indexes that should be
   // valid, or else, MUCH WORSE, script will see a list that is too long and be
   // able to access "items" at indexes that are out of bounds (read/write to
   // bad memory)!!
 
   nsRefPtr<DOMSVGAnimatedLengthList> kungFuDeathGrip;
   if (mBaseVal) {
-    if (!aNewValue.Length()) {
+    if (aNewValue.Length() < mBaseVal->Length()) {
       // InternalListLengthWillChange might clear last reference to |this|.
       // Retain a temporary reference to keep from dying before returning.
       kungFuDeathGrip = this;
     }
     mBaseVal->InternalListLengthWillChange(aNewValue.Length());
   }
 
   // If our attribute is not animating, then our animVal mirrors our baseVal
--- a/content/svg/content/src/DOMSVGAnimatedNumberList.cpp
+++ b/content/svg/content/src/DOMSVGAnimatedNumberList.cpp
@@ -118,17 +118,17 @@ DOMSVGAnimatedNumberList::InternalBaseVa
   // we MUST keep our baseVal in sync. If we don't, script will either see a
   // list that is too short and be unable to access indexes that should be
   // valid, or else, MUCH WORSE, script will see a list that is too long and be
   // able to access "items" at indexes that are out of bounds (read/write to
   // bad memory)!!
 
   nsRefPtr<DOMSVGAnimatedNumberList> kungFuDeathGrip;
   if (mBaseVal) {
-    if (!aNewValue.Length()) {
+    if (aNewValue.Length() < mBaseVal->Length()) {
       // InternalListLengthWillChange might clear last reference to |this|.
       // Retain a temporary reference to keep from dying before returning.
       kungFuDeathGrip = this;
     }
     mBaseVal->InternalListLengthWillChange(aNewValue.Length());
   }
 
   // If our attribute is not animating, then our animVal mirrors our baseVal
--- a/content/svg/content/src/DOMSVGLengthList.cpp
+++ b/content/svg/content/src/DOMSVGLengthList.cpp
@@ -114,17 +114,17 @@ DOMSVGLengthList::InternalListLengthWill
 
   if (aNewLength > DOMSVGLength::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     aNewLength = DOMSVGLength::MaxListIndex();
   }
 
   nsRefPtr<DOMSVGLengthList> kungFuDeathGrip;
-  if (oldLength && !aNewLength) {
+  if (aNewLength < oldLength) {
     // RemovingFromList() might clear last reference to |this|.
     // Retain a temporary reference to keep from dying before returning.
     kungFuDeathGrip = this;
   }
 
   // If our length will decrease, notify the items that will be removed:
   for (PRUint32 i = aNewLength; i < oldLength; ++i) {
     if (mItems[i]) {
--- a/content/svg/content/src/DOMSVGNumberList.cpp
+++ b/content/svg/content/src/DOMSVGNumberList.cpp
@@ -114,17 +114,17 @@ DOMSVGNumberList::InternalListLengthWill
 
   if (aNewLength > DOMSVGNumber::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     aNewLength = DOMSVGNumber::MaxListIndex();
   }
 
   nsRefPtr<DOMSVGNumberList> kungFuDeathGrip;
-  if (oldLength && !aNewLength) {
+  if (aNewLength < oldLength) {
     // RemovingFromList() might clear last reference to |this|.
     // Retain a temporary reference to keep from dying before returning.
     kungFuDeathGrip = this;
   }
 
   // If our length will decrease, notify the items that will be removed:
   for (PRUint32 i = aNewLength; i < oldLength; ++i) {
     if (mItems[i]) {
--- a/content/svg/content/src/DOMSVGPathSegList.cpp
+++ b/content/svg/content/src/DOMSVGPathSegList.cpp
@@ -151,17 +151,17 @@ DOMSVGPathSegList::InternalListWillChang
   PRUint32 index = 0;
 
   PRUint32 dataLength = aNewValue.mData.Length();
   PRUint32 dataIndex = 0; // index into aNewValue's raw data array
 
   PRUint32 newSegType;
 
   nsRefPtr<DOMSVGPathSegList> kungFuDeathGrip;
-  if (length && aNewValue.IsEmpty()) {
+  if (aNewValue.Length() < length) {
     // RemovingFromList() might clear last reference to |this|.
     // Retain a temporary reference to keep from dying before returning.
     kungFuDeathGrip = this;
   }
 
   while (index < length && dataIndex < dataLength) {
     newSegType = SVGPathSegUtils::DecodeType(aNewValue.mData[dataIndex]);
     if (ItemAt(index) && ItemAt(index)->Type() != newSegType) {
--- a/content/svg/content/src/DOMSVGPointList.cpp
+++ b/content/svg/content/src/DOMSVGPointList.cpp
@@ -143,17 +143,17 @@ DOMSVGPointList::InternalListWillChangeT
   PRUint32 newLength = aNewValue.Length();
   if (newLength > DOMSVGPoint::MaxListIndex()) {
     // It's safe to get out of sync with our internal list as long as we have
     // FEWER items than it does.
     newLength = DOMSVGPoint::MaxListIndex();
   }
 
   nsRefPtr<DOMSVGPointList> kungFuDeathGrip;
-  if (oldLength && !newLength) {
+  if (newLength < oldLength) {
     // RemovingFromList() might clear last reference to |this|.
     // Retain a temporary reference to keep from dying before returning.
     kungFuDeathGrip = this;
   }
 
   // If our length will decrease, notify the items that will be removed:
   for (PRUint32 i = newLength; i < oldLength; ++i) {
     if (mItems[i]) {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9187,17 +9187,27 @@ nsresult nsDocShell::DoChannelLoad(nsICh
     nsLoadFlags loadFlags = 0;
     (void) aChannel->GetLoadFlags(&loadFlags);
     loadFlags |= nsIChannel::LOAD_DOCUMENT_URI |
                  nsIChannel::LOAD_CALL_CONTENT_SNIFFERS;
 
     // Load attributes depend on load type...
     switch (mLoadType) {
     case LOAD_HISTORY:
-        loadFlags |= nsIRequest::VALIDATE_NEVER;
+        {
+            // Only send VALIDATE_NEVER if mLSHE's URI was never changed via
+            // push/replaceState (bug 669671).
+            PRBool uriModified = PR_FALSE;
+            if (mLSHE) {
+                mLSHE->GetURIWasModified(&uriModified);
+            }
+
+            if (!uriModified)
+                loadFlags |= nsIRequest::VALIDATE_NEVER;
+        }
         break;
 
     case LOAD_RELOAD_CHARSET_CHANGE:
         loadFlags |= nsIRequest::LOAD_FROM_CACHE;
         break;
     
     case LOAD_RELOAD_NORMAL:
     case LOAD_REFRESH:
@@ -9849,16 +9859,25 @@ nsDocShell::AddState(nsIVariant *aData, 
         newSHEntry->SetURI(newURI);
     }
 
     // Step 4: Modify new/original session history entry and clear its POST
     // data, if there is any.
     newSHEntry->SetStateData(scContainer);
     newSHEntry->SetPostData(nsnull);
 
+    // If this push/replaceState changed the document's current URI and the new
+    // URI differs from the old URI in more than the hash, or if the old
+    // SHEntry's URI was modified in this way by a push/replaceState call
+    // set URIWasModified to true for the current SHEntry (bug 669671).
+    PRBool sameExceptHashes = PR_TRUE, oldURIWasModified = PR_FALSE;
+    newURI->EqualsExceptRef(mCurrentURI, &sameExceptHashes);
+    oldOSHE->GetURIWasModified(&oldURIWasModified);
+    newSHEntry->SetURIWasModified(!sameExceptHashes || oldURIWasModified);
+
     // Step 5: If aReplace is false, indicating that we're doing a pushState
     // rather than a replaceState, notify bfcache that we've added a page to
     // the history so it can evict content viewers if appropriate.
     if (!aReplace) {
         nsCOMPtr<nsISHistory> rootSH;
         GetRootSessionHistory(getter_AddRefs(rootSH));
         NS_ENSURE_TRUE(rootSH, NS_ERROR_UNEXPECTED);
 
--- a/docshell/shistory/public/nsISHEntry.idl
+++ b/docshell/shistory/public/nsISHEntry.idl
@@ -54,17 +54,17 @@ interface nsIStructuredCloneContainer;
 %{C++
 struct nsIntRect;
 class nsDocShellEditorData;
 %}
 [ref] native nsIntRect(nsIntRect);
 [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
 
 
-[scriptable, uuid(5f3ebf43-6944-45fb-b1b1-78a05bf9370b)]
+[scriptable, uuid(b92d403e-f5ec-4b81-b0e3-6e6c241cef2d)]
 interface nsISHEntry : nsIHistoryEntry
 {
     /** URI for the document */
     void setURI(in nsIURI aURI);
 
     /** Referrer URI */
     attribute nsIURI referrerURI;
 
@@ -164,16 +164,29 @@ interface nsISHEntry : nsIHistoryEntry
     /** attribute to indicate whether the page is already expired in cache */
     attribute boolean expirationStatus;
 
     /**
      * attribute to indicate the content-type of the document that this
      * is a session history entry for
      */
     attribute ACString contentType; 
+
+    /**
+     * If we created this SHEntry via history.pushState or modified it via
+     * history.replaceState, and if we changed the SHEntry's URI via the
+     * push/replaceState call, and if the SHEntry's new URI differs from its
+     * old URI by more than just the hash, then we set this field to true.
+     *
+     * Additionally, if this SHEntry was created by calling pushState from a
+     * SHEntry whose URI was modified, this SHEntry's URIWasModified field is
+     * true.
+     *
+     */
+    attribute boolean URIWasModified;
  
     /** Set/Get scrollers' positon in anchored pages */
     void setScrollPosition(in long x, in long y);
     void getScrollPosition(out long x, out long y);
 
     /** Additional ways to create an entry */
     [noscript] void create(in nsIURI URI, in AString title,
                            in nsIInputStream inputStream,
--- a/docshell/shistory/src/nsSHEntry.cpp
+++ b/docshell/shistory/src/nsSHEntry.cpp
@@ -103,16 +103,17 @@ static void StopTrackingEntry(nsSHEntry 
 
 
 nsSHEntry::nsSHEntry() 
   : mLoadType(0)
   , mID(gEntryID++)
   , mDocIdentifier(gEntryDocIdentifier++)
   , mScrollPositionX(0)
   , mScrollPositionY(0)
+  , mURIWasModified(PR_FALSE)
   , mIsFrameNavigation(PR_FALSE)
   , mSaveLayoutState(PR_TRUE)
   , mExpired(PR_FALSE)
   , mSticky(PR_TRUE)
   , mDynamicallyCreated(PR_FALSE)
   , mParent(nsnull)
   , mViewerBounds(0, 0, 0, 0)
   , mDocShellID(0)
@@ -127,16 +128,17 @@ nsSHEntry::nsSHEntry(const nsSHEntry &ot
   , mTitle(other.mTitle)
   , mPostData(other.mPostData)
   , mLayoutHistoryState(other.mLayoutHistoryState)
   , mLoadType(0)         // XXX why not copy?
   , mID(other.mID)
   , mDocIdentifier(other.mDocIdentifier)
   , mScrollPositionX(0)  // XXX why not copy?
   , mScrollPositionY(0)  // XXX why not copy?
+  , mURIWasModified(other.mURIWasModified)
   , mIsFrameNavigation(other.mIsFrameNavigation)
   , mSaveLayoutState(other.mSaveLayoutState)
   , mExpired(other.mExpired)
   , mSticky(PR_TRUE)
   , mDynamicallyCreated(other.mDynamicallyCreated)
   // XXX why not copy mContentType?
   , mCacheKey(other.mCacheKey)
   , mParent(other.mParent)
@@ -203,16 +205,28 @@ NS_IMETHODIMP nsSHEntry::SetScrollPositi
 
 NS_IMETHODIMP nsSHEntry::GetScrollPosition(PRInt32 *x, PRInt32 *y)
 {
   *x = mScrollPositionX;
   *y = mScrollPositionY;
   return NS_OK;
 }
 
+NS_IMETHODIMP nsSHEntry::GetURIWasModified(PRBool* aOut)
+{
+  *aOut = mURIWasModified;
+  return NS_OK;
+}
+
+NS_IMETHODIMP nsSHEntry::SetURIWasModified(PRBool aIn)
+{
+  mURIWasModified = aIn;
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsSHEntry::GetURI(nsIURI** aURI)
 {
   *aURI = mURI;
   NS_IF_ADDREF(*aURI);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsSHEntry::SetURI(nsIURI* aURI)
--- a/docshell/shistory/src/nsSHEntry.h
+++ b/docshell/shistory/src/nsSHEntry.h
@@ -97,16 +97,17 @@ private:
   nsCOMPtr<nsIInputStream>        mPostData;
   nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
   nsCOMArray<nsISHEntry>          mChildren;
   PRUint32                        mLoadType;
   PRUint32                        mID;
   PRInt64                         mDocIdentifier;
   PRInt32                         mScrollPositionX;
   PRInt32                         mScrollPositionY;
+  PRPackedBool                    mURIWasModified;
   PRPackedBool                    mIsFrameNavigation;
   PRPackedBool                    mSaveLayoutState;
   PRPackedBool                    mExpired;
   PRPackedBool                    mSticky;
   PRPackedBool                    mDynamicallyCreated;
   nsCString                       mContentType;
   nsCOMPtr<nsISupports>           mCacheKey;
   nsISHEntry *                    mParent;  // weak reference
--- a/docshell/test/Makefile.in
+++ b/docshell/test/Makefile.in
@@ -111,16 +111,18 @@ include $(topsrcdir)/config/rules.mk
 		file_bug660404^headers^ \
 		test_bug662170.html \
 		file_bug662170.html \
 		test_bug570341.html \
 		bug570341_recordevents.html \
 		test_bug668513.html \
 		bug668513_redirect.html \
 		bug668513_redirect.html^headers^ \
+		test_bug669671.html \
+		file_bug669671.sjs \
 		$(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 _TEST_FILES += \
 		test_bug511449.html \
 		file_bug511449.html \
 		$(NULL)
 endif
new file mode 100644
--- /dev/null
+++ b/docshell/test/file_bug669671.sjs
@@ -0,0 +1,13 @@
+function handleRequest(request, response)
+{
+  var count = parseInt(getState('count'));
+  if (!count)
+    count = 0;
+  setState('count', count + 1 + '');
+
+  response.setHeader('Content-Type', 'text/html', false);
+  response.setHeader('Cache-Control', 'max-age=0');
+  response.write('<html><body onload="opener.onChildLoad()" ' +
+                 'onunload="parseInt(\'0\')">' +
+                 count + '</body></html>');
+}
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bug669671.html
@@ -0,0 +1,140 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=669671
+-->
+<head>
+  <title>Test for Bug 669671</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=669671">Mozilla Bug 669671</a>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+<script type="application/javascript;version=1.7">
+
+/**
+ * Test for Bug 669671.
+ *
+ * This is a bit complicated.  We have a script, file_bug669671.sjs, which counts
+ * how many times it's loaded and returns that count in the body of an HTML
+ * document.  For brevity, call this page X.
+ *
+ * X is sent with Cache-Control: max-age=0 and can't be bfcached (it has an
+ * onunload handler).  Our test does the following in a popup:
+ *
+ * 1) Load X?pushed, to prime the cache.
+ * 2) Navigate to X.
+ * 3) Call pushState and navigate from X to X?pushed.
+ * 4) Navigate to X?navigated.
+ * 5) Go back (to X?pushed).
+ *
+ * We do all this work so we can check that in step 5, we fetch X?pushed from
+ * the network -- we shouldn't use our cached copy, because of the
+ * cache-control header X sends.
+ *
+ * Then we go back and repeat the whole process but call history.replaceState
+ * instead of pushState.  And for good measure, we test once more, this time
+ * modifying only the hash of the URI using replaceState.  In this case, we
+ * *should* load from the cache.
+ *
+ **/
+SimpleTest.waitForExplicitFinish();
+
+function onChildLoad()
+{
+  SimpleTest.executeSoon(function() { gGen.next() });
+}
+
+var _loadCount = 0;
+function checkPopupLoadCount()
+{
+  is(popup.document.body.innerHTML, _loadCount + '', 'Load count');
+
+  // We normally want to increment _loadCount here.  But if the test fails
+  // because we didn't do a load we should have, let's not cause a cascade of
+  // failures by incrementing _loadCount.
+  var origCount = _loadCount;
+  if (popup.document.body.innerHTML >= _loadCount + '')
+    _loadCount++;
+  return origCount;
+}
+
+function test()
+{
+  // Step 1 - The popup's body counts how many times we've requested the
+  // resource.  This is the first time we've requested it, so it should be '0'.
+  checkPopupLoadCount();
+
+  // Step 2 - We'll get another onChildLoad when this finishes.
+  popup.location = 'file_bug669671.sjs';
+  yield;
+
+  // Step 3 - Call pushState and change the URI back to ?pushed.
+  checkPopupLoadCount();
+  popup.history.pushState('', '', '?pushed');
+
+  // Step 4 - Navigate away.  This should trigger another onChildLoad.
+  popup.location = 'file_bug669671.sjs?navigated-1';
+  yield;
+
+  // Step 5 - Go back.  This should result in another onload (because the file is
+  // not in bfcache) and should be the fourth time we've requested the sjs file.
+  checkPopupLoadCount();
+  popup.back();
+  yield;
+
+  // This is the check which was failing before we fixed the bug.
+  checkPopupLoadCount();
+
+  popup.close();
+
+  // Do the whole thing again, but with replaceState.
+  popup = window.open('file_bug669671.sjs?replaced');
+  yield;
+  checkPopupLoadCount();
+  popup.location = 'file_bug669671.sjs';
+  yield;
+  checkPopupLoadCount();
+  popup.history.replaceState('', '', '?replaced');
+  popup.location = 'file_bug669671.sjs?navigated-2';
+  yield;
+  checkPopupLoadCount();
+  popup.back();
+  yield;
+  checkPopupLoadCount();
+  popup.close();
+
+  // Once more, with feeling.  Notice that we don't have to prime the cache
+  // with an extra load here, because X and X#hash share the same cache entry.
+  popup = window.open('file_bug669671.sjs?hash-test');
+  yield;
+  var initialCount = checkPopupLoadCount();
+  popup.history.replaceState('', '', '#hash');
+  popup.location = 'file_bug669671.sjs?navigated-3';
+  yield;
+  checkPopupLoadCount();
+  popup.back();
+  yield;
+  is(popup.document.body.innerHTML, initialCount + '',
+     'Load count (should be cached)');
+  popup.close();
+
+  SimpleTest.finish();
+  yield;
+}
+
+// This will call into onChildLoad once it loads.
+var popup = window.open('file_bug669671.sjs?pushed');
+
+var gGen = test();
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/base/nsDOMMemoryReporter.cpp
+++ b/dom/base/nsDOMMemoryReporter.cpp
@@ -49,41 +49,41 @@ NS_IMPL_ISUPPORTS1(nsDOMMemoryReporter, 
 void
 nsDOMMemoryReporter::Init()
 {
   // The memory reporter manager is going to own this object.
   NS_RegisterMemoryReporter(new nsDOMMemoryReporter());
 }
 
 NS_IMETHODIMP
-nsDOMMemoryReporter::GetProcess(char** aProcess)
+nsDOMMemoryReporter::GetProcess(nsACString &aProcess)
 {
   // "" means the main process.
-  *aProcess = strdup("");
+  aProcess.Truncate();
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMMemoryReporter::GetPath(char** aMemoryPath)
+nsDOMMemoryReporter::GetPath(nsACString &aMemoryPath)
 {
-  *aMemoryPath = strdup("explicit/dom");
+  aMemoryPath.AssignLiteral("explicit/dom");
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMMemoryReporter::GetKind(int* aKind)
+nsDOMMemoryReporter::GetKind(PRInt32* aKind)
 {
   *aKind = KIND_HEAP;
   return NS_OK;
 }
 
 NS_IMETHODIMP
-nsDOMMemoryReporter::GetDescription(char** aDescription)
+nsDOMMemoryReporter::GetDescription(nsACString &aDescription)
 {
-  *aDescription = strdup("Memory used by the DOM.");
+  aDescription.AssignLiteral("Memory used by the DOM.");
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMMemoryReporter::GetUnits(PRInt32* aUnits)
 {
   *aUnits = UNITS_BYTES;
   return NS_OK;
--- a/dom/base/nsDOMMemoryReporter.h
+++ b/dom/base/nsDOMMemoryReporter.h
@@ -39,25 +39,19 @@
 #define nsDOMMemoryReporter_h__
 
 #include "nsIMemoryReporter.h"
 
 
 class nsDOMMemoryReporter: public nsIMemoryReporter {
 public:
   NS_DECL_ISUPPORTS
+  NS_DECL_NSIMEMORYREPORTER
 
   static void Init();
 
-  NS_IMETHOD GetProcess(char** aProcess);
-  NS_IMETHOD GetPath(char** aMemoryPath);
-  NS_IMETHOD GetKind(int* aKnd);
-  NS_IMETHOD GetDescription(char** aDescription);
-  NS_IMETHOD GetUnits(PRInt32* aUnits);
-  NS_IMETHOD GetAmount(PRInt64* aAmount);
-
 private:
   // Protect ctor, use Init() instead.
   nsDOMMemoryReporter();
 };
 
 #endif // nsDOMMemoryReporter_h__
 
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -180,33 +180,38 @@ static PRBool sDidShutdown;
 static PRInt32 sContextCount;
 
 static PRTime sMaxScriptRunTime;
 static PRTime sMaxChromeScriptRunTime;
 
 static nsIScriptSecurityManager *sSecurityManager;
 
 // nsMemoryPressureObserver observes the memory-pressure notifications
-// and forces a garbage collection and cycle collection when it happens.
+// and forces a garbage collection and cycle collection when it happens, if
+// the appropriate pref is set.
+
+static PRBool sGCOnMemoryPressure;
 
 class nsMemoryPressureObserver : public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
 };
 
 NS_IMPL_ISUPPORTS1(nsMemoryPressureObserver, nsIObserver)
 
 NS_IMETHODIMP
 nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic,
                                   const PRUnichar* aData)
 {
-  nsJSContext::GarbageCollectNow();
-  nsJSContext::CycleCollectNow();
+  if (sGCOnMemoryPressure) {
+    nsJSContext::GarbageCollectNow();
+    nsJSContext::CycleCollectNow();
+  }
   return NS_OK;
 }
 
 /****************************************************************
  ************************** AutoFree ****************************
  ****************************************************************/
 
 class AutoFree {
@@ -3790,16 +3795,20 @@ nsJSRuntime::Init()
                                 "javascript.options.mem.gc_per_compartment");
   SetMemoryGCModePrefChangedCallback("javascript.options.mem.gc_per_compartment",
                                      nsnull);
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (!obs)
     return NS_ERROR_FAILURE;
 
+  Preferences::AddBoolVarCache(&sGCOnMemoryPressure,
+                               "javascript.options.gc_on_memory_pressure",
+                               PR_TRUE);
+
   nsIObserver* memPressureObserver = new nsMemoryPressureObserver();
   NS_ENSURE_TRUE(memPressureObserver, NS_ERROR_OUT_OF_MEMORY);
   obs->AddObserver(memPressureObserver, "memory-pressure", PR_FALSE);
 
   sIsInitialized = PR_TRUE;
 
   return NS_OK;
 }
--- a/dom/interfaces/html/nsIDOMHTMLDocument.idl
+++ b/dom/interfaces/html/nsIDOMHTMLDocument.idl
@@ -44,18 +44,19 @@
 %}
 
 /**
  * The nsIDOMHTMLDocument interface is the interface to a [X]HTML
  * document object.
  *
  * @see <http://www.whatwg.org/html/>
  */
+interface nsISelection;
 
-[scriptable, uuid(3c0ca40f-72c5-4d15-935e-ccaff7953f2c)]
+[scriptable, uuid(3ab3e856-361d-435a-8a4d-b462799945cd)]
 interface nsIDOMHTMLDocument : nsIDOMDocument
 {
   readonly attribute DOMString            URL;
            attribute DOMString            domain;
            attribute DOMString            cookie;
   // returns "BackCompat" if we're in quirks mode,
   // or "CSS1Compat" if we're in strict mode
   readonly attribute DOMString            compatMode;
@@ -127,17 +128,17 @@ interface nsIDOMHTMLDocument : nsIDOMDoc
 
   readonly attribute nsIDOMHTMLCollection anchors;
   readonly attribute nsIDOMHTMLCollection applets;
 
   void                      clear();
 
 
   // DOM Range
-  DOMString                 getSelection();
+  nsISelection                getSelection();
 
 
   // Mozilla extensions
   /**
    * @deprecated These are old Netscape 4 methods. Do not use,
    *             the implementation is no-op.
    */
   void                      captureEvents(in long eventFlags);
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -358,21 +358,21 @@ ContentChild::RecvPMemoryReportRequestCo
       nsCOMPtr<nsIMemoryReporter> r;
       e->GetNext(getter_AddRefs(r));
 
       nsCString path;
       PRInt32 kind;
       PRInt32 units;
       PRInt64 amount;
       nsCString desc;
-      r->GetPath(getter_Copies(path));
+      r->GetPath(path);
       r->GetKind(&kind);
       r->GetUnits(&units);
       r->GetAmount(&amount);
-      r->GetDescription(getter_Copies(desc));
+      r->GetDescription(desc);
 
       MemoryReport memreport(process, path, kind, units, amount, desc);
       reports.AppendElement(memreport);
     }
 
     // Then do the memory multi-reporters, by calling CollectReports on each
     // one, whereupon the callback will turn each measurement into a
     // MemoryReport.
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -84,19 +84,29 @@
 
         <activity android:name="Restarter"
                   android:process="@ANDROID_PACKAGE_NAME@Restarter"
                   android:theme="@style/GreyTheme">
           <intent-filter>
             <action android:name="org.mozilla.gecko.restart"/>
           </intent-filter>
         </activity>
+
 #if MOZ_CRASHREPORTER
 	<activity android:name="CrashReporter"
                   android:label="@string/crash_reporter_title"
 		  android:icon="@drawable/crash_reporter" >
           <intent-filter>
             <action android:name="org.mozilla.gecko.reportCrash" />
           </intent-filter>
 	</activity>
 #endif
+
+        <activity android:name="LauncherShortcuts"
+                  android:label="@string/launcher_shortcuts_title">
+            <!--  This intent-filter allows your shortcuts to be created in the launcher. -->
+            <intent-filter>
+                <action android:name="android.intent.action.CREATE_SHORTCUT" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
     </application>
 </manifest> 
--- a/embedding/android/GeckoApp.java
+++ b/embedding/android/GeckoApp.java
@@ -298,17 +298,17 @@ abstract public class GeckoApp
             String uri = intent.getDataString();
             GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
             Log.i("GeckoApp","onNewIntent: "+uri);
         }
         else if (Intent.ACTION_MAIN.equals(action)) {
             Log.i("GeckoApp", "Intent : ACTION_MAIN");
             GeckoAppShell.sendEventToGecko(new GeckoEvent(""));
         }
-        else if (action.equals("org.mozilla.fennec.WEBAPP")) {
+        else if (action.equals("org.mozilla.gecko.WEBAPP")) {
             String uri = intent.getStringExtra("args");
             GeckoAppShell.sendEventToGecko(new GeckoEvent(uri));
             Log.i("GeckoApp","Intent : WEBAPP - " + uri);
         }
     }
 
     @Override
     public void onPause()
--- a/embedding/android/GeckoAppShell.java
+++ b/embedding/android/GeckoAppShell.java
@@ -673,17 +673,17 @@ public class GeckoAppShell
         gRestartScheduled = true;
     }
 
     // "Installs" an application by creating a shortcut
     static void installWebApplication(String aURI, String aTitle, String aIconData) {
         Log.w("GeckoAppJava", "installWebApplication for " + aURI + " [" + aTitle + "]");
 
         // the intent to be launched by the shortcut
-        Intent shortcutIntent = new Intent("org.mozilla.fennec.WEBAPP");
+        Intent shortcutIntent = new Intent("org.mozilla.gecko.WEBAPP");
         shortcutIntent.setClassName(GeckoApp.mAppContext,
                                     GeckoApp.mAppContext.getPackageName() + ".App");
         shortcutIntent.putExtra("args", "--webapp=" + aURI);
 
         Intent intent = new Intent();
         intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
         intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, aTitle);
         byte[] raw = Base64.decode(aIconData.substring(22), Base64.DEFAULT);
new file mode 100644
--- /dev/null
+++ b/embedding/android/LauncherShortcuts.java.in
@@ -0,0 +1,184 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Android code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Vladimir Vukicevic <vladimir@pobox.com>
+ *   Wes Johnston <wjohnston@mozilla.com>
+ *   Mark Finkle <mfinkle@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#filter substitution
+package @ANDROID_PACKAGE_NAME@;
+
+import java.io.*;
+
+import org.mozilla.gecko.*;
+
+import android.os.*;
+import android.content.*;
+import android.app.*;
+import android.text.*;
+import android.util.*;
+import android.widget.*;
+import android.database.sqlite.*;
+import android.database.*;
+import android.view.*;
+import android.net.Uri;
+import android.graphics.*;
+
+
+public class LauncherShortcuts extends ListActivity {
+    public static final String CREATE_SHORTCUT = "org.mozilla.gecko.CREATE_SHORTCUT";
+
+    public class LauncherCursorAdapter extends SimpleCursorAdapter {
+        private Cursor _cursor;
+        private Context _context;
+
+        public LauncherCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
+            // Using the older, deprecated constructor so we can work on API < 11
+            super(context, layout, c, from, to);
+            _cursor = c;
+            _context = context;
+        }
+
+        @Override
+        public void bindView(View view, Context context, Cursor cursor) {
+            ImageView imageView = (ImageView) view.findViewById(R.id.favicon);
+    
+            String favicon = cursor.getString(3);
+            byte[] raw = Base64.decode(favicon.substring(22), Base64.DEFAULT);
+            Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(raw, 0, raw.length), 48, 48, true);
+            imageView.setImageBitmap(bitmap);
+
+            super.bindView(view, context, cursor);
+        }
+    }
+
+    private Cursor getCursor(Context context) {
+        File home = new File(context.getFilesDir(), "mozilla");
+        if (!home.exists())
+            return null;
+
+        File profile = null;
+        String[] files = home.list();
+        for (int i = 0; i < files.length; i++) {
+            if (files[i].endsWith(".default")) {
+                profile = new File(home, files[i]);
+                break;
+            }
+        }
+
+        if (profile == null)
+            return null;
+
+        File webapps = new File(profile, "webapps.sqlite");
+        if (!webapps.exists())
+            return null;
+
+        Log.i("LauncherShortcuts", "Opening: " + webapps.getPath());
+        mDb = SQLiteDatabase.openDatabase(webapps.getPath(), null, SQLiteDatabase.OPEN_READONLY | SQLiteDatabase.NO_LOCALIZED_COLLATORS);
+        return mDb.rawQuery("SELECT rowid as _id, title, uri, icon FROM webapps", null);
+    }
+    
+    private Cursor mCursor;
+    private SQLiteDatabase mDb;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.launch_app_list);
+
+        final Intent intent = getIntent();
+        final String action = intent.getAction();
+
+        if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
+            mCursor = getCursor(this);
+            if (mCursor != null) {
+                // After selecting an item, the empty view can flash on screen. Clear
+                // the text so we don't see it.
+                TextView emptyText = (TextView)findViewById(android.R.id.empty);
+                emptyText.setText("");
+
+                // Load the list using a custom adapter so we can create the bitmaps
+                ListAdapter adapter = new LauncherCursorAdapter(
+                    this,
+                    R.layout.launch_app_listitem,
+                    mCursor,
+                    new String[] {"title"},
+                    new int[] {R.id.title}
+                );
+                setListAdapter(adapter);
+            }
+        }
+    }
+
+    @Override
+    public void onListItemClick(ListView l, View v, int position, long id) {
+        mCursor.moveToPosition(position);
+
+        Intent shortcutintent = new Intent("org.mozilla.gecko.WEBAPP");
+        shortcutintent.setClass(this, App.class);
+        shortcutintent.putExtra("args", "--webapp=" + mCursor.getString(2));
+
+        Intent intent = new Intent();
+        intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, mCursor.getString(1));
+        intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutintent);
+
+        String favicon = mCursor.getString(3);
+        byte[] raw = Base64.decode(favicon.substring(22), Base64.DEFAULT);
+
+        DisplayMetrics dm = new DisplayMetrics();
+        getWindowManager().getDefaultDisplay().getMetrics(dm);
+        int size;
+        switch (dm.densityDpi) {
+            case DisplayMetrics.DENSITY_MEDIUM:
+                size = 48;
+                break;
+            case DisplayMetrics.DENSITY_HIGH:
+                size = 72;
+                break;
+            default:
+                size = 72;
+        }
+
+        Bitmap bitmap = Bitmap.createScaledBitmap(BitmapFactory.decodeByteArray(raw, 0, raw.length), size, size, true);
+        intent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
+
+        // Now, return the result to the launcher
+        setResult(RESULT_OK, intent);
+        mDb.close();
+        mCursor.close();
+        finish();
+    }
+}
--- a/embedding/android/Makefile.in
+++ b/embedding/android/Makefile.in
@@ -54,16 +54,17 @@ JAVAFILES = \
   GeckoInputConnection.java \
   AlertNotification.java \
   $(NULL)
 
 PROCESSEDJAVAFILES = \
   App.java \
   Restarter.java \
   NotificationHandler.java \
+  LauncherShortcuts.java \
   $(NULL)
 
 
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
@@ -111,16 +112,18 @@ ICON_PATH = $(topsrcdir)/$(MOZ_BRANDING_
 ICON_PATH_HDPI = $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/content/icon64.png
 DEFINES += -DMOZ_ANDROID_SHARED_ID="$(ANDROID_PACKAGE_NAME).sharedID"
 endif
 
 RES_LAYOUT = \
   res/layout/notification_progress.xml \
   res/layout/notification_progress_text.xml \
   res/layout/notification_icon_text.xml \
+  res/layout/launch_app_list.xml \
+  res/layout/launch_app_listitem.xml \
   $(NULL)
 
 RES_VALUES = res/values/colors.xml res/values/themes.xml
 
 AB_rCD = $(shell echo $(AB_CD) | sed -e s/-/-r/)
 
 JAVA_CLASSPATH = $(ANDROID_SDK)/android.jar
 
--- a/embedding/android/locales/en-US/android_strings.dtd
+++ b/embedding/android/locales/en-US/android_strings.dtd
@@ -11,8 +11,11 @@
 <!ENTITY  crash_help_message "Please help us fix this problem!">
 <!ENTITY  crash_send_report_message "Send Mozilla a crash report">
 <!ENTITY  crash_include_url "Include page address">
 <!ENTITY  crash_close_label "Close">
 <!ENTITY  crash_restart_label "Restart &brandShortName;">
 <!ENTITY  sending_crash_report "Sending crash report\u2026">
 <!ENTITY  exit_label "Exit">
 <!ENTITY  continue_label "Continue">
+
+<!ENTITY  launcher_shortcuts_title "&brandShortName; Web Apps">
+<!ENTITY  launcher_shortcuts_empty "No web apps were found">
new file mode 100644
--- /dev/null
+++ b/embedding/android/resources/layout/launch_app_list.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+         android:layout_width="match_parent"
+         android:layout_height="match_parent"
+         android:padding="3dip"
+         android:orientation="vertical"
+         android:windowIsFloating="true">
+
+    <ListView android:id="@android:id/list"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:layout_weight="1"
+              android:drawSelectorOnTop="false"/>
+
+    <TextView android:id="@android:id/empty"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent"
+              android:gravity="center"
+              android:text="@string/launcher_shortcuts_empty"/>
+ </LinearLayout>
new file mode 100644
--- /dev/null
+++ b/embedding/android/resources/layout/launch_app_listitem.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="?android:attr/listPreferredItemHeight"
+    android:padding="6dip"
+    android:orientation="horizontal">
+    <ImageView
+        android:id="@+id/favicon"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:layout_marginRight="6dip"/>
+    <TextView
+        android:id="@+id/title"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"/>
+ </LinearLayout>
--- a/embedding/android/strings.xml.in
+++ b/embedding/android/strings.xml.in
@@ -16,9 +16,12 @@
   <string name="crash_help_message">&crash_help_message;</string>
   <string name="crash_send_report_message">&crash_send_report_message;</string>
   <string name="crash_include_url">&crash_include_url;</string>
   <string name="crash_close_label">&crash_close_label;</string>
   <string name="crash_restart_label">&crash_restart_label;</string>
   <string name="sending_crash_report">&sending_crash_report;</string>
   <string name="exit_label">&exit_label;</string>
   <string name="continue_label">&continue_label;</string>
+
+  <string name="launcher_shortcuts_title">&launcher_shortcuts_title;</string>
+  <string name="launcher_shortcuts_empty">&launcher_shortcuts_empty;</string>
 </resources>
--- a/gfx/thebes/gfxASurface.cpp
+++ b/gfx/thebes/gfxASurface.cpp
@@ -595,23 +595,23 @@ class SurfaceMemoryReporter :
 {
 public:
     SurfaceMemoryReporter(gfxASurface::gfxSurfaceType aType)
         : mType(aType)
     { }
 
     NS_DECL_ISUPPORTS
 
-    NS_IMETHOD GetProcess(char **process) {
-        *process = strdup("");
+    NS_IMETHOD GetProcess(nsACString &process) {
+        process.Truncate();
         return NS_OK;
     }
 
-    NS_IMETHOD GetPath(char **memoryPath) {
-        *memoryPath = strdup(SurfaceMemoryReporterPathForType(mType));
+    NS_IMETHOD GetPath(nsACString &path) {
+        path.Assign(SurfaceMemoryReporterPathForType(mType));
         return NS_OK;
     }
 
     NS_IMETHOD GetKind(PRInt32 *kind) {
         *kind = KIND_OTHER;
         return NS_OK;
     }
     
@@ -620,18 +620,18 @@ public:
         return NS_OK;
     }
 
     NS_IMETHOD GetAmount(PRInt64 *amount) {
         *amount = gSurfaceMemoryUsed[mType];
         return NS_OK;
     }
 
-    NS_IMETHOD GetDescription(char **desc) {
-        *desc = strdup("Memory used by gfx surface of the given type.");
+    NS_IMETHOD GetDescription(nsACString &desc) {
+        desc.AssignLiteral("Memory used by gfx surface of the given type.");
         return NS_OK;
     }
 
     gfxASurface::gfxSurfaceType mType;
 };
 
 NS_IMPL_ISUPPORTS1(SurfaceMemoryReporter, nsIMemoryReporter)
 
--- a/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
+++ b/ipc/chromium/src/chrome/common/ipc_channel_posix.cc
@@ -476,29 +476,31 @@ bool Channel::ChannelImpl::ProcessIncomi
           }
           break;
         }
       }
     }
 
     // Process messages from input buffer.
     const char *p;
+    const char *overflowp;
     const char *end;
     if (input_overflow_buf_.empty()) {
+      overflowp = NULL;
       p = input_buf_;
       end = p + bytes_read;
     } else {
       if (input_overflow_buf_.size() >
          static_cast<size_t>(kMaximumMessageSize - bytes_read)) {
         input_overflow_buf_.clear();
         LOG(ERROR) << "IPC message is too big";
         return false;
       }
       input_overflow_buf_.append(input_buf_, bytes_read);
-      p = input_overflow_buf_.data();
+      overflowp = p = input_overflow_buf_.data();
       end = p + input_overflow_buf_.size();
     }
 
     // A pointer to an array of |num_fds| file descriptors which includes any
     // fds that have spilled over from a previous read.
     const int* fds;
     unsigned num_fds;
     unsigned fds_i = 0;  // the index of the first unused descriptor
@@ -566,17 +568,25 @@ bool Channel::ChannelImpl::ProcessIncomi
           listener_->OnMessageReceived(m);
         }
         p = message_tail;
       } else {
         // Last message is partial.
         break;
       }
     }
-    input_overflow_buf_.assign(p, end - p);
+    if (end == p) {
+      input_overflow_buf_.clear();
+    } else if (!overflowp) {
+      // p is from input_buf_
+      input_overflow_buf_.assign(p, end - p);
+    } else if (p > overflowp) {
+      // p is from input_overflow_buf_
+      input_overflow_buf_.erase(0, p - overflowp);
+    }
     input_overflow_fds_ = std::vector<int>(&fds[fds_i], &fds[num_fds]);
 
     // When the input data buffer is empty, the overflow fds should be too. If
     // this is not the case, we probably have a rogue renderer which is trying
     // to fill our descriptor table.
     if (input_overflow_buf_.empty() && !input_overflow_fds_.empty()) {
       // We close these descriptors in Close()
       return false;
--- a/ipc/glue/SharedMemory.cpp
+++ b/ipc/glue/SharedMemory.cpp
@@ -35,16 +35,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include <math.h>
 
+#include "nsString.h"
 #include "nsIMemoryReporter.h"
 #include "mozilla/ipc/SharedMemory.h"
 
 namespace mozilla {
 namespace ipc {
 
 static PRInt64 gShmemAllocated;
 static PRInt64 gShmemMapped;
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -145,16 +145,17 @@ CPPSRCS		= \
 		jsemit.cpp \
 		jsexn.cpp \
 		jsfriendapi.cpp \
 		jsfun.cpp \
 		jsgc.cpp \
 		jsgcmark.cpp \
 		jsgcchunk.cpp \
 		jsgcstats.cpp \
+		jscrashreport.cpp \
 		jshash.cpp \
 		jsinterp.cpp \
 		jsinvoke.cpp \
 		jsiter.cpp \
 		jslock.cpp \
 		jslog2.cpp \
 		jsmath.cpp \
 		jsnativestack.cpp \
@@ -201,16 +202,17 @@ INSTALLED_HEADERS = \
 		jsapi.h \
 		jsarena.h \
 		jsatom.h \
 		jsbit.h \
 		jsclist.h \
 		jsclone.h \
 		jscntxt.h \
 		jscompat.h \
+		jscrashreport.h \
 		jsdate.h \
 		jsdbgapi.h \
 		jsdhash.h \
 		jsemit.h \
 		jsfun.h \
 		jsfriendapi.h \
 		jsgc.h \
 		jscell.h \
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1178,16 +1178,26 @@ typedef struct JSCTypesCallbacks JSCType
  * pointer to static data that exists for the lifetime of 'ctypesObj', but it
  * may safely be altered after calling this function and without having
  * to call this function again.
  */
 extern JS_PUBLIC_API(JSBool)
 JS_SetCTypesCallbacks(JSContext *cx, JSObject *ctypesObj, JSCTypesCallbacks *callbacks);
 #endif
 
+typedef JSBool
+(* JSEnumerateDiagnosticMemoryCallback)(void *ptr, size_t length);
+
+/*
+ * Enumerate memory regions that contain diagnostic information
+ * intended to be included in crash report minidumps.
+ */
+extern JS_PUBLIC_API(void)
+JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback);
+
 /*
  * Macros to hide interpreter stack layout details from a JSFastNative using
  * its jsval *vp parameter. The stack layout underlying invocation can't change
  * without breaking source and binary compatibility (argv[-2] is well-known to
  * be the callee jsval, and argv[-1] is as well known to be |this|).
  *
  * Note well: However, argv[-1] may be JSVAL_NULL where with slow natives it
  * is the global object, so embeddings implementing fast natives *must* call
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -408,16 +408,22 @@ struct JSRuntime {
      * gcTriggerCompartment is reset to NULL and a global GC is performed.
      */
     JSCompartment       *gcTriggerCompartment;
 
     /* Compartment that is currently involved in per-compartment GC */
     JSCompartment       *gcCurrentCompartment;
 
     /*
+     * If this is non-NULL, all marked objects must belong to this compartment.
+     * This is used to look for compartment bugs.
+     */
+    JSCompartment       *gcCheckCompartment;
+
+    /*
      * We can pack these flags as only the GC thread writes to them. Atomic
      * updates to packed bytes are not guaranteed, so stores issued by one
      * thread may be lost due to unsynchronized read-modify-write cycles on
      * other threads.
      */
     bool                gcPoke;
     bool                gcMarkAndSweep;
     bool                gcRunning;
new file mode 100644
--- /dev/null
+++ b/js/src/jscrashformat.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * May 28, 2008.
+ *
+ * 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):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef jscrashformat_h___
+#define jscrashformat_h___
+
+#include <string.h>
+
+namespace js {
+namespace crash {
+
+const static int crash_cookie_len = 16;
+const static char crash_cookie[crash_cookie_len] = "*J*S*CRASHDATA*";
+
+/* These values are used for CrashHeader::id. */
+enum {
+    JS_CRASH_STACK_GC = 0x400,
+    JS_CRASH_STACK_ERROR = 0x401,
+    JS_CRASH_RING = 0x800
+};
+
+/*
+ * All the data here will be stored directly in the minidump, so we use
+ * platform-independent types. We also ensure that the size of every field is a
+ * multiple of 8 bytes, to guarantee that they won't be padded.
+ */
+
+struct CrashHeader
+{
+    char cookie[crash_cookie_len];
+
+    /* id of the crash data, chosen from the enum above. */
+    uint64 id;
+
+    CrashHeader(uint64 id) : id(id) { memcpy(cookie, crash_cookie, crash_cookie_len); }
+};
+
+struct CrashRegisters
+{
+    uint64 ip, sp, bp;
+};
+
+const static int crash_buffer_size = 32 * 1024;
+
+struct CrashStack
+{
+    CrashStack(uint64 id) : header(id) {}
+
+    CrashHeader header;
+    uint64 snaptime;      /* Unix time when the stack was snapshotted. */
+    CrashRegisters regs;  /* Register contents for the snapshot. */
+    uint64 stack_base;    /* Base address of stack at the time of snapshot. */
+    uint64 stack_len;     /* Extent of the stack. */
+    char stack[crash_buffer_size]; /* Contents of the stack. */
+};
+
+struct CrashRing
+{
+    CrashRing(uint64 id) : header(id), offset(0) { memset(buffer, 0, sizeof(buffer)); }
+
+    CrashHeader header;
+    uint64 offset; /* Next byte to be written in the buffer. */
+    char buffer[crash_buffer_size];
+};
+
+/* These are the tag values for each entry in the CrashRing. */
+enum {
+    JS_CRASH_TAG_GC = 0x200
+};
+
+} /* namespace crash */
+} /* namespace js */
+
+#endif
new file mode 100644
--- /dev/null
+++ b/js/src/jscrashreport.cpp
@@ -0,0 +1,280 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * May 28, 2008.
+ *
+ * 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):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsapi.h"
+#include "jscntxt.h"
+#include "jscrashreport.h"
+#include "jscrashformat.h"
+
+#include <time.h>
+
+namespace js {
+namespace crash {
+
+const static int stack_snapshot_max_size = 32768;
+
+#if defined(XP_WIN)
+
+#include <Windows.h>
+
+static bool
+GetStack(uint64 *stack, uint64 *stack_len, CrashRegisters *regs, char *buffer, size_t size)
+{
+    /* Try to figure out how big the stack is. */
+    char dummy;
+    MEMORY_BASIC_INFORMATION info;
+    if (VirtualQuery(reinterpret_cast<LPCVOID>(&dummy), &info, sizeof(info)) == 0)
+        return false;
+    if (info.State != MEM_COMMIT)
+        return false;
+
+    /* 256 is a fudge factor to account for the rest of GetStack's frame. */
+    uint64 p = uint64(&dummy) - 256;
+    uint64 len = stack_snapshot_max_size;
+
+    if (p + len > uint64(info.BaseAddress) + info.RegionSize)
+        len = uint64(info.BaseAddress) + info.RegionSize - p;
+
+    if (len > size)
+        len = size;
+
+    *stack = p;
+    *stack_len = len;
+
+    /* Get the register state. */
+#if JS_BITS_PER_WORD == 32
+    uint32 vip, vsp, vbp;
+    __asm {
+    Label:
+        mov [vbp], ebp;
+        mov [vsp], esp;
+        mov eax, [Label];
+        mov [vip], eax;
+    }
+    regs->ip = vip;
+    regs->sp = vsp;
+    regs->bp = vbp;
+#else
+    CONTEXT context;
+    RtlCaptureContext(&context);
+    regs->ip = context.Rip;
+    regs->sp = context.Rsp;
+    regs->bp = context.Rbp;
+#endif
+
+    memcpy(buffer, (void *)p, len);
+
+    return true;
+}
+
+#elif defined(__linux__) && (defined(__x86_64__) || defined(__i386__))
+
+#include <unistd.h>
+#include <ucontext.h>
+#include <sys/mman.h>
+
+static bool
+GetStack(uint64 *stack, uint64 *stack_len, CrashRegisters *regs, char *buffer, size_t size)
+{
+    /* 256 is a fudge factor to account for the rest of GetStack's frame. */
+    char dummy;
+    uint64 p = uint64(&dummy) - 256;
+    uint64 pgsz = getpagesize();
+    uint64 len = stack_snapshot_max_size;
+    p &= ~(pgsz - 1);
+
+    /* Try to figure out how big the stack is. */
+    while (len > 0) {
+	if (mlock((const void *)p, len) == 0) {
+	    munlock((const void *)p, len);
+	    break;
+	}
+	len -= pgsz;
+    }
+
+    if (len > size)
+        len = size;
+
+    *stack = p;
+    *stack_len = len;
+
+    /* Get the register state. */
+    ucontext_t context;
+    if (getcontext(&context) != 0)
+	return false;
+
+#if JS_BITS_PER_WORD == 64
+    regs->sp = (uint64)context.uc_mcontext.gregs[REG_RSP];
+    regs->bp = (uint64)context.uc_mcontext.gregs[REG_RBP];
+    regs->ip = (uint64)context.uc_mcontext.gregs[REG_RIP];
+#elif JS_BITS_PER_WORD == 32
+    regs->sp = (uint64)context.uc_mcontext.gregs[REG_ESP];
+    regs->bp = (uint64)context.uc_mcontext.gregs[REG_EBP];
+    regs->ip = (uint64)context.uc_mcontext.gregs[REG_EIP];
+#endif
+
+    memcpy(buffer, (void *)p, len);
+
+    return true;
+}
+
+#else
+
+static bool
+GetStack(uint64 *stack, uint64 *stack_len, CrashRegisters *regs, char *buffer, size_t size)
+{
+    return false;
+}
+
+#endif
+
+class Stack : private CrashStack
+{
+public:
+    Stack(uint64 id);
+
+    bool snapshot();
+};
+
+Stack::Stack(uint64 id)
+  : CrashStack(id)
+{
+}
+
+bool
+Stack::snapshot()
+{
+    snaptime = time(NULL);
+    return GetStack(&stack_base, &stack_len, &regs, stack, sizeof(stack));
+}
+
+class Ring : private CrashRing
+{
+public:
+    Ring(uint64 id);
+
+    void push(uint64 tag, void *data, size_t size);
+
+private:
+    size_t bufferSize() { return crash_buffer_size; }
+    void copyBytes(void *data, size_t size);
+};
+
+Ring::Ring(uint64 id)
+  : CrashRing(id)
+{
+}
+
+void
+Ring::push(uint64 tag, void *data, size_t size)
+{
+    uint64 t = time(NULL);
+
+    copyBytes(&tag, sizeof(uint64));
+    copyBytes(&t, sizeof(uint64));
+    copyBytes(data, size);
+    uint64 mysize = size;
+    copyBytes(&mysize, sizeof(uint64));
+}
+
+void
+Ring::copyBytes(void *data, size_t size)
+{
+    if (size >= bufferSize())
+        size = bufferSize();
+
+    if (offset + size > bufferSize()) {
+        size_t first = bufferSize() - offset;
+        size_t second = size - first;
+        memcpy(&buffer[offset], data, first);
+        memcpy(buffer, (char *)data + first, second);
+        offset = second;
+    } else {
+        memcpy(&buffer[offset], data, size);
+        offset += size;
+    }
+}
+
+static bool gInitialized;
+
+static Stack gGCStack(JS_CRASH_STACK_GC);
+static Stack gErrorStack(JS_CRASH_STACK_ERROR);
+static Ring gRingBuffer(JS_CRASH_RING);
+
+} /* namespace crash */
+} /* namespace js */
+
+using namespace js;
+using namespace js::crash;
+
+JS_FRIEND_API(void)
+js_SnapshotGCStack()
+{
+    if (gInitialized)
+        gGCStack.snapshot();
+}
+
+JS_FRIEND_API(void)
+js_SnapshotErrorStack()
+{
+    if (gInitialized)
+        gErrorStack.snapshot();
+}
+
+JS_FRIEND_API(void)
+js_SaveCrashData(uint64 tag, void *ptr, size_t size)
+{
+    if (gInitialized)
+        gRingBuffer.push(tag, ptr, size);
+}
+
+JS_PUBLIC_API(void)
+JS_EnumerateDiagnosticMemoryRegions(JSEnumerateDiagnosticMemoryCallback callback)
+{
+#if 1
+    if (!gInitialized) {
+        gInitialized = true;
+        (*callback)(&gGCStack, sizeof(gGCStack));
+        (*callback)(&gErrorStack, sizeof(gErrorStack));
+        (*callback)(&gRingBuffer, sizeof(gRingBuffer));
+    }
+#endif
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/jscrashreport.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=4 sw=4 et tw=99:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
+ * May 28, 2008.
+ *
+ * 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):
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef jscrashreport_h___
+#define jscrashreport_h___
+
+#include "jstypes.h"
+
+JS_BEGIN_EXTERN_C
+
+JS_FRIEND_API(void)
+js_SnapshotGCStack();
+
+JS_FRIEND_API(void)
+js_SnapshotErrorStack();
+
+JS_FRIEND_API(void)
+js_SaveCrashData(uint64 tag, void *ptr, size_t size);
+
+JS_END_EXTERN_C
+
+#endif /* jscrashreport_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -55,16 +55,18 @@
 #include "jsutil.h"
 #include "jshash.h"
 #include "jsbit.h"
 #include "jsclist.h"
 #include "jsprf.h"
 #include "jsapi.h"
 #include "jsatom.h"
 #include "jscompartment.h"
+#include "jscrashreport.h"
+#include "jscrashformat.h"
 #include "jscntxt.h"
 #include "jsversion.h"
 #include "jsdbgapi.h"
 #include "jsexn.h"
 #include "jsfun.h"
 #include "jsgc.h"
 #include "jsgcchunk.h"
 #include "jsgcmark.h"
@@ -234,19 +236,17 @@ Arena::finalize(JSContext *cx)
                     newListTail->end = thing - sizeof(T);
                     newListTail = newListTail->nextSpanUnchecked();
                     newFreeSpanStart = 0;
                 }
             } else {
                 if (!newFreeSpanStart)
                     newFreeSpanStart = thing;
                 t->finalize(cx);
-#ifdef DEBUG
                 memset(t, JS_FREE_PATTERN, sizeof(T));
-#endif
             }
         }
     }
 
     if (allClear) {
         JS_ASSERT(newListTail == &newListHead);
         JS_ASSERT(newFreeSpanStart == thingsStart(sizeof(T)));
         return true;
@@ -2657,16 +2657,22 @@ GCCycle(JSContext *cx, JSCompartment *co
     rt->setGCLastBytes(rt->gcBytes, gckind);
     rt->gcCurrentCompartment = NULL;
     rt->gcWeakMapList = NULL;
 
     for (JSCompartment **c = rt->compartments.begin(); c != rt->compartments.end(); ++c)
         (*c)->setGCLastBytes((*c)->gcBytes, gckind);
 }
 
+struct GCCrashData
+{
+    int isRegen;
+    int isCompartment;
+};
+
 void
 js_GC(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind)
 {
     JSRuntime *rt = cx->runtime;
 
     /*
      * Don't collect garbage if the runtime isn't up, and cx is not the last
      * context in the runtime.  The last context must force a GC, and nothing
@@ -2678,16 +2684,21 @@ js_GC(JSContext *cx, JSCompartment *comp
 
     if (JS_ON_TRACE(cx)) {
         JS_ASSERT(gckind != GC_LAST_CONTEXT);
         return;
     }
 
     RecordNativeStackTopForGC(cx);
 
+    GCCrashData crashData;
+    crashData.isRegen = rt->shapeGen & SHAPE_OVERFLOW_BIT;
+    crashData.isCompartment = !!comp;
+    js_SaveCrashData(crash::JS_CRASH_TAG_GC, &crashData, sizeof(crashData));
+
     GCTIMER_BEGIN(rt, comp);
 
     do {
         /*
          * Let the API user decide to defer a GC if it wants to (unless this
          * is the last context).  Invoke the callback regardless. Sample the
          * callback in case we are freely racing with a JS_SetGCCallback{,RT}
          * on another thread.
@@ -2716,16 +2727,18 @@ js_GC(JSContext *cx, JSCompartment *comp
          * stop creating garbage.
          */
     } while (gckind == GC_LAST_CONTEXT && rt->gcPoke);
 
     rt->gcNextFullGCTime = PRMJ_Now() + GC_IDLE_FULL_SPAN;
 
     rt->gcChunkAllocationSinceLastGC = false;
     GCTIMER_END(gckind == GC_LAST_CONTEXT);
+
+    js_SnapshotGCStack();
 }
 
 namespace js {
 
 class AutoCopyFreeListToArenas {
     JSRuntime *rt;
 
   public:
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -110,16 +110,22 @@ Mark(JSTracer *trc, T *thing)
 
     JS_ASSERT(!JSAtom::isStatic(thing));
     JS_ASSERT(thing->isAligned());
 
     JSRuntime *rt = trc->context->runtime;
     JS_ASSERT(thing->arenaHeader()->compartment);
     JS_ASSERT(thing->arenaHeader()->compartment->rt == rt);
 
+    if (rt->gcCheckCompartment && thing->compartment() != rt->gcCheckCompartment &&
+        thing->compartment() != rt->atomsCompartment)
+    {
+        JS_Assert("compartment mismatch in GC", __FILE__, __LINE__);
+    }
+
     /*
      * Don't mark things outside a compartment if we are in a per-compartment
      * GC.
      */
     if (!rt->gcCurrentCompartment || thing->compartment() == rt->gcCurrentCompartment) {
         if (IS_GC_MARKING_TRACER(trc))
             PushMarkStack(static_cast<GCMarker *>(trc), thing);
         else
@@ -154,16 +160,26 @@ MarkObject(JSTracer *trc, JSObject &obj,
 {
     JS_ASSERT(trc);
     JS_ASSERT(&obj);
     JS_SET_TRACING_NAME(trc, name);
     Mark(trc, &obj);
 }
 
 void
+MarkCrossCompartmentObject(JSTracer *trc, JSObject &obj, const char *name)
+{
+    JSRuntime *rt = trc->context->runtime;
+    if (rt->gcCurrentCompartment && rt->gcCurrentCompartment != obj.compartment())
+        return;
+
+    MarkObject(trc, obj, name);
+}
+
+void
 MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
 		      const void *arg, size_t index)
 {
     JS_ASSERT(trc);
     JS_ASSERT(&obj);
     JS_SET_TRACING_DETAILS(trc, printer, arg, index);
     Mark(trc, &obj);
 }
@@ -347,16 +363,32 @@ MarkValueRaw(JSTracer *trc, const js::Va
 void
 MarkValue(JSTracer *trc, const js::Value &v, const char *name)
 {
     JS_SET_TRACING_NAME(trc, name);
     MarkValueRaw(trc, v);
 }
 
 void
+MarkCrossCompartmentValue(JSTracer *trc, const js::Value &v, const char *name)
+{
+    if (v.isMarkable()) {
+        js::gc::Cell *cell = (js::gc::Cell *)v.toGCThing();
+        unsigned kind = v.gcKind();
+        if (kind == JSTRACE_STRING && ((JSString *)cell)->isStaticAtom())
+            return;
+        JSRuntime *rt = trc->context->runtime;
+        if (rt->gcCurrentCompartment && cell->compartment() != rt->gcCurrentCompartment)
+            return;
+
+        MarkValue(trc, v, name);
+    }
+}
+
+void
 MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name)
 {
     for (Value *vp = beg; vp < end; ++vp) {
         JS_SET_TRACING_INDEX(trc, name, vp - beg);
         MarkValueRaw(trc, *vp);
     }
 }
 
@@ -743,16 +775,19 @@ MarkChildren(JSTracer *trc, JSXML *xml)
 }
 #endif
 
 } /* namespace gc */
 
 void
 GCMarker::drainMarkStack()
 {
+    JSRuntime *rt = context->runtime;
+    rt->gcCheckCompartment = rt->gcCurrentCompartment;
+
     while (!isMarkStackEmpty()) {
         while (!ropeStack.isEmpty())
             ScanRope(this, ropeStack.pop());
 
         while (!objStack.isEmpty())
             ScanObject(this, objStack.pop());
 
         while (!xmlStack.isEmpty())
@@ -767,16 +802,18 @@ GCMarker::drainMarkStack()
         if (isMarkStackEmpty()) {
             /*
              * Mark children of things that caused too deep recursion during the above
              * tracing. Don't do this until we're done with everything else.
              */
             markDelayedChildren();
         }
     }
+
+    rt->gcCheckCompartment = NULL;
 }
 
 } /* namespace js */
 
 JS_PUBLIC_API(void)
 JS_TraceChildren(JSTracer *trc, void *thing, uint32 kind)
 {
     switch (kind) {
--- a/js/src/jsgcmark.h
+++ b/js/src/jsgcmark.h
@@ -57,16 +57,23 @@ void
 MarkString(JSTracer *trc, JSString *str);
 
 void
 MarkString(JSTracer *trc, JSString *str, const char *name);
 
 void
 MarkObject(JSTracer *trc, JSObject &obj, const char *name);
 
+/*
+ * Mark an object that may be in a different compartment from the compartment
+ * being GC'd. (Although it won't be marked if it's in the wrong compartment.)
+ */
+void
+MarkCrossCompartmentObject(JSTracer *trc, JSObject &obj, const char *name);
+
 void
 MarkObjectWithPrinter(JSTracer *trc, JSObject &obj, JSTraceNamePrinter printer,
 		      const void *arg, size_t index);
 
 void
 MarkShape(JSTracer *trc, const Shape *shape, const char *name);
 
 void
@@ -97,16 +104,23 @@ void
 MarkKind(JSTracer *trc, void *thing, uint32 kind);
 
 void
 MarkValueRaw(JSTracer *trc, const js::Value &v);
 
 void
 MarkValue(JSTracer *trc, const js::Value &v, const char *name);
 
+/*
+ * Mark a value that may be in a different compartment from the compartment
+ * being GC'd. (Although it won't be marked if it's in the wrong compartment.)
+ */
+void
+MarkCrossCompartmentValue(JSTracer *trc, const js::Value &v, const char *name);
+
 void
 MarkValueRange(JSTracer *trc, Value *beg, Value *end, const char *name);
 
 void
 MarkValueRange(JSTracer *trc, size_t len, Value *vec, const char *name);
 
 void
 MarkShapeRange(JSTracer *trc, const Shape **beg, const Shape **end, const char *name);
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -975,30 +975,30 @@ proxy_DeleteProperty(JSContext *cx, JSOb
     rval->setBoolean(deleted);
     return true;
 }
 
 static void
 proxy_TraceObject(JSTracer *trc, JSObject *obj)
 {
     obj->getProxyHandler()->trace(trc, obj);
-    MarkValue(trc, obj->getProxyPrivate(), "private");
-    MarkValue(trc, obj->getProxyExtra(), "extra");
+    MarkCrossCompartmentValue(trc, obj->getProxyPrivate(), "private");
+    MarkCrossCompartmentValue(trc, obj->getProxyExtra(), "extra");
     if (obj->isFunctionProxy()) {
-        MarkValue(trc, GetCall(obj), "call");
-        MarkValue(trc, GetConstruct(obj), "construct");
+        MarkCrossCompartmentValue(trc, GetCall(obj), "call");
+        MarkCrossCompartmentValue(trc, GetConstruct(obj), "construct");
     }
 }
 
 static void
 proxy_TraceFunction(JSTracer *trc, JSObject *obj)
 {
     proxy_TraceObject(trc, obj);
-    MarkValue(trc, GetCall(obj), "call");
-    MarkValue(trc, GetConstruct(obj), "construct");
+    MarkCrossCompartmentValue(trc, GetCall(obj), "call");
+    MarkCrossCompartmentValue(trc, GetConstruct(obj), "construct");
 }
 
 static JSBool
 proxy_Convert(JSContext *cx, JSObject *proxy, JSType hint, Value *vp)
 {
     JS_ASSERT(proxy->isProxy());
     return JSProxy::defaultValue(cx, proxy, hint, vp);
 }
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -40,30 +40,33 @@
 /*
  * PR assertion checker.
  */
 
 #ifndef jsutil_h___
 #define jsutil_h___
 
 #include "jstypes.h"
+#include "jscrashreport.h"
 #include "mozilla/Util.h"
 #include <stdlib.h>
 #include <string.h>
 
 JS_BEGIN_EXTERN_C
 
 #define JS_CRASH_UNLESS(__cond)                                                 \
     JS_BEGIN_MACRO                                                              \
         if (!(__cond)) {                                                        \
             *(int *)(uintptr_t)0xccadbeef = 0;                                  \
             ((void(*)())0)(); /* More reliable, but doesn't say CCADBEEF */     \
         }                                                                       \
     JS_END_MACRO
 
+#define JS_FREE_PATTERN 0xDA
+
 #ifdef DEBUG
 
 #define JS_ASSERT(expr)                                                       \
     ((expr) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
 
 #define JS_ASSERT_IF(cond, expr)                                              \
     ((!(cond) || (expr)) ? (void)0 : JS_Assert(#expr, __FILE__, __LINE__))
 
@@ -75,18 +78,16 @@ JS_BEGIN_EXTERN_C
 #define JS_ALWAYS_FALSE(expr) JS_ASSERT(!(expr))
 
 # ifdef JS_THREADSAFE
 # define JS_THREADSAFE_ASSERT(expr) JS_ASSERT(expr) 
 # else
 # define JS_THREADSAFE_ASSERT(expr) ((void) 0)
 # endif
 
-#define JS_FREE_PATTERN 0xDA
-
 #else
 
 #define JS_ASSERT(expr)         ((void) 0)
 #define JS_ASSERT_IF(cond,expr) ((void) 0)
 #define JS_NOT_REACHED(reason)
 #define JS_ALWAYS_TRUE(expr)    ((void) (expr))
 #define JS_ALWAYS_FALSE(expr)    ((void) (expr))
 #define JS_THREADSAFE_ASSERT(expr) ((void) 0)
@@ -215,34 +216,40 @@ extern JS_PUBLIC_DATA(JSUint32) OOM_coun
             return NULL; \
         } \
     } while (0)
 
 #else
 #define JS_OOM_POSSIBLY_FAIL() do {} while(0)
 #endif
 
+static JS_INLINE void *js_record_oom(void *p) {
+    if (!p)
+        js_SnapshotErrorStack();
+    return p;
+}
+
 /*
  * SpiderMonkey code should not be calling these allocation functions directly.
  * Instead, all calls should go through JSRuntime, JSContext or OffTheBooks.
  * However, js_free() can be called directly.
  */
 static JS_INLINE void* js_malloc(size_t bytes) {
     JS_OOM_POSSIBLY_FAIL();
-    return malloc(bytes);
+    return js_record_oom(malloc(bytes));
 }
 
 static JS_INLINE void* js_calloc(size_t bytes) {
     JS_OOM_POSSIBLY_FAIL();
-    return calloc(bytes, 1);
+    return js_record_oom(calloc(bytes, 1));
 }
 
 static JS_INLINE void* js_realloc(void* p, size_t bytes) {
     JS_OOM_POSSIBLY_FAIL();
-    return realloc(p, bytes);
+    return js_record_oom(realloc(p, bytes));
 }
 
 static JS_INLINE void js_free(void* p) {
     free(p);
 }
 #endif/* JS_USE_CUSTOM_ALLOCATOR */
 
 JS_END_EXTERN_C
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -763,9 +763,15 @@ JSCrossCompartmentWrapper::defaultValue(
 
     if (!JSWrapper::defaultValue(cx, wrapper, hint, vp))
         return false;
 
     call.leave();
     return call.origin->wrap(cx, vp);
 }
 
+void
+JSCrossCompartmentWrapper::trace(JSTracer *trc, JSObject *wrapper)
+{
+    MarkCrossCompartmentObject(trc, *wrappedObject(wrapper), "wrappedObject");
+}
+
 JSCrossCompartmentWrapper JSCrossCompartmentWrapper::singleton(0u);
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -148,16 +148,18 @@ class JS_FRIEND_API(JSCrossCompartmentWr
     virtual bool call(JSContext *cx, JSObject *wrapper, uintN argc, js::Value *vp);
     virtual bool construct(JSContext *cx, JSObject *wrapper,
                            uintN argc, js::Value *argv, js::Value *rval);
     virtual bool hasInstance(JSContext *cx, JSObject *wrapper, const js::Value *vp, bool *bp);
     virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper);
     virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent);
     virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, js::Value *vp);
 
+    virtual void trace(JSTracer *trc, JSObject *wrapper);
+
     static JSCrossCompartmentWrapper singleton;
 };
 
 namespace js {
 
 // A hacky class that lets a friend force a fake frame. We must already be
 // in the compartment of |target| when we enter the forced frame.
 class JS_FRIEND_API(ForceFrame)
--- a/js/src/xpconnect/src/dom_quickstubs.qsconf
+++ b/js/src/xpconnect/src/dom_quickstubs.qsconf
@@ -871,17 +871,17 @@ customMethodCalls = {
         'code': '    nsRefPtr<nsContentList> result ='
                 'self->GetElementsByTagNameNS(arg0, arg1);',
         'canFail': False
         },
     'nsIDOMElement_': {
         'thisType': 'nsGenericElement'
         },
     'nsIDOMElement_GetTagName': {
-        'thisType': 'nsINode',
+        'thisType': 'nsGenericElement',
         'code': 'nsString result = self->NodeName();',
         'canFail': False
         },
     'nsIDOMDocument_CreateElement': {
         'thisType': 'nsDocument',
         'code': '    nsCOMPtr<nsIContent> result;\n'
                 '    rv = self->CreateElement(arg0, getter_AddRefs(result));'
     },
--- a/js/src/xpconnect/src/xpcjsruntime.cpp
+++ b/js/src/xpconnect/src/xpcjsruntime.cpp
@@ -44,16 +44,20 @@
 #include "WrapperFactory.h"
 #include "dom_quickstubs.h"
 
 #include "jsgcchunk.h"
 #include "nsIMemoryReporter.h"
 #include "mozilla/FunctionTimer.h"
 #include "prsystem.h"
 
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
 using namespace mozilla;
 
 /***************************************************************************/
 
 const char* XPCJSRuntime::mStrings[] = {
     "constructor",          // IDX_CONSTRUCTOR
     "toString",             // IDX_TO_STRING
     "toSource",             // IDX_TO_SOURCE
@@ -1662,16 +1666,24 @@ public:
         return NS_OK;
     }
 };
 NS_IMPL_THREADSAFE_ISUPPORTS1(
   XPConnectJSCompartmentsMultiReporter
 , nsIMemoryMultiReporter
 )
 
+#ifdef MOZ_CRASHREPORTER
+static JSBool
+DiagnosticMemoryCallback(void *ptr, size_t size)
+{
+    return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK;
+}
+#endif
+
 XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect)
  : mXPConnect(aXPConnect),
    mJSRuntime(nsnull),
    mWrappedJSMap(JSObject2WrappedJSMap::newMap(XPC_JS_MAP_SIZE)),
    mWrappedJSClassMap(IID2WrappedJSClassMap::newMap(XPC_JS_CLASS_MAP_SIZE)),
    mIID2NativeInterfaceMap(IID2NativeInterfaceMap::newMap(XPC_NATIVE_INTERFACE_MAP_SIZE)),
    mClassInfo2NativeSetMap(ClassInfo2NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
@@ -1719,16 +1731,19 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
         JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff);
         JS_SetContextCallback(mJSRuntime, ContextCallback);
         JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback);
         JS_SetGCCallbackRT(mJSRuntime, GCCallback);
         JS_SetExtraGCRoots(mJSRuntime, TraceJS, this);
         JS_SetWrapObjectCallbacks(mJSRuntime,
                                   xpc::WrapperFactory::Rewrap,
                                   xpc::WrapperFactory::PrepareForWrapping);
+#ifdef MOZ_CRASHREPORTER
+        JS_EnumerateDiagnosticMemoryRegions(DiagnosticMemoryCallback);
+#endif
         mWatchdogWakeup = JS_NEW_CONDVAR(mJSRuntime->gcLock);
         if (!mWatchdogWakeup)
             NS_RUNTIMEABORT("JS_NEW_CONDVAR failed.");
 
         mJSRuntime->setActivityCallback(ActivityCallback, this);
 
         mJSRuntime->setCustomGCChunkAllocator(&gXPCJSChunkAllocator);
 
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -6624,17 +6624,17 @@ nsFrame::GetParentStyleContextFrame(nsPr
 
 
 /**
  * This function takes a "special" frame and _if_ that frame is an anonymous
  * block created by an ib split it returns the block's preceding inline.  This
  * is needed because the split inline's style context is the parent of the
  * anonymous block's style context.
  *
- * If aFrame is not ananonymous block, null is returned.
+ * If aFrame is not an anonymous block, null is returned.
  */
 static nsIFrame*
 GetIBSpecialSiblingForAnonymousBlock(nsIFrame* aFrame)
 {
   NS_PRECONDITION(aFrame, "Must have a non-null frame!");
   NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IS_SPECIAL,
                "GetIBSpecialSibling should not be called on a non-special frame");
 
--- a/memory/mozalloc/mozalloc_abort.cpp
+++ b/memory/mozalloc/mozalloc_abort.cpp
@@ -42,16 +42,17 @@
 #include <stdlib.h>             // for abort()
 
 #if defined(_MSC_VER)           // MSVC
 #  include <intrin.h>           // for __debugbreak()
 #elif defined(XP_WIN)           // mingw
 #  include <windows.h>          // for DebugBreak
 #elif defined(XP_UNIX)
 #  include <unistd.h>           // for _exit
+#  include <signal.h>
 #endif
 
 #if defined(XP_WIN) || defined(XP_OS2)
 #  define MOZALLOC_EXPORT __declspec(dllexport)
 #endif
 
 #include "mozilla/mozalloc_abort.h"
 
@@ -67,24 +68,44 @@ TouchBadMemory()
 }
 
 void
 mozalloc_abort(const char* const msg)
 {
     fputs(msg, stderr);
     fputs("\n", stderr);
 
-#if defined(XP_UNIX) && !defined(XP_MACOSX)
-    abort();
-#elif defined(_MSC_VER)
+#if defined(_MSC_VER)
     __debugbreak();
 #elif defined(XP_WIN)
     DebugBreak();
 #endif
-    // abort() doesn't trigger breakpad on Mac, "fall through" to the
-    // fail-safe code
+
+    // On *NIX platforms the prefered way to abort is by touching bad memory,
+    // since this generates a stack trace inside our own code (avoiding
+    // problems with starting the trace inside libc, where we might not have
+    // symbols and can get lost).
+
+    TouchBadMemory();
 
-    // Still haven't aborted?  Try dereferencing null.
-    TouchBadMemory();
+    // If we haven't aborted yet, we can try to raise SIGABRT which might work
+    // on some *NIXs, but not OS X (it doesn't trigger breakpad there).
+    // Note that we don't call abort(), since raise is likelier to give us
+    // useful stack data, and also since abort() is redirected to call this
+    // function (see below).
+#if defined(XP_UNIX) && !defined(XP_MACOSX)
+    raise(SIGABRT);
+#endif
 
     // Still haven't aborted?  Try _exit().
     _exit(127);
 }
+
+#if defined(XP_UNIX)
+// Define abort() here, so that it is used instead of the system abort(). This
+// lets us control the behavior when aborting, in order to get better results
+// on *NIX platfrorms. See mozalloc_abort for details.
+void abort(void)
+{
+  mozalloc_abort("Redirecting call to abort() to mozalloc_abort\n");
+}
+#endif
+
--- a/mobile/app/mobile.js
+++ b/mobile/app/mobile.js
@@ -379,16 +379,20 @@ pref("geo.enabled", true);
 // see https://bugzilla.mozilla.org/show_bug.cgi?id=481566#c9
 pref("content.sink.enable_perf_mode",  2); // 0 - switch, 1 - interactive, 2 - perf
 pref("content.sink.pending_event_mode", 0);
 pref("content.sink.perf_deflect_count", 1000000);
 pref("content.sink.perf_parse_time", 50000000);
 
 pref("javascript.options.mem.high_water_mark", 32);
 
+// Disable the JS engine's gc on memory pressure, since we do one in the mobile
+// browser (bug 669346).
+pref("javascript.options.gc_on_memory_pressure", false);
+
 pref("dom.max_chrome_script_run_time", 0); // disable slow script dialog for chrome
 pref("dom.max_script_run_time", 20);
 
 // JS error console
 pref("devtools.errorconsole.enabled", false);
 
 // kinetic tweakables
 pref("browser.ui.kinetic.updateInterval", 16);
--- a/mobile/chrome/content/browser-scripts.js
+++ b/mobile/chrome/content/browser-scripts.js
@@ -64,16 +64,17 @@ Cu.import("resource://gre/modules/Geomet
 XPCOMUtils.defineLazyGetter(this, "CommonUI", function() {
   let CommonUI = {};
   Services.scriptloader.loadSubScript("chrome://browser/content/common-ui.js", CommonUI);
   return CommonUI;
 });
 
 [
   ["FullScreenVideo"],
+  ["WebappsUI"],
   ["BadgeHandlers"],
   ["ContextHelper"],
   ["SelectionHelper"],
   ["FormHelperUI"],
   ["FindHelperUI"],
   ["NewTabPopup"],
   ["PageActions"],
   ["BrowserSearch"],
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -1033,16 +1033,31 @@ var BrowserUI = {
         break;
       case "DOMWillOpenModalDialog":
         return this._domWillOpenModalDialog(browser);
         break;
       case "DOMWindowClose":
         return this._domWindowClose(browser);
         break;
       case "DOMLinkAdded":
+        // checks for an icon to use for a web app
+        // priority is : icon < apple-touch-icon
+        let rel = json.rel.toLowerCase().split(" ");
+        if ((rel.indexOf("icon") != -1) && !browser.appIcon) {
+          // We should also use the sizes attribute if available
+          // see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon
+          browser.appIcon = json.href;
+        }
+        else if (rel.indexOf("apple-touch-icon") != -1) {
+          // XXX should we support apple-touch-icon-precomposed ?
+          // see http://developer.apple.com/safari/library/documentation/appleapplications/reference/safariwebcontent/configuringwebapplications/configuringwebapplications.html
+          browser.appIcon = json.href;
+        }
+
+        // Handle favicon changes
         if (Browser.selectedBrowser == browser)
           this._updateIcon(Browser.selectedBrowser.mIconURL);
         break;
       case "Browser:SaveAs:Return":
         if (json.type != Ci.nsIPrintSettings.kOutputFormatPDF)
           return;
 
         let dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
@@ -1233,17 +1248,18 @@ var BrowserUI = {
           }
 
           return;
         }
 
         this.activePanel = RemoteTabsList;
         break;
       case "cmd_quit":
-        GlobalOverlay.goQuitApplication();
+        // Only close one window
+        this._closeOrQuit();
         break;
       case "cmd_close":
         this._closeOrQuit();
         break;
       case "cmd_menu":
         AppMenu.toggle();
         break;
       case "cmd_newTab":
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -373,23 +373,24 @@ var Browser = {
     messageManager.addMessageListener("Browser:FormSubmit", this);
     messageManager.addMessageListener("Browser:KeyPress", this);
     messageManager.addMessageListener("Browser:ZoomToPoint:Return", this);
     messageManager.addMessageListener("Browser:CanUnload:Return", this);
     messageManager.addMessageListener("scroll", this);
     messageManager.addMessageListener("Browser:CertException", this);
     messageManager.addMessageListener("Browser:BlockedSite", this);
 
-    // broadcast a UIReady message so add-ons know we are finished with startup
+    // Broadcast a UIReady message so add-ons know we are finished with startup
     let event = document.createEvent("Events");
     event.initEvent("UIReady", true, false);
     window.dispatchEvent(event);
 
-    // if we have an opener this was not the first window opened and will not
+    // If we have an opener this was not the first window opened and will not
     // receive an initial resize event. instead we fire the resize handler manually
+    // Bug 610834
     if (window.opener)
       resizeHandler({ target: window });
   },
 
   _alertShown: function _alertShown() {
     // ensure that the full notification still visible, even if the urlbar is floating
     if (BrowserUI.isToolbarLocked())
       Browser.pageScrollboxScroller.scrollTo(0, 0);
@@ -936,17 +937,17 @@ var Browser = {
    * @param [optional] dx
    * @param [optional] dy an offset distance at which to perform the visibility
    * computation
    */
   computeSidebarVisibility: function computeSidebarVisibility(dx, dy) {
     function visibility(aSidebarRect, aVisibleRect) {
       let width = aSidebarRect.width;
       aSidebarRect.restrictTo(aVisibleRect);
-      return (aSidebarRect.width ? aSidebarRect.width / width : 0);
+      return (width ? aSidebarRect.width / width : 0);
     }
 
     if (!dx) dx = 0;
     if (!dy) dy = 0;
 
     let [leftSidebar, rightSidebar] = [Elements.tabs.getBoundingClientRect(), Elements.controls.getBoundingClientRect()];
 
     let visibleRect = new Rect(0, 0, window.innerWidth, 1);
@@ -1487,16 +1488,17 @@ Browser.WebProgress.prototype = {
 
         let locationHasChanged = (location != tab.browser.lastLocation);
         if (locationHasChanged) {
           Browser.getNotificationBox(tab.browser).removeTransientNotifications();
           tab.resetZoomLevel();
           tab.hostChanged = true;
           tab.browser.lastLocation = location;
           tab.browser.userTypedValue = "";
+          tab.browser.appIcon = null;
 
 #ifdef MOZ_CRASH_REPORTER
           if (CrashReporter.enabled)
             CrashReporter.annotateCrashReport("URL", spec);
 #endif
           this._waitForLoad(tab);
           tab.useFallbackWidth = false;
         }
@@ -1571,16 +1573,18 @@ Browser.WebProgress.prototype = {
 
       browser.messageManager.addMessageListener("MozScrolledAreaChanged", aTab.scrolledAreaChanged);
       aTab.updateContentCapture();
     });
   }
 };
 
 
+const OPEN_APPTAB = 100; // Hack until we get a real API
+
 function nsBrowserAccess() { }
 
 nsBrowserAccess.prototype = {
   QueryInterface: function(aIID) {
     if (aIID.equals(Ci.nsIBrowserDOMWindow) || aIID.equals(Ci.nsISupports))
       return this;
     throw Cr.NS_NOINTERFACE;
   },
@@ -1613,23 +1617,41 @@ nsBrowserAccess.prototype = {
       return null;
     } else if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_NEWTAB) {
       let owner = isExternal ? null : Browser.selectedTab;
       let tab = Browser.addTab("about:blank", true, owner, { getAttention: true });
       if (isExternal)
         tab.closeOnExit = true;
       browser = tab.browser;
       BrowserUI.hidePanel();
+    } else if (aWhere == OPEN_APPTAB) {
+      Browser.tabs.forEach(function(aTab) {
+        if ("appURI" in aTab.browser && aTab.browser.appURI.spec == aURI.spec) {
+          Browser.selectedTab = aTab;
+          browser = aTab.browser;
+        }
+      });
+
+      if (!browser) {
+        // Make a new tab to hold the app
+        let tab = Browser.addTab("about:blank", true, null, { getAttention: true });
+        browser = tab.browser;
+        browser.appURI = aURI;
+      } else {
+        // Just use the existing browser, but return null to keep the system from trying to load the URI again
+        browser = null;
+      }
+      BrowserUI.hidePanel();
     } else { // OPEN_CURRENTWINDOW and illegal values
       browser = Browser.selectedBrowser;
     }
 
     try {
       let referrer;
-      if (aURI) {
+      if (aURI && browser) {
         if (aOpener) {
           location = aOpener.location;
           referrer = Services.io.newURI(location, null, null);
         }
         browser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
       }
       browser.focus();
     } catch(e) { }
@@ -2513,31 +2535,39 @@ var ContentCrashObserver = {
         self.CrashSubmit.submit(dumpID, Elements.stack, null, null);
       }
     }, 0, this);
   }
 };
 
 var MemoryObserver = {
   observe: function mo_observe(aSubject, aTopic, aData) {
+    function gc() {
+      window.QueryInterface(Ci.nsIInterfaceRequestor)
+            .getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
+      Cu.forceGC();
+    };
+
     if (aData == "heap-minimize") {
-      // do non-destructive stuff here.
+      // The JS engine would normally GC on this notification, but since we
+      // disabled that in favor of this method (bug 669346), we should gc here.
+      gc();
       return;
     }
 
     for (let i = Browser.tabs.length - 1; i >= 0; i--) {
       let tab = Browser.tabs[i];
       if (tab == Browser.selectedTab)
         continue;
       tab.resurrect();
     }
 
-    window.QueryInterface(Ci.nsIInterfaceRequestor)
-          .getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
-    Cu.forceGC();
+    // gc *after* throwing out the tabs so we can reclaim that memory.
+    gc();
+
     // Bug 637582 - The low memory condition throws out some stuff that we still
     // need, re-selecting the active tab gets us back to where we need to be.
     let sTab = Browser.selectedTab;
     Browser._selectedTab = null;
     Browser.selectedTab = sTab;
   }
 };
 
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -63,20 +63,19 @@
         onload="Browser.startup();"
         onunload="Browser.shutdown();"
         onclose="return Browser.closing();"
         windowtype="navigator:browser"
         chromedir="&locale.dir;"
         title="&brandShortName;"
 #ifdef MOZ_PLATFORM_MAEMO
         sizemode="fullscreen"
-#else
+#endif
         width="480"
         height="800"
-#endif
         onkeypress="onDebugKeyPress(event);"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
         xmlns:html="http://www.w3.org/1999/xhtml">
 
   <script type="application/javascript" src="chrome://browser/content/browser.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser-ui.js"/>
   <script type="application/javascript" src="chrome://browser/content/browser-scripts.js"/>
   <script type="application/javascript" src="chrome://browser/content/Util.js"/>
@@ -344,16 +343,17 @@
           onclick="SharingUI.show(getBrowser().currentURI.spec, getBrowser().contentTitle);"/>
 #endif
         <pageaction id="pageaction-password" title="&pageactions.password.forget;"
           onclick="PageActions.forgetPassword(event);"/>
         <pageaction id="pageaction-reset" title="&pageactions.reset;"
           onclick="PageActions.clearPagePermissions(event);"/>
         <pageaction id="pageaction-search" title="&pageactions.search.addNew;"/>
         <pageaction id="pageaction-charset" title="&pageactions.charEncoding;" onclick="CharsetMenu.show();"/>
+        <pageaction id="pageaction-webapps-install" title="&pageactions.webapps.install;" onclick="WebappsUI.show();"/>
       </hbox>
     </arrowbox>
 
     <arrowbox id="bookmark-popup" class="arrowbox-dark" hidden="true" align="center" offset="12">
       <label value="&bookmarkPopup.label;"/>
       <separator class="thin"/>
       <vbox>
         <button id="bookmark-popup-edit" label="&bookmarkEdit.label;" oncommand="BookmarkHelper.edit();"/>
--- a/mobile/chrome/content/common-ui.js
+++ b/mobile/chrome/content/common-ui.js
@@ -169,16 +169,17 @@ var PageActions = {
 
     this.register("pageaction-reset", this.updatePagePermissions, this);
     this.register("pageaction-password", this.updateForgetPassword, this);
 #ifdef NS_PRINTING
     this.register("pageaction-saveas", this.updatePageSaveAs, this);
 #endif
     this.register("pageaction-share", this.updateShare, this);
     this.register("pageaction-search", BrowserSearch.updatePageSearchEngines, BrowserSearch);
+    this.register("pageaction-webapps-install", WebappsUI.updateWebappsInstall, WebappsUI);
   },
 
   handleEvent: function handleEvent(aEvent) {
     switch (aEvent.type) {
       case "click":
         getIdentityHandler().hide();
         break;
     }
@@ -1653,8 +1654,133 @@ var CharsetMenu = {
     browser.messageManager.sendAsyncMessage("Browser:SetCharset", {
       charset: aCharset
     });
     let history = Cc["@mozilla.org/browser/nav-history-service;1"].getService(Ci.nsINavHistoryService);
     history.setCharsetForURI(browser.documentURI, aCharset);
   }
 
 };
+
+var WebappsUI = {
+  _dialog: null,
+  _manifest: null,
+
+  checkBox: function(aEvent) {
+    let elem = aEvent.originalTarget;
+    let perm = elem.getAttribute("perm");
+    if (this._manifest.capabilities && this._manifest.capabilities.indexOf(perm) != -1) {
+      if (elem.checked) {
+        elem.classList.remove("webapps-noperm");
+        elem.classList.add("webapps-perm");
+      } else {
+        elem.classList.remove("webapps-perm");
+        elem.classList.add("webapps-noperm");
+      }
+    }
+  },
+
+  show: function show(aManifest) {
+    if (!aManifest) {
+      // Try every way to get an icon
+      let browser = Browser.selectedBrowser;
+      let icon = browser.appIcon;
+      if (!icon)
+        icon = browser.mIconURL;
+      if (!icon) 
+        icon = gFaviconService.getFaviconImageForPage(browser.currentURI).spec;
+
+      // Create a simple manifest
+      aManifest = {
+        uri: browser.currentURI.spec,
+        name: browser.contentTitle,
+        icon: icon
+      };
+    }
+
+    this._manifest = aManifest;
+    this._dialog = importDialog(window, "chrome://browser/content/webapps.xul", null);
+
+    if (aManifest.name)
+      document.getElementById("webapps-title").value = aManifest.name;
+    if (aManifest.icon)
+      document.getElementById("webapps-icon").src = aManifest.icon;  
+
+    let uri = Services.io.newURI(aManifest.uri, null, null);
+
+    let perms = [["offline", "offline-app"], ["geoloc", "geo"], ["notifications", "desktop-notifications"]];
+    perms.forEach(function(tuple) {
+      let elem = document.getElementById("webapps-" + tuple[0] + "-checkbox");
+      let currentPerm = Services.perms.testExactPermission(uri, tuple[1]);
+      if ((aManifest.capabilities && (aManifest.capabilities.indexOf(tuple[1]) != -1)) || (currentPerm == Ci.nsIPermissionManager.ALLOW_ACTION))
+        elem.checked = true;
+      else
+        elem.checked = (currentPerm == Ci.nsIPermissionManager.ALLOW_ACTION);
+      elem.classList.remove("webapps-noperm");
+      elem.classList.add("webapps-perm");
+    });
+
+    BrowserUI.pushPopup(this, this._dialog);
+
+    // Force a modal dialog
+    this._dialog.waitForClose();
+  },
+
+  hide: function hide() {
+    this._dialog.close();
+    this._dialog = null;
+    BrowserUI.popPopup(this);
+  },
+
+  _updatePermission: function updatePermission(aId, aPerm) {
+    try {
+      let uri = Services.io.newURI(this._manifest.uri, null, null);
+      Services.perms.add(uri, aPerm, document.getElementById(aId).checked ? Ci.nsIPermissionManager.ALLOW_ACTION : Ci.nsIPermissionManager.DENY_ACTION);
+    } catch(e) {
+      Cu.reportError(e);
+    }
+  },
+  
+  launch: function launch() {
+    let title = document.getElementById("webapps-title").value;
+    if (!title)
+      return;
+
+    this._updatePermission("webapps-offline-checkbox", "offline-app");
+    this._updatePermission("webapps-geoloc-checkbox", "geo");
+    this._updatePermission("webapps-notifications-checkbox", "desktop-notification");
+
+    this.hide();
+    this.install(this._manifest.uri, title, this._manifest.icon);
+  },
+  
+  updateWebappsInstall: function updateWebappsInstall(aNode) {
+    return !document.getElementById("main-window").hasAttribute("webapp");
+  },
+  
+  install: function(aURI, aTitle, aIcon) {
+    const kIconSize = 64;
+    
+    let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+    canvas.setAttribute("style", "display: none");
+
+    let self = this;
+    let image = new Image();
+    image.onload = function() {
+      canvas.width = canvas.height = kIconSize; // clears the canvas
+      let ctx = canvas.getContext("2d");
+      ctx.drawImage(image, 0, 0, kIconSize, kIconSize);
+      let data = canvas.toDataURL("image/png", "");
+      canvas = null;
+      try {
+        let webapp = Cc["@mozilla.org/webapps/support;1"].getService(Ci.nsIWebappsSupport);
+        webapp.installApplication(aTitle, aURI, aIcon, data);
+      } catch(e) {
+        Cu.reportError(e);
+      }
+    }
+    image.onerror = function() {
+      // can't load the icon (bad URI) : fallback to the default one from chrome
+      self.install(aURI, aTitle, "chrome://browser/skin/images/favicon-default-30.png");
+    }
+    image.src = aIcon;
+  }
+};
new file mode 100644
--- /dev/null
+++ b/mobile/chrome/content/webapps.xul
@@ -0,0 +1,94 @@
+<?xml version="1.0"?>
+<!-- ***** BEGIN LICENSE BLOCK *****
+   - Version: MPL 1.1/GPL 2.0/LGPL 2.1
+   -
+   - The contents of this file are subject to the Mozilla Public License Version
+   - 1.1 (the "License"); you may not use this file except in compliance with
+   - the License. You may obtain a copy of the License at
+   - http://www.mozilla.org/MPL/
+   -
+   - Software distributed under the License is distributed on an "AS IS" basis,
+   - WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+   - for the specific language governing rights and limitations under the
+   - License.
+   -
+   - The Original Code is Mozilla Mobile Browser.
+   -
+   - The Initial Developer of the Original Code is
+   - Mozilla Corporation.
+   - Portions created by the Initial Developer are Copyright (C) 2008
+   - the Initial Developer. All Rights Reserved.
+   -
+   - Contributor(s):
+   -   Fabrice Desré <fabrice@mozilla.com>
+   -
+   - Alternatively, the contents of this file may be used under the terms of
+   - either the GNU General Public License Version 2 or later (the "GPL"), or
+   - the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+   - in which case the provisions of the GPL or the LGPL are applicable instead
+   - of those above. If you wish to allow use of your version of this file only
+   - under the terms of either the GPL or the LGPL, and not to allow others to
+   - use your version of this file under the terms of the MPL, indicate your
+   - decision by deleting the provisions above and replace them with the notice
+   - and other provisions required by the LGPL or the GPL. If you do not delete
+   - the provisions above, a recipient may use your version of this file under
+   - the terms of any one of the MPL, the GPL or the LGPL.
+   -
+   - ***** END LICENSE BLOCK ***** -->
+<!DOCTYPE dialog [
+<!ENTITY % promptDTD SYSTEM "chrome://browser/locale/prompt.dtd">
+%promptDTD;
+<!ENTITY % webappsDTD SYSTEM "chrome://browser/locale/webapps.dtd">
+%webappsDTD;
+]>
+<dialog id="webapp-dialog" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <keyset>
+    <key keycode="VK_ESCAPE" command="cmd_cancel"/>
+    <key keycode="VK_RETURN" command="cmd_ok"/>
+  </keyset>
+
+  <commandset>
+    <command id="cmd_cancel" oncommand="WebappsUI.hide();"/>
+    <command id="cmd_ok" oncommand="WebappsUI.launch();"/>
+  </commandset>
+
+  <vbox class="prompt-title" id="webapps-title-box">
+    <hbox align="center">
+      <image id="webapps-icon"/>
+      <vbox flex="1">
+        <textbox id="webapps-title" placeholder="&webapps.title.placeholder;" flex="1"/>
+      </vbox>
+    </hbox>
+  </vbox>
+  <separator class="prompt-line"/>
+  <scrollbox class="prompt-message prompt-header" id="webapps-perm-box" orient="vertical" oncommand="WebappsUI.checkBox(event)" flex="1">
+    <label crop="center" flex="1" value="&webapps.permissions;"/>
+    <button id="webapps-geoloc-checkbox" perm="geo" type="checkbox" class="button-checkbox webapps-perm" flex="1">
+      <image class="button-image-icon"/>
+      <vbox flex="1">
+        <description class="prompt-checkbox-label" flex="1">&webapps.perm.geolocation;</description>
+        <description class="prompt-checkbox-label webapps-perm-requested-hint" id="webapps-geoloc-app">&webapps.perm.requested;</description>
+      </vbox>
+    </button>
+    <button id="webapps-offline-checkbox" perm="offline-app" type="checkbox" class="button-checkbox webapps-perm" flex="1">
+      <image class="button-image-icon"/>
+      <vbox flex="1">
+        <description class="prompt-checkbox-label" flex="1">&webapps.perm.offline;</description>
+        <description class="prompt-checkbox-label webapps-perm-requested-hint" id="webapps-offline-app">&webapps.perm.requested;</description>
+      </vbox>
+    </button>
+    <button id="webapps-notifications-checkbox" perm="desktop-notifications" type="checkbox" class="button-checkbox webapps-perm" flex="1">
+      <image class="button-image-icon"/>
+      <vbox flex="1">
+        <description class="prompt-checkbox-label" flex="1">&webapps.perm.notifications;</description>
+        <description class="prompt-checkbox-label webapps-perm-requested-hint" id="webapps-notifications-app">&webapps.perm.requested;</description>
+      </vbox>
+    </button>
+  </scrollbox>
+  <hbox pack="center" class="prompt-buttons">
+    <button class="prompt-button" command="cmd_ok" label="&ok.label;"/>
+    <separator/>
+    <button class="prompt-button" command="cmd_cancel" label="&cancel.label;"/>
+  </hbox>
+</dialog>
+
--- a/mobile/chrome/jar.mn
+++ b/mobile/chrome/jar.mn
@@ -59,16 +59,17 @@ chrome.jar:
   content/console.js                   (content/console.js)
   content/prompt/alert.xul             (content/prompt/alert.xul)
   content/prompt/confirm.xul           (content/prompt/confirm.xul)
   content/prompt/prompt.xul            (content/prompt/prompt.xul)
   content/prompt/promptPassword.xul    (content/prompt/promptPassword.xul)
   content/prompt/select.xul            (content/prompt/select.xul)
   content/prompt/prompt.js             (content/prompt/prompt.js)
   content/share.xul                    (content/share.xul)
+  content/webapps.xul                  (content/webapps.xul)
   content/AnimatedZoom.js              (content/AnimatedZoom.js)
 #ifdef MOZ_SERVICES_SYNC
   content/sync.js                      (content/sync.js)
 #endif
   content/LoginManagerChild.js         (content/LoginManagerChild.js)
   content/fullscreen-video.js          (content/fullscreen-video.js)
   content/fullscreen-video.xhtml       (content/fullscreen-video.xhtml)
 
--- a/mobile/components/BrowserCLH.js
+++ b/mobile/components/BrowserCLH.js
@@ -39,43 +39,41 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 function openWindow(aParent, aURL, aTarget, aFeatures, aArgs) {
   let argString = null;
-  if (aArgs) {
+  if (aArgs && !(aArgs instanceof Ci.nsISupportsArray)) {
     argString = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
     argString.data = aArgs;
   }
 
-  return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argString);
+  return Services.ww.openWindow(aParent, aURL, aTarget, aFeatures, argString || aArgs);
 }
 
 function resolveURIInternal(aCmdLine, aArgument) {
   let uri = aCmdLine.resolveURI(aArgument);
 
   if (!(uri instanceof Ci.nsIFileURL))
     return uri;
 
   try {
     if (uri.file.exists())
       return uri;
-  }
-  catch (e) {
+  } catch (e) {
     Cu.reportError(e);
   }
 
   try {
     let urifixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
     uri = urifixup.createFixupURI(aArgument, 0);
-  }
-  catch (e) {
+  } catch (e) {
     Cu.reportError(e);
   }
 
   return uri;
 }
 
 /**
  * Determines whether a home page override is needed.
@@ -150,26 +148,31 @@ BrowserCLH.prototype = {
         let uri = resolveURIInternal(aCmdLine, chromeParam);
         let netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil);
         if (!netutil.URIChainHasFlags(uri, Ci.nsIHttpProtocolHandler.URI_INHERITS_SECURITY_CONTEXT)) {
           openWindow(null, uri.spec, "_blank", features, null);
 
           // Stop the normal commandline processing from continuing
           aCmdLine.preventDefault = true;
         }
-      }
-      catch (e) {
+      } catch (e) {
         Cu.reportError(e);
       }
       return;
     }
 
     // Check and remove the alert flag here, but we'll handle it a bit later - see below
     let alertFlag = aCmdLine.handleFlagWithParam("alert", false);
 
+    // Check and remove the webapp param
+    let appFlag = aCmdLine.handleFlagWithParam("webapp", false);
+    let appURI;
+    if (appFlag)
+      appURI = resolveURIInternal(aCmdLine, appFlag);
+
     // Keep an array of possible URL arguments
     let uris = [];
 
     // Check for the "url" flag
     let uriFlag = aCmdLine.handleFlagWithParam("url", false);
     if (uriFlag) {
       let uri = resolveURIInternal(aCmdLine, uriFlag);
       if (uri)
@@ -182,75 +185,81 @@ BrowserCLH.prototype = {
         continue;
 
       let uri = resolveURIInternal(aCmdLine, arg);
       if (uri)
         uris.push(uri);
     }
 
     // Open the main browser window, if we don't already have one
-    let win;
-    let localePickerWin;
+    let browserWin;
     try {
-      win = Services.wm.getMostRecentWindow("navigator:browser");
-      localePickerWin = Services.wm.getMostRecentWindow("navigator:localepicker");
-      if (localePickerWin) {
-        localePickerWin.focus();
+      let localeWin = Services.wm.getMostRecentWindow("navigator:localepicker");
+      if (localeWin) {
+        localeWin.focus();
         aCmdLine.preventDefault = true;
         return;
-      } else  if (!win) {
+      }
+
+      browserWin = Services.wm.getMostRecentWindow("navigator:browser");
+      if (!browserWin) {
         // Default to the saved homepage
         let defaultURL = getHomePage();
 
         // Override the default if we have a URL passed on command line
         if (uris.length > 0) {
           defaultURL = uris[0].spec;
           uris = uris.slice(1);
         }
 
         // Show the locale selector if we have a new profile
         if (needHomepageOverride() == "new profile" && Services.prefs.getBoolPref("browser.firstrun.show.localepicker")) {
-          win = openWindow(null, "chrome://browser/content/localePicker.xul", "_blank", "chrome,dialog=no,all", defaultURL);
+          browserWin = openWindow(null, "chrome://browser/content/localePicker.xul", "_blank", "chrome,dialog=no,all", defaultURL);
           aCmdLine.preventDefault = true;
           return;
         }
 
-        win = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", defaultURL);
+        browserWin = openWindow(null, "chrome://browser/content/browser.xul", "_blank", "chrome,dialog=no,all", defaultURL);
       }
 
-      win.focus();
+      browserWin.focus();
 
       // Stop the normal commandline processing from continuing. We just opened the main browser window
       aCmdLine.preventDefault = true;
-    } catch (e) { }
+    } catch (e) {
+      Cu.reportError(e);
+    }
 
     // Assumption: All remaining command line arguments have been sent remotely (browser is already running)
     // Action: Open any URLs we find into an existing browser window
 
     // First, get a browserDOMWindow object
-    while (!win.browserDOMWindow)
+    while (!browserWin.browserDOMWindow)
       Services.tm.currentThread.processNextEvent(true);
 
     // Open any URIs into new tabs
     for (let i = 0; i < uris.length; i++)
-      win.browserDOMWindow.openURI(uris[i], null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+      browserWin.browserDOMWindow.openURI(uris[i], null, Ci.nsIBrowserDOMWindow.OPEN_NEWTAB, Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+
+    if (appURI)
+      browserWin.browserDOMWindow.openURI(appURI, null, browserWin.OPEN_APPTAB, Ci.nsIBrowserDOMWindow.OPEN_NEW);
 
     // Handle the notification, if called from it
     if (alertFlag) {
       if (alertFlag == "update-app") {
         // Notification was already displayed and clicked, skip it next time
         Services.prefs.setBoolPref("app.update.skipNotification", true);
 
         var updateService = Cc["@mozilla.org/updates/update-service;1"].getService(Ci.nsIApplicationUpdateService);
         var updateTimerCallback = updateService.QueryInterface(Ci.nsITimerCallback);
         updateTimerCallback.notify(null);
       } else if (alertFlag.length >= 9 && alertFlag.substr(0, 9) == "download:") {
-        showPanelWhenReady(win, "downloads-container");
+        showPanelWhenReady(browserWin, "downloads-container");
       } else if (alertFlag == "addons") {
-        showPanelWhenReady(win, "addons-container");
+        showPanelWhenReady(browserWin, "addons-container");
       }
     }
   },
 
   // QI
   QueryInterface: XPCOMUtils.generateQI([Ci.nsICommandLineHandler]),
 
   // XPCOMUtils factory
--- a/mobile/components/Makefile.in
+++ b/mobile/components/Makefile.in
@@ -70,16 +70,17 @@ EXTRA_COMPONENTS = \
         PromptService.js \
         ContentDispatchChooser.js \
         AutoCompleteCache.js \
         AddonUpdateService.js \
         FormAutoComplete.js \
         LoginManager.js \
         LoginManagerPrompter.js \
         BlocklistPrompt.js \
+        WebappsSupport.js \
         $(NULL)
 
 ifdef MOZ_SAFE_BROWSING
 EXTRA_COMPONENTS += SafeBrowsing.js
 endif
 
 ifdef MOZ_UPDATER
 EXTRA_COMPONENTS += UpdatePrompt.js
--- a/mobile/components/MobileComponents.manifest
+++ b/mobile/components/MobileComponents.manifest
@@ -115,8 +115,12 @@ contract @mozilla.org/safebrowsing/appli
 category app-startup SafeBrowsing service,@mozilla.org/safebrowsing/application;1
 #endif
 
 #ifdef MOZ_UPDATER
 # UpdatePrompt.js
 component {88b3eb21-d072-4e3b-886d-f89d8c49fe59} UpdatePrompt.js
 contract @mozilla.org/updates/update-prompt;1 {88b3eb21-d072-4e3b-886d-f89d8c49fe59}
 #endif
+
+# webapps
+component {cb1107c1-1e15-4f11-99c8-27b9ec221a2a} WebappsSupport.js
+contract @mozilla.org/webapps/support;1 {cb1107c1-1e15-4f11-99c8-27b9ec221a2a}
new file mode 100644
--- /dev/null
+++ b/mobile/components/WebappsSupport.js
@@ -0,0 +1,125 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Mobile Browser.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Fabrice Desré <fabrice@mozilla.com>
+ *   Mark Finkle <mfinkle@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+const Cu = Components.utils; 
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
+const DB_VERSION = 1;
+
+function WebappsSupport() {
+  this.init();
+}
+
+WebappsSupport.prototype = {
+  db: null,
+  
+  init: function() {
+    let file =  Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties).get("ProfD", Ci.nsIFile);
+    file.append("webapps.sqlite");
+    this.db = Services.storage.openDatabase(file);
+    let version = this.db.schemaVersion;
+    
+    if (version == 0) {
+      this.db.executeSimpleSQL("CREATE TABLE webapps (title TEXT, uri TEXT PRIMARY KEY, icon TEXT)");
+      this.db.schemaVersion = DB_VERSION;
+    }
+
+    XPCOMUtils.defineLazyGetter(this, "_installQuery", function() {
+      return this.db.createAsyncStatement("INSERT INTO webapps (title, uri, icon) VALUES(:title, :uri, :icon)");
+    });
+
+    XPCOMUtils.defineLazyGetter(this, "_findQuery", function() {
+      return this.db.createStatement("SELECT uri FROM webapps where uri = :uri");
+    });
+
+    Services.obs.addObserver(this, "quit-application-granted", false);
+  },
+ 
+  // entry point
+  installApplication: function(aTitle, aURI, aIconURI, aIconData) {
+    let stmt = this._installQuery;
+    stmt.params.title = aTitle;
+    stmt.params.uri = aURI;
+    stmt.params.icon = aIconData;
+    stmt.executeAsync();
+  },
+ 
+  isApplicationInstalled: function(aURI) {
+    let stmt = this._findQuery;
+    let found = false;
+    try {
+      stmt.params.uri = aURI;
+      found = stmt.executeStep();
+    } finally {
+      stmt.reset();
+    }
+    return found;
+  },
+
+  // nsIObserver
+  observe: function(aSubject, aTopic, aData) {
+    Services.obs.removeObserver(this, "quit-application-granted");
+
+    // Finalize the statements that we have used
+    let stmts = [
+      "_installQuery",
+      "_findQuery"
+    ];
+    for (let i = 0; i < stmts.length; i++) {
+      // We do not want to create any query we haven't already created, so
+      // see if it is a getter first.
+      if (Object.getOwnPropertyDescriptor(this, stmts[i]).value !== undefined) {
+        this[stmts[i]].finalize();
+      }
+    }
+
+    this.db.asyncClose();
+  },
+
+  // QI
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebappsSupport]),
+
+  // XPCOMUtils factory
+  classID: Components.ID("{cb1107c1-1e15-4f11-99c8-27b9ec221a2a}")
+};
+
+const NSGetFactory = XPCOMUtils.generateNSGetFactory([WebappsSupport]);
+
--- a/mobile/installer/package-manifest.in
+++ b/mobile/installer/package-manifest.in
@@ -270,16 +270,17 @@
 @BINPATH@/components/xpcom_io.xpt
 @BINPATH@/components/xpcom_threads.xpt
 @BINPATH@/components/xpcom_xpti.xpt
 @BINPATH@/components/xpconnect.xpt
 @BINPATH@/components/xulapp.xpt
 @BINPATH@/components/xuldoc.xpt
 @BINPATH@/components/xultmpl.xpt
 @BINPATH@/components/zipwriter.xpt
+@BINPATH@/components/webapps.xpt
 
 ; JavaScript components
 @BINPATH@/components/ConsoleAPI.manifest
 @BINPATH@/components/ConsoleAPI.js
 @BINPATH@/components/FeedProcessor.manifest
 @BINPATH@/components/FeedProcessor.js
 @BINPATH@/components/BrowserFeeds.manifest
 @BINPATH@/components/FeedConverter.js
@@ -325,16 +326,17 @@
 @BINPATH@/components/GPSDGeolocationProvider.js
 @BINPATH@/components/nsSidebar.manifest
 @BINPATH@/components/nsSidebar.js
 @BINPATH@/components/extensions.manifest
 @BINPATH@/components/addonManager.js
 @BINPATH@/components/amContentHandler.js
 @BINPATH@/components/amWebInstallListener.js
 @BINPATH@/components/nsBlocklistService.js
+
 #ifdef MOZ_UPDATER
 @BINPATH@/components/nsUpdateService.manifest
 @BINPATH@/components/nsUpdateService.js
 @BINPATH@/components/nsUpdateServiceStub.js
 #endif
 @BINPATH@/components/nsUpdateTimerManager.manifest
 @BINPATH@/components/nsUpdateTimerManager.js
 @BINPATH@/components/pluginGlue.manifest
@@ -605,11 +607,12 @@ bin/components/@DLL_PREFIX@nkgnomevfs@DL
 @BINPATH@/components/SessionStore.js
 @BINPATH@/components/Sidebar.js
 #ifdef MOZ_SAFE_BROWSING
 @BINPATH@/components/SafeBrowsing.js
 #endif
 #ifdef MOZ_UPDATER
 @BINPATH@/components/UpdatePrompt.js
 #endif
+@BINPATH@/components/WebappsSupport.js
 @BINPATH@/components/XPIDialogService.js
 @BINPATH@/components/browsercomps.xpt
 @BINPATH@/extensions/feedback@mobile.mozilla.org.xpi
--- a/mobile/locales/en-US/chrome/browser.dtd
+++ b/mobile/locales/en-US/chrome/browser.dtd
@@ -106,10 +106,11 @@
 <!ENTITY pageactions.saveas.pdf      "Save As PDF">
 <!ENTITY pageactions.share.page      "Share Page">
 <!ENTITY pageactions.password.forget "Forget Password">
 <!ENTITY pageactions.quit            "Quit">
 <!ENTITY pageactions.reset           "Clear Site Preferences">
 <!ENTITY pageactions.findInPage      "Find In Page">
 <!ENTITY pageactions.search.addNew   "Add Search Engine">
 <!ENTITY pageactions.charEncoding    "Character Encoding">
+<!ENTITY pageactions.webapps.install "Install as App">
 
 <!ENTITY appMenu.siteOptions         "Site Options">
new file mode 100644
--- /dev/null
+++ b/mobile/locales/en-US/chrome/webapps.dtd
@@ -0,0 +1,6 @@
+<!ENTITY webapps.title.placeholder "Enter a title">
+<!ENTITY webapps.permissions "Allow access:">
+<!ENTITY webapps.perm.geolocation "Location-aware browsing">
+<!ENTITY webapps.perm.offline "Offline data storage">
+<!ENTITY webapps.perm.notifications "Desktop notifications">
+<!ENTITY webapps.perm.requested "requested">
--- a/mobile/locales/jar.mn
+++ b/mobile/locales/jar.mn
@@ -11,16 +11,17 @@
   locale/@AB_CD@/browser/localepicker.properties  (%chrome/localepicker.properties)
   locale/@AB_CD@/browser/region.properties        (%chrome/region.properties)
   locale/@AB_CD@/browser/preferences.dtd          (%chrome/preferences.dtd)
   locale/@AB_CD@/browser/checkbox.dtd             (%chrome/checkbox.dtd)
   locale/@AB_CD@/browser/notification.dtd         (%chrome/notification.dtd)
   locale/@AB_CD@/browser/sync.dtd                 (%chrome/sync.dtd)
   locale/@AB_CD@/browser/sync.properties          (%chrome/sync.properties)
   locale/@AB_CD@/browser/prompt.dtd               (%chrome/prompt.dtd)
+  locale/@AB_CD@/browser/webapps.dtd              (%chrome/webapps.dtd)
   locale/@AB_CD@/browser/feedback.dtd             (%chrome/feedback.dtd)
   locale/@AB_CD@/browser/phishing.dtd             (%chrome/phishing.dtd)
   locale/@AB_CD@/browser/bookmarks.json           (bookmarks.json)
   locale/@AB_CD@/browser/searchplugins/list.txt   (%searchplugins/list.txt)
 
 # Fennec-specific overrides of generic strings
 * locale/@AB_CD@/browser/netError.dtd             (%chrome/overrides/netError.dtd)
 % override chrome://global/locale/netError.dtd    chrome://browser/locale/netError.dtd
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -1324,16 +1324,35 @@ setting {
 }
 
 /* full-screen video ------------------------------------------------------- */
 .full-screen {
   position: absolute;
   z-index: 500;
 }
 
+/* openwebapps capabilities ------------------------------------------------------------ */
+.webapps-noperm description.webapps-perm-requested-hint {
+  display: block;
+}
+
+.webapps-perm description.webapps-perm-requested-hint {
+  display: none;
+}
+
+#webapps-icon {
+  width: 48px;
+  height: 48px;
+  margin: @margin_normal@;
+}
+
+#webapps-title {
+  -moz-margin-end: @margin_normal@;
+}
+
 /* Android menu ------------------------------------------------------------ */
 #appmenu {
   background: rgba(255,255,255,0.95);
   box-shadow: 0 @shadow_width_large@ @shadow_width_xlarge@ @shadow_width_large@ black;
   border-style: solid;
   border-color: #6d6d6d;
   border-width: @border_width_large@ @border_width_large@ 0 @border_width_large@;
 }
--- a/mobile/themes/core/gingerbread/browser.css
+++ b/mobile/themes/core/gingerbread/browser.css
@@ -1306,16 +1306,35 @@ setting {
 }
 
 /* full-screen video ------------------------------------------------------- */
 .full-screen {
   position: absolute;
   z-index: 500;
 }
 
+/* openwebapps capabilities ------------------------------------------------------------ */
+.webapps-noperm description.webapps-perm-requested-hint {
+  display: block;
+}
+
+.webapps-perm description.webapps-perm-requested-hint {
+  display: none;
+}
+
+#webapps-icon {
+  width: 32px;
+  height: 32px;
+  margin: @margin_normal@;
+}
+
+#webapps-title {
+  -moz-margin-end: @margin_normal@;
+}
+
 /* Android menu ------------------------------------------------------------ */
 #appmenu {
   background: @color_background_default@;
   border-style: solid;
   border-color: #6d6d6d;
   border-width: @border_width_small@ @border_width_small@ 0 @border_width_small@;
 }
 
--- a/mobile/themes/core/gingerbread/platform.css
+++ b/mobile/themes/core/gingerbread/platform.css
@@ -282,22 +282,23 @@ toolbarbutton[open="true"] {
   -moz-margin-end: @margin_normal@;
   list-style-image: url("chrome://browser/skin/images/check-unselected-hdpi.png");
 }
 
 .button-checkbox[checked="true"] > .button-image-icon {
   list-style-image: url("chrome://browser/skin/images/check-selected-hdpi.png");
 }
 
+.button-checkbox > .button-box,
 .button-checkbox:hover:active > .button-box,
 .button-checkbox[checked="true"] > .button-box {
   padding-top: @padding_tiny@;
   padding-bottom: @padding_xsmall@;
-  -moz-padding-start: @margin_small@;
-  -moz-padding-end: @margin_small@;
+  -moz-padding-start: @padding_small@;
+  -moz-padding-end: @padding_small@;
 }
 
 /* radio buttons ----------------------------------------------------------- */
 radiogroup {
   -moz-box-orient: horizontal;
 }
 
 .radio-label {
--- a/modules/libpr0n/src/imgLoader.cpp
+++ b/modules/libpr0n/src/imgLoader.cpp
@@ -153,40 +153,40 @@ public:
   };
 
   imgMemoryReporter(ReporterType aType)
     : mType(aType)
   { }
 
   NS_DECL_ISUPPORTS
 
-  NS_IMETHOD GetProcess(char **process)
+  NS_IMETHOD GetProcess(nsACString &process)
   {
-    *process = strdup("");
+    process.Truncate();
     return NS_OK;
   }
 
-  NS_IMETHOD GetPath(char **memoryPath)
+  NS_IMETHOD GetPath(nsACString &path)
   {
     if (mType == ChromeUsedRaw) {
-      *memoryPath = strdup("explicit/images/chrome/used/raw");
+      path.AssignLiteral("explicit/images/chrome/used/raw");
     } else if (mType == ChromeUsedUncompressed) {
-      *memoryPath = strdup("explicit/images/chrome/used/uncompressed");
+      path.AssignLiteral("explicit/images/chrome/used/uncompressed");
     } else if (mType == ChromeUnusedRaw) {
-      *memoryPath = strdup("explicit/images/chrome/unused/raw");
+      path.AssignLiteral("explicit/images/chrome/unused/raw");
     } else if (mType == ChromeUnusedUncompressed) {
-      *memoryPath = strdup("explicit/images/chrome/unused/uncompressed");
+      path.AssignLiteral("explicit/images/chrome/unused/uncompressed");
     } else if (mType == ContentUsedRaw) {
-      *memoryPath = strdup("explicit/images/content/used/raw");
+      path.AssignLiteral("explicit/images/content/used/raw");
     } else if (mType == ContentUsedUncompressed) {
-      *memoryPath = strdup("explicit/images/content/used/uncompressed");
+      path.AssignLiteral("explicit/images/content/used/uncompressed");
     } else if (mType == ContentUnusedRaw) {
-      *memoryPath = strdup("explicit/images/content/unused/raw");
+      path.AssignLiteral("explicit/images/content/unused/raw");
     } else if (mType == ContentUnusedUncompressed) {
-      *memoryPath = strdup("explicit/images/content/unused/uncompressed");
+      path.AssignLiteral("explicit/images/content/unused/uncompressed");
     }
     return NS_OK;
   }
 
   NS_IMETHOD GetKind(PRInt32 *kind)
   {
     *kind = KIND_HEAP;
     return NS_OK;
@@ -244,34 +244,34 @@ public:
     } else {
       imgLoader::sCache.EnumerateRead(EnumEntries, &arg);
     }
 
     *amount = arg.value;
     return NS_OK;
   }
 
-  NS_IMETHOD GetDescription(char **desc)
+  NS_IMETHOD GetDescription(nsACString &desc)
   {
     if (mType == ChromeUsedRaw) {
-      *desc = strdup("Memory used by in-use chrome images (compressed data).");
+      desc.AssignLiteral("Memory used by in-use chrome images (compressed data).");
     } else if (mType == ChromeUsedUncompressed) {
-      *desc = strdup("Memory used by in-use chrome images (uncompressed data).");
+      desc.AssignLiteral("Memory used by in-use chrome images (uncompressed data).");
     } else if (mType == ChromeUnusedRaw) {
-      *desc = strdup("Memory used by not in-use chrome images (compressed data).");
+      desc.AssignLiteral("Memory used by not in-use chrome images (compressed data).");
     } else if (mType == ChromeUnusedUncompressed) {
-      *desc = strdup("Memory used by not in-use chrome images (uncompressed data).");
+      desc.AssignLiteral("Memory used by not in-use chrome images (uncompressed data).");
     } else if (mType == ContentUsedRaw) {
-      *desc = strdup("Memory used by in-use content images (compressed data).");
+      desc.AssignLiteral("Memory used by in-use content images (compressed data).");
     } else if (mType == ContentUsedUncompressed) {
-      *desc = strdup("Memory used by in-use content images (uncompressed data).");
+      desc.AssignLiteral("Memory used by in-use content images (uncompressed data).");
     } else if (mType == ContentUnusedRaw) {
-      *desc = strdup("Memory used by not in-use content images (compressed data).");
+      desc.AssignLiteral("Memory used by not in-use content images (compressed data).");
     } else if (mType == ContentUnusedUncompressed) {
-      *desc = strdup("Memory used by not in-use content images (uncompressed data).");
+      desc.AssignLiteral("Memory used by not in-use content images (uncompressed data).");
     }
     return NS_OK;
   }
 
   ReporterType mType;
 };
 
 NS_IMPL_ISUPPORTS1(imgMemoryReporter, nsIMemoryReporter)
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -623,16 +623,17 @@ pref("javascript.options.methodjit_alway
 // This preference limits the memory usage of javascript.
 // If you want to change these values for your device,
 // please find Bug 417052 comment 17 and Bug 456721
 // Comment 32 and Bug 613551.
 pref("javascript.options.mem.high_water_mark", 128);
 pref("javascript.options.mem.max", -1);
 pref("javascript.options.mem.gc_per_compartment", true);
 pref("javascript.options.mem.log", false);
+pref("javascript.options.gc_on_memory_pressure", true);
 
 // advanced prefs
 pref("advanced.mailftp",                    false);
 pref("image.animation_mode",                "normal");
 
 // Same-origin policy for file URIs, "false" is traditional
 pref("security.fileuri.strict_origin_policy", true);
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -644,18 +644,22 @@ CallTypeSniffers(void *aClosure, const P
 }
 
 nsresult
 nsHttpChannel::CallOnStartRequest()
 {
     mTracingEnabled = PR_FALSE;
 
     if (mResponseHead && mResponseHead->ContentType().IsEmpty()) {
+        NS_ASSERTION(mConnectionInfo, "Should have connection info here");
         if (!mContentTypeHint.IsEmpty())
             mResponseHead->SetContentType(mContentTypeHint);
+        else if (mResponseHead->Version() == NS_HTTP_VERSION_0_9 &&
+                 mConnectionInfo->Port() != mConnectionInfo->DefaultPort())
+            mResponseHead->SetContentType(NS_LITERAL_CSTRING(TEXT_PLAIN));
         else {
             // Uh-oh.  We had better find out what type we are!
 
             // XXX This does not work with content-encodings...  but
             // neither does applying the conversion from the URILoader
 
             nsCOMPtr<nsIStreamConverterService> serv;
             nsresult rv = gHttpHandler->
--- a/netwerk/protocol/websocket/WebSocketChannel.cpp
+++ b/netwerk/protocol/websocket/WebSocketChannel.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -563,16 +563,22 @@ WebSocketChannel::~WebSocketChannel()
     NS_ProxyRelease(mainThread, forgettableListener, PR_FALSE);
   }
 
   if (mContext) {
     nsISupports *forgettableContext;
     mContext.forget(&forgettableContext);
     NS_ProxyRelease(mainThread, forgettableContext, PR_FALSE);
   }
+
+  if (mLoadGroup) {
+    nsILoadGroup *forgettableGroup;
+    mLoadGroup.forget(&forgettableGroup);
+    NS_ProxyRelease(mainThread, forgettableGroup, PR_FALSE);
+  }
 }
 
 void
 WebSocketChannel::Shutdown()
 {
   delete sWebSocketAdmissions;
   sWebSocketAdmissions = nsnull;
 }
--- a/netwerk/protocol/websocket/WebSocketChannel.h
+++ b/netwerk/protocol/websocket/WebSocketChannel.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et tw=80 : */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
@@ -204,17 +204,16 @@ private:
     nsCString *mMsg;
     PRBool     mIsControl;
     PRInt32    mBinaryLen;
   };
 
   nsCOMPtr<nsIEventTarget>                 mSocketThread;
   nsCOMPtr<nsIHttpChannelInternal>         mChannel;
   nsCOMPtr<nsIHttpChannel>                 mHttpChannel;
-  nsCOMPtr<nsILoadGroup>                   mLoadGroup;
   nsCOMPtr<nsICancelable>                  mDNSRequest;
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIRandomGenerator>             mRandomGenerator;
 
   nsCString                       mHashedSecret;
   nsCString                       mAddress;
 
   nsCOMPtr<nsISocketTransport>    mTransport;
--- a/netwerk/protocol/websocket/nsIWebSocketChannel.idl
+++ b/netwerk/protocol/websocket/nsIWebSocketChannel.idl
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set sw=4 ts=4 et tw=80 : */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
--- a/netwerk/protocol/websocket/nsIWebSocketListener.idl
+++ b/netwerk/protocol/websocket/nsIWebSocketListener.idl
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set sw=4 ts=4 et tw=80 : */
 /* ***** BEGIN LICENSE BLOCK *****
  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  *
  * The contents of this file are subject to the Mozilla Public License Version
  * 1.1 (the "License"); you may not use this file except in compliance with
  * the License. You may obtain a copy of the License at
  * http://www.mozilla.org/MPL/
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_bug667907.js
@@ -0,0 +1,81 @@
+do_load_httpd_js();
+
+var httpserver = null;
+var simplePath = "/simple";
+var normalPath = "/normal";
+var httpbody = "<html></html>";
+var uri1 = "http://localhost:4444" + simplePath;
+var uri2 = "http://localhost:4444" + normalPath;
+
+function make_channel(url) {
+  var ios = Cc["@mozilla.org/network/io-service;1"].
+            getService(Ci.nsIIOService);
+  return ios.newChannel(url, "", null);
+}
+
+var listener_proto = {
+  QueryInterface: function(iid) {
+    if (iid.equals(Components.interfaces.nsIStreamListener) ||
+        iid.equals(Components.interfaces.nsIRequestObserver) ||
+        iid.equals(Components.interfaces.nsISupports))
+      return this;
+    throw Components.results.NS_ERROR_NO_INTERFACE;
+  },
+
+  onStartRequest: function(request, context) {
+    do_check_eq(request.QueryInterface(Ci.nsIChannel).contentType,
+		this.contentType);
+    request.cancel(Cr.NS_BINDING_ABORTED);
+  },
+
+  onDataAvailable: function(request, context, stream, offset, count) {
+    do_throw("Unexpected onDataAvailable");
+  },
+
+  onStopRequest: function(request, context, status) {
+    do_check_eq(status, Cr.NS_BINDING_ABORTED);
+    this.termination_func();
+  }  
+};
+
+function listener(contentType, termination_func) {
+  this.contentType = contentType;
+  this.termination_func = termination_func;
+}
+listener.prototype = listener_proto;
+
+function run_test()
+{
+  httpserver = new nsHttpServer();
+  httpserver.registerPathHandler(simplePath, simpleHandler);
+  httpserver.registerPathHandler(normalPath, normalHandler);
+  httpserver.start(4444);
+  
+  var channel = make_channel(uri1);
+  channel.asyncOpen(new listener("text/plain", function() {
+	run_test2();
+      }), null);
+
+  do_test_pending();
+}
+
+function run_test2()
+{
+  var channel = make_channel(uri2);
+  channel.asyncOpen(new listener("text/html", function() {
+	httpserver.stop(do_test_finished);
+      }), null);
+}
+
+function simpleHandler(metadata, response)
+{
+  response.seizePower();
+  response.bodyOutputStream.write(httpbody, httpbody.length);
+  response.finish();
+}
+
+function normalHandler(metadata, response)
+{
+  response.bodyOutputStream.write(httpbody, httpbody.length);
+  response.finish();
+}
--- a/netwerk/test/unit/xpcshell.ini
+++ b/netwerk/test/unit/xpcshell.ini
@@ -63,16 +63,17 @@ tail =
 [test_bug652761.js]
 [test_bug651100.js]
 [test_bug654926.js]
 [test_bug654926_doom_and_read.js]
 [test_bug654926_test_seek.js]
 [test_bug659569.js]
 [test_bug660066.js]
 [test_bug651185.js]
+[test_bug667907.js]
 [test_cacheflags.js]
 [test_channel_close.js]
 [test_compareURIs.js]
 [test_content_sniffer.js]
 [test_cookie_header.js]
 [test_data_protocol.js]
 [test_dns_service.js]
 [test_duplicate_headers.js]
--- a/storage/src/mozStorageConnection.cpp
+++ b/storage/src/mozStorageConnection.cpp
@@ -343,40 +343,36 @@ public:
   StorageMemoryReporter(Connection &aDBConn,
                         ReporterType aType)
   : mDBConn(aDBConn)
   , mType(aType)
   {
   }
 
 
-  NS_IMETHOD GetProcess(char **process)
+  NS_IMETHOD GetProcess(nsACString &process)
   {
-    *process = strdup("");
+    process.Truncate();
     return NS_OK;
   }
 
-  NS_IMETHOD GetPath(char **memoryPath)
+  NS_IMETHOD GetPath(nsACString &path)
   {
-    nsCString path;
-
-    path.AppendLiteral("explicit/storage/sqlite/");
+    path.AssignLiteral("explicit/storage/sqlite/");
     path.Append(mDBConn.getFilename());
 
     if (mType == Cache_Used) {
       path.AppendLiteral("/cache-used");
     }
     else if (mType == Schema_Used) {
       path.AppendLiteral("/schema-used");
     }
     else if (mType == Stmt_Used) {
       path.AppendLiteral("/stmt-used");
     }
-
-    *memoryPath = ::ToNewCString(path);
     return NS_OK;
   }
 
   NS_IMETHOD GetKind(PRInt32 *kind)
   {
     *kind = KIND_HEAP;
     return NS_OK;
   }
@@ -401,27 +397,27 @@ public:
     }
 
     int cur=0, max=0;
     int rc = ::sqlite3_db_status(mDBConn, type, &cur, &max, 0);
     *amount = cur;
     return convertResultCode(rc);
   }
 
-  NS_IMETHOD GetDescription(char **desc)
+  NS_IMETHOD GetDescription(nsACString &desc)
   {
     if (mType == Cache_Used) {
-      *desc = ::strdup("Memory (approximate) used by all pager caches.");
+      desc.AssignLiteral("Memory (approximate) used by all pager caches.");
     }
     else if (mType == Schema_Used) {
-      *desc = ::strdup("Memory (approximate) used to store the schema "
-                       "for all databases associated with the connection");
+      desc.AssignLiteral("Memory (approximate) used to store the schema "
+                          "for all databases associated with the connection");
     }
     else if (mType == Stmt_Used) {
-      *desc = ::strdup("Memory (approximate) used by all prepared statements");
+      desc.AssignLiteral("Memory (approximate) used by all prepared statements");
     }
     return NS_OK;
   }
 
   Connection &mDBConn;
   nsCString mFileName;
   ReporterType mType;
 };
--- a/testing/mochitest/runtestsremote.py
+++ b/testing/mochitest/runtestsremote.py
@@ -131,17 +131,17 @@ class RemoteOptions(MochitestOptions):
 
         options.webServer = options.remoteWebServer
 
         if (options.deviceIP == None):
             print "ERROR: you must provide a device IP"
             return None
 
         if (options.remoteLogFile == None):
-            options.remoteLogFile = options.remoteTestRoot + '/mochitest.log'
+            options.remoteLogFile = options.remoteTestRoot + '/logs/mochitest.log'
 
         if (options.remoteLogFile.count('/') < 1):
             options.remoteLogFile = options.remoteTestRoot + '/' + options.remoteLogFile
 
         # remoteAppPath or app must be specified to find the product to launch
         if (options.remoteAppPath and options.app):
             print "ERROR: You cannot specify both the remoteAppPath and the app setting"
             return None
@@ -331,16 +331,18 @@ def main():
         auto.setProduct(options.remoteProductName)
 
     mochitest = MochiRemote(auto, dm, options)
 
     options = parser.verifyOptions(options, mochitest)
     if (options == None):
         sys.exit(1)
     
+    logParent = os.path.dirname(options.remoteLogFile)
+    dm.mkDir(logParent);
     auto.setRemoteLog(options.remoteLogFile)
     auto.setServerInfo(options.webServer, options.httpPort, options.sslPort)
 
     procName = options.app.split('/')[-1]
     if (dm.processExist(procName)):
       dm.killProcess(procName)
 
     sys.exit(mochitest.runTests(options))
--- a/testing/testsuite-targets.mk
+++ b/testing/testsuite-targets.mk
@@ -56,29 +56,43 @@ MOCHITESTS := mochitest-plain mochitest-
 mochitest:: $(MOCHITESTS)
 
 RUN_MOCHITEST = \
 	rm -f ./$@.log && \
 	$(PYTHON) _tests/testing/mochitest/runtests.py --autorun --close-when-done \
 	  --console-level=INFO --log-file=./$@.log --file-level=INFO \
 	  $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
 
+RUN_MOCHITEST_REMOTE = \
+	rm -f ./$@.log && \
+	$(PYTHON) _tests/testing/mochitest/runtestsremote.py --autorun --close-when-done \
+	  --console-level=INFO --log-file=./$@.log --file-level=INFO $(DM_FLAGS) \
+	  --app=$(ANDROID_PACKAGE_NAME) --deviceIP=${TEST_DEVICE} --xre-path=${MOZ_HOST_BIN} \
+	  $(SYMBOLS_PATH) $(TEST_PATH_ARG) $(EXTRA_TEST_ARGS)
+
 ifndef NO_FAIL_ON_TEST_ERRORS
 define CHECK_TEST_ERROR
   @errors=`grep "TEST-UNEXPECTED-" $@.log` ;\
   if test "$$errors" ; then \
 	  echo "$@ failed:"; \
 	  echo "$$errors"; \
 	  exit 1; \
   else \
 	  echo "$@ passed"; \
   fi
 endef
 endif
 
+mochitest-remote:
+	if test -f ${MOZ_HOST_BIN}/xpcshell && [ "${TEST_DEVICE}" != "" ]; \
+          then $(RUN_MOCHITEST_REMOTE); \
+        else \
+          @echo "please prepare your host with environment variables for TEST_DEVICE and MOZ_HOST_BIN"; \
+        fi
+
 mochitest-plain:
 	$(RUN_MOCHITEST)
 	$(CHECK_TEST_ERROR)
 
 # Allow mochitest-1 ... mochitest-5 for developer ease
 mochitest-1 mochitest-2 mochitest-3 mochitest-4 mochitest-5: mochitest-%:
 	echo "mochitest: $* / 5"
 	$(RUN_MOCHITEST) --chunk-by-dir=4 --total-chunks=5 --this-chunk=$*
--- a/toolkit/components/console/hudservice/tests/browser/Makefile.in
+++ b/toolkit/components/console/hudservice/tests/browser/Makefile.in
@@ -185,18 +185,16 @@ include $(topsrcdir)/config/rules.mk
 	test-bug-595934-css-parser.html \
 	test-bug-595934-css-parser.css \
 	test-bug-595934-canvas-css.html \
 	test-bug-595934-canvas-css.js \
 	test-bug-595934-malformedxml-external.html \
 	test-bug-595934-malformedxml-external.xml \
 	test-bug-595934-empty-getelementbyid.html \
 	test-bug-595934-empty-getelementbyid.js \
-	test-bug-595934-getselection.html \
-	test-bug-595934-getselection.js \
 	test-bug-595934-image.html \
 	test-bug-595934-image.jpg \
 	test-bug-597136-external-script-errors.html \
 	test-bug-597136-external-script-errors.js \
 	test-bug-613013-console-api-iframe.html \
 	test-bug-597756-reopen-closed-tab.html \
 	test-bug-600183-charset.html \
 	test-bug-600183-charset.html^headers^ \
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_595934_message_categories.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_595934_message_categories.js
@@ -84,21 +84,16 @@ const TESTS = [
     category: "DOM",
     matchString: "getElementById",
   },
   { // #14
     file: "test-bug-595934-canvas-css.html",
     category: "CSS Parser",
     matchString: "foobarCanvasCssParser",
   },
-  { // #15
-    file: "test-bug-595934-getselection.html",
-    category: "content javascript",
-    matchString: "getSelection",
-  },
   { // #16
     file: "test-bug-595934-image.html",
     category: "Image",
     matchString: "corrupt",
   },
 ];
 
 let pos = -1;
deleted file mode 100644
--- a/toolkit/components/console/hudservice/tests/browser/test-bug-595934-getselection.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE html>
-<html lang="en">
-  <head>
-    <title>Web Console test for bug 595934 - category: content javascript.
-    (getSelection())</title>
-<!-- Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/ -->
-    <script type="text/javascript"
-            src="test-bug-595934-getselection.js"></script>
-  </head>
-  <body>
-    <p>Web Console test for bug 595934 - category "content javascript"
-       (getSelection()).</p>
-  </body>
-</html>
-
deleted file mode 100644
--- a/toolkit/components/console/hudservice/tests/browser/test-bug-595934-getselection.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/*
- * Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-window.addEventListener("load", function() {
-  document.getSelection();
-}, false);
-
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.cc
@@ -440,17 +440,18 @@ void ExceptionHandler::WaitForContinueSi
 // This function runs in a compromised context: see the top of the file.
 // Runs on the cloned process.
 bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context,
                               size_t context_size) {
   return google_breakpad::WriteMinidump(next_minidump_path_c_,
                                         crashing_process,
                                         context,
                                         context_size,
-                                        mapping_info_);
+                                        mapping_info_,
+                                        app_memory_info_);
 }
 
 // static
 bool ExceptionHandler::WriteMinidump(const std::string &dump_path,
                                      MinidumpCallback callback,
                                      void* callback_context) {
   return WriteMinidump(dump_path, false, callback, callback_context);
 }
@@ -534,9 +535,24 @@ void ExceptionHandler::AddMappingInfo(co
    strncpy(info.name, name.c_str(), std::min(name.size() + 1, sizeof(info)));
  
    std::pair<MappingInfo, u_int8_t[sizeof(MDGUID)]> mapping;
    mapping.first = info;
    memcpy(mapping.second, identifier, sizeof(MDGUID));
    mapping_info_.push_back(mapping);
 }
 
+void ExceptionHandler::RegisterAppMemory(void *ptr, size_t length) {
+  app_memory_info_.push_back(AppMemory(ptr, length));
+}
+
+void ExceptionHandler::UnregisterAppMemory(void *ptr) {
+  for (AppMemoryList::iterator iter = app_memory_info_.begin();
+       iter != app_memory_info_.end();
+       ++iter) {
+    if (iter->ptr == ptr) {
+      app_memory_info_.erase(iter);
+      return;
+    }
+  }
+}
+
 }  // namespace google_breakpad
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/handler/exception_handler.h
@@ -214,16 +214,21 @@ class ExceptionHandler {
   // a custom library loader is used that maps things in a way
   // that the linux dumper can't handle by reading the maps file.
   void AddMappingInfo(const std::string& name,
                       const u_int8_t identifier[sizeof(MDGUID)],
                       uintptr_t start_address,
                       size_t mapping_size,
                       size_t file_offset);
 
+  // Calling RegisterAppMemory(p, len) causes len bytes starting
+  // at address p to be copied to the minidump when a crash happens.
+  void RegisterAppMemory(void *ptr, size_t length);
+  void UnregisterAppMemory(void *ptr);
+
  private:
   void Init(const std::string &dump_path,
             const int server_fd);
   bool InstallHandlers();
   void UninstallHandlers();
   void PreresolveSymbols();
   bool GenerateDump(CrashContext *context);
   void SendContinueSignalToChild();
@@ -273,13 +278,17 @@ class ExceptionHandler {
   // can do this. We create a pipe which we can use to block the
   // cloned process after creating it, until we have explicitly enabled 
   // ptrace. This is used to store the file descriptors for the pipe
   int fdes[2];
 
   // Callers can add extra info about mappings for cases where the
   // dumper code cannot extract enough information from /proc/<pid>/maps.
   MappingList mapping_info_;
+
+  // Callers can request additional memory regions to be included in
+  // the dump.
+  AppMemoryList app_memory_info_;
 };
 
 }  // namespace google_breakpad
 
 #endif  // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.cc
@@ -413,47 +413,51 @@ namespace google_breakpad {
 // ptrace, and we can't assume that crashing_tid_ still exists (or
 // ever did).
 class MinidumpWriter {
  public:
   // case (1) above
   MinidumpWriter(const char* filename,
                  pid_t crashing_pid,
                  const ExceptionHandler::CrashContext* context,
-                 const MappingList& mappings)
+                 const MappingList& mappings,
+                 const AppMemoryList& appmem)
       : filename_(filename),
         siginfo_(&context->siginfo),
         ucontext_(&context->context),
 #if !defined(__ARM_EABI__)
         float_state_(&context->float_state),
 #else
         //TODO: fix this after fixing ExceptionHandler
         float_state_(NULL),
 #endif
         crashing_tid_(context->tid),
         crashing_tid_pc_(0),
         dumper_(crashing_pid),
         memory_blocks_(dumper_.allocator()),
-        mapping_info_(mappings) {
+        mapping_info_(mappings),
+        app_memory_info_(appmem) {
   }
 
   // case (2) above
   MinidumpWriter(const char* filename,
                  pid_t pid,
                  pid_t blame_thread,
-                 const MappingList& mappings)
+                 const MappingList& mappings,
+                 const AppMemoryList& appmem)
       : filename_(filename),
         siginfo_(NULL),         // we fill this in if we find blame_thread
         ucontext_(NULL),
         float_state_(NULL),
         crashing_tid_(blame_thread),
         crashing_tid_pc_(0),    // set if we find blame_thread
         dumper_(pid),
         memory_blocks_(dumper_.allocator()),
-        mapping_info_(mappings) {
+        mapping_info_(mappings),
+        app_memory_info_(appmem) {
   }
 
   bool Init() {
     return dumper_.Init() && minidump_writer_.Open(filename_) &&
            dumper_.ThreadsAttach();
   }
 
   ~MinidumpWriter() {
@@ -510,16 +514,19 @@ class MinidumpWriter {
     if (!WriteThreadListStream(&dirent))
       return false;
     dir.CopyIndex(dir_index++, &dirent);
 
     if (!WriteMappings(&dirent))
       return false;
     dir.CopyIndex(dir_index++, &dirent);
 
+    if (!WriteAppMemory())
+      return false;
+
     if (!WriteMemoryListStream(&dirent))
       return false;
     dir.CopyIndex(dir_index++, &dirent);
 
     if (siginfo_ || crashing_tid_pc_) {
       if (!WriteExceptionStream(&dirent))
         return false;
       dir.CopyIndex(dir_index++, &dirent);
@@ -819,16 +826,38 @@ class MinidumpWriter {
       }
 
       list.CopyIndexAfterObject(i, &thread, sizeof(thread));
     }
 
     return true;
   }
 
+  bool WriteAppMemory() {
+    for (AppMemoryList::const_iterator iter = app_memory_info_.begin();
+         iter != app_memory_info_.end();
+         ++iter) {
+      uint8_t* data_copy =
+	(uint8_t*) dumper_.allocator()->Alloc(iter->length);
+      dumper_.CopyFromProcess(data_copy, crashing_tid_, iter->ptr,
+			      iter->length);
+
+      UntypedMDRVA memory(&minidump_writer_);
+      if (!memory.Allocate(iter->length))
+        return false;
+      memory.Copy(data_copy, iter->length);
+      MDMemoryDescriptor desc;
+      desc.start_of_memory_range = (uintptr_t)iter->ptr;
+      desc.memory = memory.location();
+      memory_blocks_.push_back(desc);
+    }
+
+    return true;
+  }
+
   static bool ShouldIncludeMapping(const MappingInfo& mapping) {
     if (mapping.name[0] == 0 || // we only want modules with filenames.
         mapping.offset || // we only want to include one mapping per shared lib.
         mapping.size < 4096) {  // too small to get a signature for.
       return false;
     }
 
     return true;
@@ -1357,40 +1386,46 @@ popline:
   MinidumpFileWriter minidump_writer_;
   MDLocationDescriptor crashing_thread_context_;
   // Blocks of memory written to the dump. These are all currently
   // written while writing the thread list stream, but saved here
   // so a memory list stream can be written afterwards.
   wasteful_vector<MDMemoryDescriptor> memory_blocks_;
   // Additional information about some mappings provided by the caller.
   const MappingList& mapping_info_;
+  // Callers can request additional memory regions to be included in
+  // the dump.
+  const AppMemoryList& app_memory_info_;
 };
 
 bool WriteMinidump(const char* filename, pid_t crashing_process,
                    const void* blob, size_t blob_size) {
   MappingList m;
-  return WriteMinidump(filename, crashing_process, blob, blob_size, m);
+  AppMemoryList a;
+  return WriteMinidump(filename, crashing_process, blob, blob_size, m, a);
 }
 
 bool WriteMinidump(const char* filename, pid_t crashing_process,
                    const void* blob, size_t blob_size,
-                   const MappingList& mappings) {
+                   const MappingList& mappings,
+                   const AppMemoryList& appmem) {
   if (blob_size != sizeof(ExceptionHandler::CrashContext))
     return false;
   const ExceptionHandler::CrashContext* context =
       reinterpret_cast<const ExceptionHandler::CrashContext*>(blob);
-  MinidumpWriter writer(filename, crashing_process, context, mappings);
+  MinidumpWriter writer(filename, crashing_process, context, mappings, appmem);
   if (!writer.Init())
     return false;
   return writer.Dump();
 }
 
 bool WriteMinidump(const char* filename, pid_t process,
                    pid_t process_blamed_thread) {
   //TODO: support mappings here
   MappingList m;
-  MinidumpWriter writer(filename, process, process_blamed_thread, m);
+  AppMemoryList a;
+  MinidumpWriter writer(filename, process, process_blamed_thread, m, a);
   if (!writer.Init())
     return false;
   return writer.Dump();
 }
 
 }  // namespace google_breakpad
--- a/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/minidump_writer.h
@@ -39,16 +39,26 @@
 #include "google_breakpad/common/minidump_format.h"
 
 namespace google_breakpad {
 
 // A list of <MappingInfo, GUID>
 typedef std::pair<struct MappingInfo, u_int8_t[sizeof(MDGUID)]> MappingEntry;
 typedef std::list<MappingEntry> MappingList;
 
+// These entries store a list of memory regions that the client wants included
+// in the minidump.
+struct AppMemory {
+  AppMemory(void *ptr, size_t length) : ptr(ptr), length(length) {}
+
+  void *ptr;
+  size_t length;
+};
+typedef std::list<AppMemory> AppMemoryList;
+
 // Write a minidump to the filesystem. This function does not malloc nor use
 // libc functions which may. Thus, it can be used in contexts where the state
 // of the heap may be corrupt.
 //   filename: the filename to write to. This is opened O_EXCL and fails if
 //     open fails.
 //   crashing_process: the pid of the crashing process. This must be trusted.
 //   blob: a blob of data from the crashing process. See exception_handler.h
 //   blob_size: the length of |blob|, in bytes
@@ -60,16 +70,18 @@ bool WriteMinidump(const char* filename,
 // Alternate form of WriteMinidump() that works with processes that
 // are not expected to have crashed.  If |process_blamed_thread| is
 // meaningful, it will be the one from which a crash signature is
 // extracted.  It is not expected that this function will be called
 // from a compromised context, but it is safe to do so.
 bool WriteMinidump(const char* filename, pid_t process,
                    pid_t process_blamed_thread);
 
-// This overload also allows passing a list of known mappings.
+// This overload also allows passing a list of known mappings and
+// a list of additional memory regions to be included in the minidump.
 bool WriteMinidump(const char* filename, pid_t crashing_process,
                    const void* blob, size_t blob_size,
-                   const MappingList& mappings);
+                   const MappingList& mappings,
+                   const AppMemoryList& appdata);
 
 }  // namespace google_breakpad
 
 #endif  // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.cc
@@ -128,19 +128,17 @@ DWORD GetProcId(HANDLE process) {
 
 namespace google_breakpad {
 
 static const int kWaitForHandlerThreadMs = 60000;
 static const int kExceptionHandlerThreadInitialStackSize = 64 * 1024;
 
 // This is passed as the context to the MinidumpWriteDump callback.
 typedef struct {
-  ULONG64 memory_base;
-  ULONG memory_size;
-  bool finished;
+  AppMemoryList::const_iterator iter, end;
 } MinidumpCallbackContext;
 
 vector<ExceptionHandler*>* ExceptionHandler::handler_stack_ = NULL;
 LONG ExceptionHandler::handler_stack_index_ = 0;
 CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_;
 volatile LONG ExceptionHandler::instance_count_ = 0;
 
 ExceptionHandler::ExceptionHandler(const wstring& dump_path,
@@ -921,23 +919,23 @@ bool ExceptionHandler::WriteMinidumpWith
 BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback(
     PVOID context,
     const PMINIDUMP_CALLBACK_INPUT callback_input,
     PMINIDUMP_CALLBACK_OUTPUT callback_output) {
   switch (callback_input->CallbackType) {
   case MemoryCallback: {
     MinidumpCallbackContext* callback_context =
         reinterpret_cast<MinidumpCallbackContext*>(context);
-    if (callback_context->finished)
+    if (callback_context->iter == callback_context->end)
       return FALSE;
 
     // Include the specified memory region.
-    callback_output->MemoryBase = callback_context->memory_base;
-    callback_output->MemorySize = callback_context->memory_size;
-    callback_context->finished = true;
+    callback_output->MemoryBase = callback_context->iter->ptr;
+    callback_output->MemorySize = callback_context->iter->length;
+    callback_context->iter++;
     return TRUE;
   }
     
     // Include all modules.
   case IncludeModuleCallback:
   case ModuleCallback:
     return TRUE;
 
@@ -1010,19 +1008,16 @@ bool ExceptionHandler::WriteMinidumpWith
       if (assertion) {
         int idx = user_streams.UserStreamCount;
         user_stream_array[idx].Type = MD_ASSERTION_INFO_STREAM;
         user_stream_array[idx].BufferSize = sizeof(MDRawAssertionInfo);
         user_stream_array[idx].Buffer = assertion;
         ++user_streams.UserStreamCount;
       }
 
-      MINIDUMP_CALLBACK_INFORMATION callback;
-      MinidumpCallbackContext context;
-      MINIDUMP_CALLBACK_INFORMATION* callback_pointer = NULL;
       // Older versions of DbgHelp.dll don't correctly put the memory around
       // the faulting instruction pointer into the minidump. This
       // callback will ensure that it gets included.
       if (exinfo) {
         // Find a memory region of 256 bytes centered on the
         // faulting instruction pointer.
         const ULONG64 instruction_pointer = 
 #if defined(_M_IX86)
@@ -1037,41 +1032,44 @@ bool ExceptionHandler::WriteMinidumpWith
         if (VirtualQuery(reinterpret_cast<LPCVOID>(instruction_pointer),
                          &info,
                          sizeof(MEMORY_BASIC_INFORMATION)) != 0 &&
             info.State == MEM_COMMIT) {
           // Attempt to get 128 bytes before and after the instruction
           // pointer, but settle for whatever's available up to the
           // boundaries of the memory region.
           const ULONG64 kIPMemorySize = 256;
-          context.memory_base = 
+          ULONG64 base = 
             std::max(reinterpret_cast<ULONG64>(info.BaseAddress),
                      instruction_pointer - (kIPMemorySize / 2));
           ULONG64 end_of_range =
             std::min(instruction_pointer + (kIPMemorySize / 2),
                      reinterpret_cast<ULONG64>(info.BaseAddress)
                      + info.RegionSize);
-          context.memory_size =
-            static_cast<ULONG>(end_of_range - context.memory_base);
- 
-          context.finished = false;
-          callback.CallbackRoutine = MinidumpWriteDumpCallback;
-          callback.CallbackParam = reinterpret_cast<void*>(&context);
-          callback_pointer = &callback;
+          ULONG size = static_cast<ULONG>(end_of_range - base);
+          app_memory_info_.push_back(AppMemory(base, size));
         }
       }
 
+      MinidumpCallbackContext context;
+      context.iter = app_memory_info_.begin();
+      context.end = app_memory_info_.end();
+
+      MINIDUMP_CALLBACK_INFORMATION callback;
+      callback.CallbackRoutine = MinidumpWriteDumpCallback;
+      callback.CallbackParam = reinterpret_cast<void*>(&context);
+
       // The explicit comparison to TRUE avoids a warning (C4800).
       success = (minidump_write_dump_(process,
                                       processId,
                                       dump_file,
                                       dump_type_,
                                       exinfo ? &except_info : NULL,
                                       &user_streams,
-                                      callback_pointer) == TRUE);
+                                      &callback) == TRUE);
 
       CloseHandle(dump_file);
     }
   }
 
   return success;
 }
 
@@ -1090,9 +1088,25 @@ void ExceptionHandler::UpdateNextID() {
 
   // remove when VC++7.1 is no longer supported
   minidump_path[MAX_PATH - 1] = L'\0';
 
   next_minidump_path_ = minidump_path;
   next_minidump_path_c_ = next_minidump_path_.c_str();
 }
 
+void ExceptionHandler::RegisterAppMemory(void *ptr, size_t length) {
+  app_memory_info_.push_back(AppMemory(reinterpret_cast<ULONG64>(ptr),
+                                       static_cast<ULONG>(length)));
+}
+
+void ExceptionHandler::UnregisterAppMemory(void *ptr) {
+  for (AppMemoryList::iterator iter = app_memory_info_.begin();
+       iter != app_memory_info_.end();
+       ++iter) {
+    if (iter->ptr == reinterpret_cast<ULONG64>(ptr)) {
+      app_memory_info_.erase(iter);
+      return;
+    }
+  }
+}
+
 }  // namespace google_breakpad
--- a/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/windows/handler/exception_handler.h
@@ -62,27 +62,38 @@
 #include <rpc.h>
 
 #pragma warning( push )
 // Disable exception handler warnings.
 #pragma warning( disable : 4530 )
 
 #include <string>
 #include <vector>
+#include <list>
 
 #include "client/windows/common/ipc_protocol.h"
 #include "client/windows/crash_generation/crash_generation_client.h"
 #include "google_breakpad/common/minidump_format.h"
 #include "processor/scoped_ptr.h"
 
 namespace google_breakpad {
 
 using std::vector;
 using std::wstring;
 
+// These entries store a list of memory regions that the client wants included
+// in the minidump.
+struct AppMemory {
+  AppMemory(ULONG64 ptr, ULONG length) : ptr(ptr), length(length) {}
+
+  ULONG64 ptr;
+  ULONG length;
+};
+typedef std::list<AppMemory> AppMemoryList;
+
 class ExceptionHandler {
  public:
   // A callback function to run before Breakpad performs any substantial
   // processing of an exception.  A FilterCallback is called before writing
   // a minidump.  context is the parameter supplied by the user as
   // callback_context when the handler was created.  exinfo points to the
   // exception record, if any; assertion points to assertion information,
   // if any.
@@ -217,16 +228,21 @@ class ExceptionHandler {
   bool get_handle_debug_exceptions() const { return handle_debug_exceptions_; }
   void set_handle_debug_exceptions(bool handle_debug_exceptions) {
     handle_debug_exceptions_ = handle_debug_exceptions;
   }
 
   // Returns whether out-of-process dump generation is used or not.
   bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; }
 
+  // Calling RegisterAppMemory(p, len) causes len bytes starting
+  // at address p to be copied to the minidump when a crash happens.
+  void RegisterAppMemory(void *ptr, size_t length);
+  void UnregisterAppMemory(void *ptr);
+
  private:
   friend class AutoExceptionHandler;
 
   // Initializes the instance with given values.
   void Initialize(const wstring& dump_path,
                   FilterCallback filter,
                   MinidumpCallback callback,
                   void* callback_context,
@@ -417,16 +433,20 @@ class ExceptionHandler {
   // the requesting thread.
   bool handler_return_value_;
 
   // If true, the handler will intercept EXCEPTION_BREAKPOINT and
   // EXCEPTION_SINGLE_STEP exceptions.  Leave this false (the default)
   // to not interfere with debuggers.
   bool handle_debug_exceptions_;
 
+  // Callers can request additional memory regions to be included in
+  // the dump.
+  AppMemoryList app_memory_info_;
+
   // A stack of ExceptionHandler objects that have installed unhandled
   // exception filters.  This vector is used by HandleException to determine
   // which ExceptionHandler object to route an exception to.  When an
   // ExceptionHandler is created with install_handler true, it will append
   // itself to this list.
   static vector<ExceptionHandler*>* handler_stack_;
 
   // The index of the ExceptionHandler in handler_stack_ that will handle the
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -1148,16 +1148,42 @@ bool GetAnnotation(const nsACString& key
   nsCAutoString entry;
   if (!crashReporterAPIData_Hash->Get(key, &entry))
     return false;
 
   data = entry;
   return true;
 }
 
+nsresult RegisterAppMemory(void* ptr, size_t length)
+{
+  if (!GetEnabled())
+    return NS_ERROR_NOT_INITIALIZED;
+
+#if defined(XP_LINUX) || defined(XP_WIN32)
+  gExceptionHandler->RegisterAppMemory(ptr, length);
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
+nsresult UnregisterAppMemory(void* ptr)
+{
+  if (!GetEnabled())
+    return NS_ERROR_NOT_INITIALIZED;
+
+#if defined(XP_LINUX) || defined(XP_WIN32)
+  gExceptionHandler->UnregisterAppMemory(ptr);
+  return NS_OK;
+#else
+  return NS_ERROR_NOT_IMPLEMENTED;
+#endif
+}
+
 bool GetServerURL(nsACString& aServerURL)
 {
   if (!gExceptionHandler)
     return false;
 
   return GetAnnotation(NS_LITERAL_CSTRING("ServerURL"), aServerURL);
 }
 
@@ -1684,26 +1710,28 @@ OnChildProcessDumpRequested(void* aConte
 #endif
                      getter_AddRefs(minidump));
 
 #if defined(__ANDROID__)
   // Do dump generation here since the CrashGenerationServer doesn't
   // have access to the library mappings.
   MappingMap::const_iterator iter = 
     child_library_mappings.find(aClientInfo->pid_);
+  google_breakpad::AppMemoryList a;
   if (iter == child_library_mappings.end()) {
     NS_WARNING("No library mappings found for child, can't write minidump!");
     return;
   }
 
   if (!google_breakpad::WriteMinidump(aFilePath->c_str(),
                                       aClientInfo->pid_,
                                       aClientInfo->crash_context,
                                       aClientInfo->crash_context_size,
-                                      iter->second))
+                                      iter->second,
+                                      a))
     return;
 #endif
 
   if (!WriteExtraForMinidump(minidump,
                              Blacklist(kSubprocessBlacklist,
                                        NS_ARRAY_LENGTH(kSubprocessBlacklist)),
                              getter_AddRefs(extraFile)))
     return;
--- a/toolkit/crashreporter/nsExceptionHandler.h
+++ b/toolkit/crashreporter/nsExceptionHandler.h
@@ -65,16 +65,20 @@ nsresult SetServerURL(const nsACString& 
 bool     GetMinidumpPath(nsAString& aPath);
 nsresult SetMinidumpPath(const nsAString& aPath);
 nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data);
 nsresult AppendAppNotesToCrashReport(const nsACString& data);
 nsresult SetRestartArgs(int argc, char** argv);
 nsresult SetupExtraData(nsILocalFile* aAppDataDirectory,
                         const nsACString& aBuildID);
 
+// Registers an additional memory region to be included in the minidump
+nsresult RegisterAppMemory(void* ptr, size_t length);
+nsresult UnregisterAppMemory(void* ptr);
+
 // Functions for working with minidumps and .extras
 typedef nsDataHashtable<nsCStringHashKey, nsCString> AnnotationTable;
 
 bool GetMinidumpForID(const nsAString& id, nsILocalFile** minidump);
 bool GetIDFromMinidump(nsILocalFile* minidump, nsAString& id);
 bool GetExtraFileForID(const nsAString& id, nsILocalFile** extraFile);
 bool GetExtraFileForMinidump(nsILocalFile* minidump, nsILocalFile** extraFile);
 bool AppendExtraData(const nsAString& id, const AnnotationTable& data);
--- a/toolkit/crashreporter/test/CrashTestUtils.jsm
+++ b/toolkit/crashreporter/test/CrashTestUtils.jsm
@@ -24,16 +24,19 @@ Components.utils.import("resource://gre/
 let dir = __LOCATION__.parent;
 let file = dir.clone();
 file.append(ctypes.libraryName("testcrasher"));
 let lib = ctypes.open(file.path);
 CrashTestUtils.crash = lib.declare("Crash",
                                    ctypes.default_abi,
                                    ctypes.void_t,
                                    ctypes.int16_t);
+CrashTestUtils.saveAppMemory = lib.declare("SaveAppMemory",
+                                           ctypes.default_abi,
+                                           ctypes.uint64_t);
 
 CrashTestUtils.lockDir = lib.declare("LockDir",
                                      ctypes.default_abi,
                                      ctypes.voidptr_t,   // nsILocalFile*
                                      ctypes.voidptr_t);  // nsISupports*
 
 
 CrashTestUtils.dumpHasStream = lib.declare("DumpHasStream",
@@ -42,8 +45,13 @@ CrashTestUtils.dumpHasStream = lib.decla
                                            ctypes.char.ptr,
                                            ctypes.uint32_t);
 
 CrashTestUtils.dumpHasInstructionPointerMemory =
   lib.declare("DumpHasInstructionPointerMemory",
               ctypes.default_abi,
               ctypes.bool,
               ctypes.char.ptr);
+
+CrashTestUtils.dumpCheckMemory = lib.declare("DumpCheckMemory",
+                                             ctypes.default_abi,
+                                             ctypes.bool,
+                                             ctypes.char.ptr);
--- a/toolkit/crashreporter/test/dumputils.cpp
+++ b/toolkit/crashreporter/test/dumputils.cpp
@@ -1,8 +1,10 @@
+#include <stdio.h>
+
 #include "google_breakpad/processor/minidump.h"
 #include "nscore.h"
 
 using namespace google_breakpad;
 
 // Return true if the specified minidump contains a stream of |stream_type|.
 extern "C"
 NS_EXPORT bool
@@ -53,8 +55,53 @@ DumpHasInstructionPointerMemory(const ch
   default:
     return false;
   }
 
   MinidumpMemoryRegion* region =
     memory_list->GetMemoryRegionForAddress(instruction_pointer);
   return region != NULL;
 }
+
+// This function tests for a very specific condition. It finds
+// an address in a file, "crash-addr", in the CWD. It checks
+// that the minidump has a memory region starting at that
+// address. The region must be 32 bytes long and contain the
+// values 0 to 31 as bytes, in ascending order.
+extern "C"
+NS_EXPORT bool
+DumpCheckMemory(const char* dump_file)
+{
+  Minidump dump(dump_file);
+  if (!dump.Read())
+    return false;
+
+  MinidumpMemoryList* memory_list = dump.GetMemoryList();
+  if (!memory_list) {
+    return false;
+  }
+
+  void *addr;
+  FILE *fp = fopen("crash-addr", "r");
+  if (!fp)
+    return false;
+  if (fscanf(fp, "%p", &addr) != 1)
+    return false;
+  fclose(fp);
+
+  remove("crash-addr");
+
+  MinidumpMemoryRegion* region =
+    memory_list->GetMemoryRegionForAddress(u_int64_t(addr));
+  if(!region)
+    return false;
+
+  const u_int8_t* chars = region->GetMemory();
+  if (region->GetSize() != 32)
+    return false;
+
+  for (int i=0; i<32; i++) {
+    if (chars[i] != i)
+      return false;
+  }
+
+  return true;
+}
--- a/toolkit/crashreporter/test/nsTestCrasher.cpp
+++ b/toolkit/crashreporter/test/nsTestCrasher.cpp
@@ -1,10 +1,13 @@
+#include <stdio.h>
+
 #include "nscore.h"
 #include "nsXULAppAPI.h"
+#include "nsExceptionHandler.h"
 
 /*
  * This pure virtual call example is from MSDN
  */
 class A;
 
 void fcn( A* );
 
@@ -62,8 +65,25 @@ void Crash(PRInt16 how)
 
 extern "C" NS_EXPORT
 nsISupports* LockDir(nsILocalFile *directory)
 {
   nsISupports* lockfile = nsnull;
   XRE_LockProfileDirectory(directory, &lockfile);
   return lockfile;
 }
+
+char testData[32];
+
+extern "C" NS_EXPORT
+PRUint64 SaveAppMemory()
+{
+  for (size_t i=0; i<sizeof(testData); i++)
+    testData[i] = i;
+
+  FILE *fp = fopen("crash-addr", "w");
+  if (!fp)
+    return 0;
+  fprintf(fp, "%p\n", (void *)testData);
+  fclose(fp);
+
+  return (PRInt64)testData;
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/unit/test_crashreporter_appmem.js
@@ -0,0 +1,12 @@
+function run_test()
+{
+  do_crash(function() {
+	     appAddr = CrashTestUtils.saveAppMemory();
+	     crashReporter.registerAppMemory(appAddr, 32);
+           },
+	   function(mdump, extra) {
+             do_check_true(mdump.exists());
+             do_check_true(mdump.fileSize > 0);
+             do_check_true(CrashTestUtils.dumpCheckMemory(mdump.path));
+           });
+}
--- a/toolkit/crashreporter/test/unit/xpcshell.ini
+++ b/toolkit/crashreporter/test/unit/xpcshell.ini
@@ -2,8 +2,11 @@
 head = head_crashreporter.js
 tail = 
 
 [test_crash_purevirtual.js]
 [test_crash_runtimeabort.js]
 [test_crashreporter.js]
 [test_crashreporter_crash.js]
 [test_crashreporter_crash_profile_lock.js]
+
+[test_crashreporter_appmem.js]
+run-if = os == 'win' || os == 'linux'
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -968,16 +968,23 @@ nsXULAppInfo::AnnotateCrashReport(const 
 
 NS_IMETHODIMP
 nsXULAppInfo::AppendAppNotesToCrashReport(const nsACString& data)
 {
   return CrashReporter::AppendAppNotesToCrashReport(data);
 }
 
 NS_IMETHODIMP
+nsXULAppInfo::RegisterAppMemory(PRUint64 pointer,
+                                PRUint64 len)
+{
+  return CrashReporter::RegisterAppMemory((void *)pointer, len);
+}
+
+NS_IMETHODIMP
 nsXULAppInfo::WriteMinidumpForException(void* aExceptionInfo)
 {
 #ifdef XP_WIN32
   return CrashReporter::WriteMinidumpForException(static_cast<EXCEPTION_POINTERS*>(aExceptionInfo));
 #else
   return NS_ERROR_NOT_IMPLEMENTED;
 #endif
 }
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -351,36 +351,41 @@ InitNPCocoaEvent(NPCocoaEvent* event)
 static void DebugPrintAllKeyboardLayouts()
 {
   TextInputHandler::DebugPrintAllKeyboardLayouts(sCocoaLog);
   IMEInputHandler::DebugPrintAllIMEModes(sCocoaLog);
 }
 
 #endif // defined(DEBUG) && defined(PR_LOGGING)
 
+void EnsureLogInitialized()
+{
+#ifdef PR_LOGGING
+  if (!sCocoaLog) {
+    sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
+#ifdef DEBUG
+    DebugPrintAllKeyboardLayouts();
+#endif // DEBUG
+  }
+#endif // PR_LOGGING
+}
+
 #pragma mark -
 
 nsChildView::nsChildView() : nsBaseWidget()
 , mView(nsnull)
 , mParentView(nsnull)
 , mParentWidget(nsnull)
 , mVisible(PR_FALSE)
 , mDrawing(PR_FALSE)
 , mPluginDrawing(PR_FALSE)
 , mIsDispatchPaint(PR_FALSE)
 , mPluginInstanceOwner(nsnull)
 {
-#ifdef PR_LOGGING
-  if (!sCocoaLog) {
-    sCocoaLog = PR_NewLogModule("nsCocoaWidgets");
-#ifdef DEBUG
-    DebugPrintAllKeyboardLayouts();
-#endif // DEBUG
-  }
-#endif // PR_LOGGING
+  EnsureLogInitialized();
 
   memset(&mPluginCGContext, 0, sizeof(mPluginCGContext));
 #ifndef NP_NO_QUICKDRAW
   memset(&mPluginQDPort, 0, sizeof(mPluginQDPort));
 #endif
 
   SetBackgroundColor(NS_RGB(255, 255, 255));
   SetForegroundColor(NS_RGB(0, 0, 0));
--- a/widget/src/cocoa/nsClipboard.mm
+++ b/widget/src/cocoa/nsClipboard.mm
@@ -59,19 +59,23 @@
 
 // Screenshots use the (undocumented) png pasteboard type.
 #define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* sCocoaLog;
 #endif
 
+extern void EnsureLogInitialized();
+
 nsClipboard::nsClipboard() : nsBaseClipboard()
 {
   mChangeCount = 0;
+
+  EnsureLogInitialized();
 }
 
 nsClipboard::~nsClipboard()
 {
 }
 
 // We separate this into its own function because after an @try, all local
 // variables within that function get marked as volatile, and our C++ type 
--- a/widget/src/cocoa/nsDragService.mm
+++ b/widget/src/cocoa/nsDragService.mm
@@ -67,16 +67,18 @@
 #include "gfxContext.h"
 
 #import <Cocoa/Cocoa.h>
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* sCocoaLog;
 #endif
 
+extern void EnsureLogInitialized();
+
 extern NSPasteboard* globalDragPboard;
 extern NSView* gLastDragView;
 extern NSEvent* gLastDragMouseDownEvent;
 extern PRBool gUserCancelledDrag;
 
 // This global makes the transferable array available to Cocoa's promised
 // file destination callback.
 nsISupportsArray *gDraggedTransferables = nsnull;
@@ -85,16 +87,18 @@ NSString* const kWildcardPboardType = @"
 NSString* const kCorePboardType_url  = @"CorePasteboardFlavorType 0x75726C20"; // 'url '  url
 NSString* const kCorePboardType_urld = @"CorePasteboardFlavorType 0x75726C64"; // 'urld'  desc
 NSString* const kCorePboardType_urln = @"CorePasteboardFlavorType 0x75726C6E"; // 'urln'  title
 
 nsDragService::nsDragService()
 {
   mNativeDragView = nil;
   mNativeDragEvent = nil;
+
+  EnsureLogInitialized();
 }
 
 nsDragService::~nsDragService()
 {
 }
 
 static nsresult SetUpDragClipboard(nsISupportsArray* aTransferableArray)
 {
--- a/widget/src/windows/nsIMM32Handler.cpp
+++ b/widget/src/windows/nsIMM32Handler.cpp
@@ -288,41 +288,61 @@ nsIMM32Handler::CommitComposition(nsWind
      aWindow, aWindow->GetWindowHandle(),
      gIMM32Handler ? gIMM32Handler->mComposingWindow : nsnull,
      gIMM32Handler && gIMM32Handler->mComposingWindow ?
        IsComposingOnOurEditor() ? " (composing on editor)" :
                                   " (composing on plug-in)" : ""));
   if (!aForce && !IsComposingWindow(aWindow)) {
     return;
   }
+
+  PRBool associated = aWindow->AssociateDefaultIMC(PR_TRUE);
+  PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
+    ("IMM32: CommitComposition, associated=%s\n",
+     associated ? "YES" : "NO"));
+
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   if (IMEContext.IsValid()) {
     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_COMPLETE, 0);
     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   }
+
+  if (associated) {
+    aWindow->AssociateDefaultIMC(PR_FALSE);
+  }
 }
 
 /* static */ void
 nsIMM32Handler::CancelComposition(nsWindow* aWindow, PRBool aForce)
 {
   PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
     ("IMM32: CancelComposition, aForce=%s, aWindow=%p, hWnd=%08x, mComposingWindow=%p%s\n",
      aForce ? "TRUE" : "FALSE",
      aWindow, aWindow->GetWindowHandle(),
      gIMM32Handler ? gIMM32Handler->mComposingWindow : nsnull,
      gIMM32Handler && gIMM32Handler->mComposingWindow ?
        IsComposingOnOurEditor() ? " (composing on editor)" :
                                   " (composing on plug-in)" : ""));
   if (!aForce && !IsComposingWindow(aWindow)) {
     return;
   }
+
+  PRBool associated = aWindow->AssociateDefaultIMC(PR_TRUE);
+  PR_LOG(gIMM32Log, PR_LOG_ALWAYS,
+    ("IMM32: CancelComposition, associated=%s\n",
+     associated ? "YES" : "NO"));
+
   nsIMEContext IMEContext(aWindow->GetWindowHandle());
   if (IMEContext.IsValid()) {
     ::ImmNotifyIME(IMEContext.get(), NI_COMPOSITIONSTR, CPS_CANCEL, 0);
   }
+
+  if (associated) {
+    aWindow->AssociateDefaultIMC(PR_FALSE);
+  }
 }
 
 /* static */ PRBool
 nsIMM32Handler::ProcessInputLangChangeMessage(nsWindow* aWindow,
                                               WPARAM wParam,
                                               LPARAM lParam,
                                               LRESULT *aRetValue,
                                               PRBool &aEatMessage)
--- a/widget/src/windows/nsIMM32Handler.h
+++ b/widget/src/windows/nsIMM32Handler.h
@@ -126,16 +126,24 @@ public:
   static PRBool IsStatusChanged() { return sIsStatusChanged; }
 
   static PRBool IsDoingKakuteiUndo(HWND aWnd);
 
   static void NotifyEndStatusChange() { sIsStatusChanged = PR_FALSE; }
 
   static PRBool CanOptimizeKeyAndIMEMessages(MSG *aNextKeyOrIMEMessage);
 
+#ifdef DEBUG
+  /**
+   * IsIMEAvailable() returns TRUE when current keyboard layout has IME.
+   * Otherwise, FALSE.
+   */
+  static PRBool IsIMEAvailable() { return sIsIME; }
+#endif
+
   // If aForce is TRUE, these methods doesn't check whether we have composition
   // or not.  If you don't set it to TRUE, these method doesn't commit/cancel
   // the composition on uexpected window.
   static void CommitComposition(nsWindow* aWindow, PRBool aForce = PR_FALSE);
   static void CancelComposition(nsWindow* aWindow, PRBool aForce = PR_FALSE);
 
 protected:
   static void EnsureHandlerInstance();
--- a/widget/src/windows/nsWindow.cpp
+++ b/widget/src/windows/nsWindow.cpp
@@ -365,17 +365,16 @@ nsWindow::nsWindow() : nsBaseWidget()
 #ifdef PR_LOGGING
   if (!gWindowsLog)
     gWindowsLog = PR_NewLogModule("nsWindowsWidgets");
 #endif
 
   mWnd                  = nsnull;
   mPaintDC              = nsnull;
   mPrevWndProc          = nsnull;
-  mOldIMC               = nsnull;
   mNativeDragTarget     = nsnull;
   mInDtor               = PR_FALSE;
   mIsVisible            = PR_FALSE;
   mIsTopWidgetWindow    = PR_FALSE;
   mUnicodeWidget        = PR_TRUE;
   mDisplayPanFeedback   = PR_FALSE;
   mTouchWindow          = PR_FALSE;
   mCustomNonClient      = PR_FALSE;
@@ -6664,34 +6663,35 @@ LRESULT nsWindow::OnKeyDown(const MSG &a
 
 #ifdef DEBUG
   //printf("In OnKeyDown virt: %d\n", DOMKeyCode);
 #endif
 
   static PRBool sRedirectedKeyDownEventPreventedDefault = PR_FALSE;
   PRBool noDefault;
   if (aFakeCharMessage || !IsRedirectedKeyDownMessage(aMsg)) {
-    HIMC oldIMC = mOldIMC;
+    nsIMEContext IMEContext(mWnd);
     noDefault =
       DispatchKeyEvent(NS_KEY_DOWN, 0, nsnull, DOMKeyCode, &aMsg, aModKeyState);
     if (aEventDispatched) {
       *aEventDispatched = PR_TRUE;
     }
 
     // If IMC wasn't associated to the window but is associated it now (i.e.,
     // focus is moved from a non-editable editor to an editor by keydown
     // event handler), WM_CHAR and WM_SYSCHAR shouldn't cause first character
     // inputting if IME is opened.  But then, we should redirect the native
     // keydown message to IME.
     // However, note that if focus has been already moved to another
     // application, we shouldn't redirect the message to it because the keydown
     // message is processed by us, so, nobody shouldn't process it.
     HWND focusedWnd = ::GetFocus();
-    if (!noDefault && !aFakeCharMessage && oldIMC && !mOldIMC && focusedWnd &&
-        !PluginHasFocus()) {
+    nsIMEContext newIMEContext(mWnd);
+    if (!noDefault && !aFakeCharMessage && focusedWnd && !PluginHasFocus() &&
+        !IMEContext.get() && newIMEContext.get()) {
       RemoveNextCharMessage(focusedWnd);
 
       INPUT keyinput;
       keyinput.type = INPUT_KEYBOARD;
       keyinput.ki.wVk = aMsg.wParam;
       keyinput.ki.wScan = GetScanCode(aMsg.lParam);
       keyinput.ki.dwFlags = KEYEVENTF_SCANCODE;
       if (IsExtendedScanCode(aMsg.lParam)) {
@@ -7310,21 +7310,18 @@ void nsWindow::OnDestroy()
   // If we're going away and for some reason we're still the rollup widget, rollup and
   // turn off capture.
   if ( this == sRollupWidget ) {
     if ( sRollupListener )
       sRollupListener->Rollup(nsnull, nsnull);
     CaptureRollupEvents(nsnull, nsnull, PR_FALSE, PR_TRUE);
   }
 
-  // If IME is disabled, restore it.
-  if (mOldIMC) {
-    mOldIMC = ::ImmAssociateContext(mWnd, mOldIMC);
-    NS_ASSERTION(!mOldIMC, "Another IMC was associated");
-  }
+  // Restore the IM context.
+  AssociateDefaultIMC(PR_TRUE);
 
   // Turn off mouse trails if enabled.
   MouseTrailer* mtrailer = nsToolkit::gMouseTrailer;
   if (mtrailer) {
     if (mtrailer->GetMouseTrailerWindow() == mWnd)
       mtrailer->DestroyTimer();
 
     if (mtrailer->GetCaptureWindow() == mWnd)
@@ -7953,21 +7950,17 @@ NS_IMETHODIMP nsWindow::SetInputMode(con
 #endif 
   if (nsIMM32Handler::IsComposing()) {
     ResetInputState();
   }
   mIMEContext = aContext;
   PRBool enable = (status == nsIWidget::IME_STATUS_ENABLED ||
                    status == nsIWidget::IME_STATUS_PLUGIN);
 
-  if (!enable != !mOldIMC)
-    return NS_OK;
-  mOldIMC = ::ImmAssociateContext(mWnd, enable ? mOldIMC : NULL);
-  NS_ASSERTION(!enable || !mOldIMC, "Another IMC was associated");
-
+  AssociateDefaultIMC(enable);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsWindow::GetInputMode(IMEContext& aContext)
 {
 #ifdef DEBUG_KBSTATE
   printf("GetInputMode: %s\n", mIMEContext.mStatus ? "Enabled" : "Disabled");
 #endif 
@@ -8020,16 +8013,52 @@ nsWindow::OnIMETextChange(PRUint32 aStar
 
 NS_IMETHODIMP
 nsWindow::OnIMESelectionChange(void)
 {
   return nsTextStore::OnSelectionChange();
 }
 #endif //NS_ENABLE_TSF
 
+PRBool nsWindow::AssociateDefaultIMC(PRBool aAssociate)
+{
+  nsIMEContext IMEContext(mWnd);
+
+  if (aAssociate) {
+    BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, IACE_DEFAULT);
+#ifdef DEBUG
+    // Note that if IME isn't available with current keyboard layout,
+    // IMM might not be installed on the system such as English Windows.
+    // On such system, IMM APIs always fail.
+    NS_ASSERTION(ret || !nsIMM32Handler::IsIMEAvailable(),
+                 "ImmAssociateContextEx failed to restore default IMC");
+    if (ret) {
+      nsIMEContext newIMEContext(mWnd);
+      NS_ASSERTION(!IMEContext.get() || newIMEContext.get() == IMEContext.get(),
+                   "Unknown IMC had been associated");
+    }
+#endif
+    return ret && !IMEContext.get();
+  }
+
+  if (mOnDestroyCalled) {
+    // If OnDestroy() has been called, we shouldn't disassociate the default
+    // IMC at destroying the window.
+    return PR_FALSE;
+  }
+
+  if (!IMEContext.get()) {
+    return PR_FALSE; // already disassociated
+  }
+
+  BOOL ret = ::ImmAssociateContextEx(mWnd, NULL, 0);
+  NS_ASSERTION(ret, "ImmAssociateContextEx failed to disassociate the IMC");
+  return ret != FALSE;
+}
+
 #ifdef ACCESSIBILITY
 
 #ifdef DEBUG_WMGETOBJECT
 #define NS_LOG_WMGETOBJECT_WNDACC(aWnd)                                        \
   nsAccessible* acc = aWnd ?                                                   \
     aWnd->DispatchAccessibleEvent(NS_GETACCESSIBLE) : nsnull;                  \
   printf("     acc: %p", acc);                                                 \
   if (acc) {                                                                   \
--- a/widget/src/windows/nsWindow.h
+++ b/widget/src/windows/nsWindow.h
@@ -257,16 +257,31 @@ public:
    * called.
    *
    * @param aReinitialize Call GetLayerManager on widgets to ensure D3D9 is
    *                      initialized, this is usually called when this function
    *                      is triggered by timeout and not user/web interaction.
    */
   static void             StartAllowingD3D9(bool aReinitialize);
 
+  /**
+   * AssociateDefaultIMC() associates or disassociates the default IMC for
+   * the window.
+   *
+   * @param aAssociate    TRUE, associates the default IMC with the window.
+   *                      Otherwise, disassociates the default IMC from the
+   *                      window.
+   * @return              TRUE if this method associated the default IMC with
+   *                      disassociated window or disassociated the default IMC
+   *                      from associated window.
+   *                      Otherwise, i.e., if this method did nothing actually,
+   *                      FALSE.
+   */
+  PRBool                  AssociateDefaultIMC(PRBool aAssociate);
+
 #if MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_WIN7
   PRBool HasTaskbarIconBeenCreated() { return mHasTaskbarIconBeenCreated; }
   // Called when either the nsWindow or an nsITaskbarTabPreview receives the noticiation that this window
   // has its icon placed on the taskbar.
   void SetHasTaskbarIconBeenCreated(PRBool created = PR_TRUE) { mHasTaskbarIconBeenCreated = created; }
 
   // Getter/setter for the nsITaskbarWindowPreview for this nsWindow
   already_AddRefed<nsITaskbarWindowPreview> GetTaskbarPreview() {
@@ -499,17 +514,16 @@ protected:
   PRPackedBool          mDisplayPanFeedback;
   PRPackedBool          mHideChrome;
   PRPackedBool          mIsRTL;
   PRPackedBool          mFullscreenMode;
   PRPackedBool          mMousePresent;
   PRUint32              mBlurSuppressLevel;
   DWORD_PTR             mOldStyle;
   DWORD_PTR             mOldExStyle;
-  HIMC                  mOldIMC;
   IMEContext            mIMEContext;
   nsNativeDragTarget*   mNativeDragTarget;
   HKL                   mLastKeyboardLayout;
   nsPopupType           mPopupType;
   nsSizeMode            mOldSizeMode;
   WindowHook            mWindowHook;
   DWORD                 mAssumeWheelIsZoomUntil;
   static PRBool         sDropShadowEnabled;
--- a/xpcom/base/nsIMemoryReporter.idl
+++ b/xpcom/base/nsIMemoryReporter.idl
@@ -45,27 +45,27 @@ interface nsISimpleEnumerator;
  * Use this when it makes sense to gather this measurement without gathering
  * related measurements at the same time.
  *
  * Note that the |amount| field may be implemented as a function, and so
  * accessing it can trigger significant computation;  the other fields can
  * be accessed without triggering this computation.  (Compare and contrast
  * this with nsIMemoryMultiReporter.)  
  */
-[scriptable, uuid(37d18434-9819-4ce1-922f-15d8b63da066)]
+[scriptable, uuid(b2c39f65-1799-4b92-a806-ab3cf6af3cfa)]
 interface nsIMemoryReporter : nsISupports
 {
   /*
    * The name of the process containing this reporter.  Each reporter initially
    * has "" in this field, indicating that it applies to the current process.
    * (This is true even for reporters in a child process.)  When a reporter
    * from a child process is copied into the main process, the copy has its
    * 'process' field set appropriately.
    */
-  readonly attribute string process;
+  readonly attribute ACString process;
 
   /*
    * The path that this memory usage should be reported under.  Paths are
    * '/'-delimited, eg. "a/b/c".  There are two categories of paths.
    *
    * - Paths starting with "explicit" represent regions of memory that have
    *   been explicitly allocated with an OS-level allocation (eg.
    *   mmap/VirtualAlloc/vm_allocate) or a heap-level allocation (eg.
@@ -89,17 +89,17 @@ interface nsIMemoryReporter : nsISupport
    *
    *   A node's children divide their parent's memory into disjoint pieces.
    *   So in the example above, |a| may not count any allocations counted by
    *   |d|, and vice versa.
    *
    * - All other paths represent cross-cutting values and may overlap with any
    *   other reporter.
    */
-  readonly attribute string path;
+  readonly attribute AUTF8String path;
 
   /*
    * There are three categories of memory reporters:
    *
    *  - MAPPED: memory allocated directly by the OS, e.g. by calling mmap,
    *    VirtualAlloc, or vm_allocate.  Reporters in this category must have
    *    units UNITS_BYTES and must have a path starting with "explicit".
    *
@@ -144,17 +144,17 @@ interface nsIMemoryReporter : nsISupport
    * The numeric value reported by this memory reporter.  -1 means "unknown",
    * ie. something went wrong when getting the amount.
    */
   readonly attribute PRInt64 amount;
 
   /*
    * A human-readable description of this memory usage report.
    */
-  readonly attribute string description;
+  readonly attribute AUTF8String description;
 };
 
 [scriptable, function, uuid(5b15f3fa-ba15-443c-8337-7770f5f0ce5d)]
 interface nsIMemoryMultiReporterCallback : nsISupports
 {
   void callback(in ACString process, in AUTF8String path, in PRInt32 kind,
                 in PRInt32 units, in PRInt64 amount,
                 in AUTF8String description, in nsISupports closure);
@@ -242,26 +242,26 @@ interface nsIMemoryReporterManager : nsI
   readonly attribute PRInt64 explicit;
 };
 
 %{C++
 
 /*
  * Note that this defaults 'process' to "", which is usually what's desired.
  */
-#define NS_MEMORY_REPORTER_IMPLEMENT(_classname, _path, _kind, _units, _usageFunction, _desc) \
+#define NS_MEMORY_REPORTER_IMPLEMENT(_classname, _path, _kind, _units, _amountFunction, _desc) \
     class MemoryReporter_##_classname : public nsIMemoryReporter {                            \
     public:                                                                                   \
       NS_DECL_ISUPPORTS                                                                       \
-      NS_IMETHOD GetProcess(char **process) { *process = strdup(""); return NS_OK; }          \
-      NS_IMETHOD GetPath(char **memoryPath) { *memoryPath = strdup(_path); return NS_OK; }    \
+      NS_IMETHOD GetProcess(nsACString &process) { process.Truncate(); return NS_OK; } \
+      NS_IMETHOD GetPath(nsACString &memoryPath) { memoryPath.Assign(_path); return NS_OK; } \
       NS_IMETHOD GetKind(int *kind) { *kind = _kind; return NS_OK; }                          \
       NS_IMETHOD GetUnits(int *units) { *units = _units; return NS_OK; }                      \
-      NS_IMETHOD GetAmount(PRInt64 *amount) { *amount = _usageFunction(); return NS_OK; }     \
-      NS_IMETHOD GetDescription(char **desc) { *desc = strdup(_desc); return NS_OK; }         \
+      NS_IMETHOD GetAmount(PRInt64 *amount) { *amount = _amountFunction(); return NS_OK; }     \
+      NS_IMETHOD GetDescription(nsACString &desc) { desc.Assign(_desc); return NS_OK; } \
     };                                                                                        \
     NS_IMPL_ISUPPORTS1(MemoryReporter_##_classname, nsIMemoryReporter)
 
 #define NS_MEMORY_REPORTER_NAME(_classname)  MemoryReporter_##_classname
 
 NS_COM nsresult NS_RegisterMemoryReporter (nsIMemoryReporter *reporter);
 NS_COM nsresult NS_RegisterMemoryMultiReporter (nsIMemoryMultiReporter *reporter);
 
--- a/xpcom/base/nsMemoryReporterManager.cpp
+++ b/xpcom/base/nsMemoryReporterManager.cpp
@@ -598,32 +598,32 @@ nsMemoryReporterManager::GetExplicit(PRI
         e->GetNext(getter_AddRefs(r));
 
         PRInt32 kind;
         nsresult rv = r->GetKind(&kind);
         NS_ENSURE_SUCCESS(rv, rv);
 
         if (kind == nsIMemoryReporter::KIND_MAPPED) {
             nsCString path;
-            rv = r->GetPath(getter_Copies(path));
+            rv = r->GetPath(path);
             NS_ENSURE_SUCCESS(rv, rv);
 
             PRInt64 amount;
             rv = r->GetAmount(&amount);
             NS_ENSURE_SUCCESS(rv, rv);
 
             // Just skip any MAPPED reporters that fail, because "heap-used" is
             // the most important one.
             if (amount != PRInt64(-1)) {
                 MemoryReport mr(path, amount);
                 mapped.AppendElement(mr);
             }
         } else {
             nsCString path;
-            rv = r->GetPath(getter_Copies(path));
+            rv = r->GetPath(path);
             NS_ENSURE_SUCCESS(rv, rv);
 
             if (path.Equals("heap-used")) {
                 rv = r->GetAmount(&heapUsed);
                 NS_ENSURE_SUCCESS(rv, rv);
                 // If "heap-used" fails, we give up, because the result would be
                 // horribly inaccurate.
                 if (heapUsed == PRInt64(-1)) {
@@ -669,44 +669,44 @@ nsMemoryReporterManager::GetExplicit(PRI
         *aExplicit += mapped[i].amount;
     }
 
     return NS_OK;
 }
 
 NS_IMPL_ISUPPORTS1(nsMemoryReporter, nsIMemoryReporter)
 
-nsMemoryReporter::nsMemoryReporter(nsCString& process,
-                                   nsCString& path,
+nsMemoryReporter::nsMemoryReporter(nsACString& process,
+                                   nsACString& path,
                                    PRInt32 kind,
                                    PRInt32 units,
                                    PRInt64 amount,
-                                   nsCString& desc)
+                                   nsACString& desc)
 : mProcess(process)
 , mPath(path)
 , mKind(kind)
 , mUnits(units)
 , mAmount(amount)
 , mDesc(desc)
 {
 }
 
 nsMemoryReporter::~nsMemoryReporter()
 {
 }
 
-NS_IMETHODIMP nsMemoryReporter::GetProcess(char **aProcess)
+NS_IMETHODIMP nsMemoryReporter::GetProcess(nsACString &aProcess)
 {
-    *aProcess = strdup(mProcess.get());
+    aProcess.Assign(mProcess);
     return NS_OK;
 }
 
-NS_IMETHODIMP nsMemoryReporter::GetPath(char **aPath)
+NS_IMETHODIMP nsMemoryReporter::GetPath(nsACString &aPath)
 {
-    *aPath = strdup(mPath.get());
+    aPath.Assign(mPath);
     return NS_OK;
 }
 
 NS_IMETHODIMP nsMemoryReporter::GetKind(PRInt32 *aKind)
 {
     *aKind = mKind;
     return NS_OK;
 }
@@ -718,19 +718,19 @@ NS_IMETHODIMP nsMemoryReporter::GetUnits
 }
 
 NS_IMETHODIMP nsMemoryReporter::GetAmount(PRInt64 *aAmount)
 {
     *aAmount = mAmount;
     return NS_OK;
 }
 
-NS_IMETHODIMP nsMemoryReporter::GetDescription(char **aDescription)
+NS_IMETHODIMP nsMemoryReporter::GetDescription(nsACString &aDescription)
 {
-    *aDescription = strdup(mDesc.get());
+    aDescription.Assign(mDesc);
     return NS_OK;
 }
 
 NS_COM nsresult
 NS_RegisterMemoryReporter (nsIMemoryReporter *reporter)
 {
     nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
     if (mgr == nsnull)
--- a/xpcom/base/nsMemoryReporterManager.h
+++ b/xpcom/base/nsMemoryReporterManager.h
@@ -6,22 +6,22 @@
 using mozilla::Mutex;
 
 class nsMemoryReporter : public nsIMemoryReporter
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIMEMORYREPORTER
 
-  nsMemoryReporter(nsCString& process,
-                   nsCString& path, 
+  nsMemoryReporter(nsACString& process,
+                   nsACString& path, 
                    PRInt32 kind,
                    PRInt32 units,
                    PRInt64 amount,
-                   nsCString& desc);
+                   nsACString& desc);
 
   ~nsMemoryReporter();
 
 protected:
   nsCString mProcess;
   nsCString mPath;
   PRInt32   mKind;
   PRInt32   mUnits;
--- a/xpcom/system/nsICrashReporter.idl
+++ b/xpcom/system/nsICrashReporter.idl
@@ -96,17 +96,30 @@ interface nsICrashReporter : nsISupports
    * @param data
    *        Data to be added.
    *
    * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
    * @throw NS_ERROR_INVALID_ARG if data contains invalid characters.
    *                             The only invalid character is '\0'.
    */
   void appendAppNotesToCrashReport(in ACString data);
-  
+
+  /**
+   * Register a given memory range to be included in the crash report.
+   *
+   * @param ptr
+   *        The starting address for the bytes.
+   * @param size
+   *        The number of bytes to include.
+   *
+   * @throw NS_ERROR_NOT_INITIALIZED if crash reporting not initialized
+   * @throw NS_ERROR_NOT_IMPLEMENTED if unavailable on the current OS
+   */
+  void registerAppMemory(in unsigned long long ptr, in unsigned long long size);
+
   /**
    * Write a minidump immediately, with the user-supplied exception
    * information. This is implemented on Windows only, because
    * SEH (structured exception handling) exists on Windows only.
    *
    * @param aExceptionInfo  EXCEPTION_INFO* provided by Window's SEH
    */
   [noscript] void writeMinidumpForException(in voidPtr aExceptionInfo);