Merge mozilla-central to mozilla-inbound
authorMatt Brubeck <mbrubeck@mozilla.com>
Fri, 30 Sep 2011 21:02:01 -0700
changeset 78389 08b0c6d4e52049b0b88f384e2652ec1c81d0d196
parent 78358 f25928e4847d4b543697bbf5cdb3cbfbd023f275 (current diff)
parent 78388 24fc6ed197968141d5fe7cb1d3ca7453a0aec7f3 (diff)
child 78390 bf32d490e3bbb302cb250e47c29f982f84fc8f13
push idunknown
push userunknown
push dateunknown
milestone10.0a1
Merge mozilla-central to mozilla-inbound
browser/components/feeds/test/test_bug368464.html
browser/components/feeds/test/test_bug408328.html
--- a/accessible/src/html/nsHTMLTableAccessible.cpp
+++ b/accessible/src/html/nsHTMLTableAccessible.cpp
@@ -1355,72 +1355,104 @@ nsHTMLTableAccessible::IsProbablyForLayo
 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) \
   { *aIsProbablyForLayout = isLayout; \
     mLayoutHeuristic = isLayout ? NS_LITERAL_STRING("layout table: ") : NS_LITERAL_STRING("data table: "); \
     mLayoutHeuristic += NS_LITERAL_STRING(heuristic); return NS_OK; }
 #else
 #define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { *aIsProbablyForLayout = isLayout; return NS_OK; }
 #endif
 
-  *aIsProbablyForLayout = PR_FALSE;
+  *aIsProbablyForLayout = false;
 
   if (IsDefunct())
     return NS_ERROR_FAILURE;
 
   nsDocAccessible *docAccessible = GetDocAccessible();
   if (docAccessible) {
     PRUint64 docState = docAccessible->State();
     if (docState & states::EDITABLE) {  // Need to see all elements while document is being edited
-      RETURN_LAYOUT_ANSWER(PR_FALSE, "In editable document");
+      RETURN_LAYOUT_ANSWER(false, "In editable document");
     }
   }
 
   // Check to see if an ARIA role overrides the role from native markup,
   // but for which we still expose table semantics (treegrid, for example).
   bool hasNonTableRole = (Role() != nsIAccessibleRole::ROLE_TABLE);
   if (hasNonTableRole) {
-    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute");
+    RETURN_LAYOUT_ANSWER(false, "Has role attribute");
   }
 
   if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
     // Role attribute is present, but overridden roles have already been dealt with.
     // Only landmarks and other roles that don't override the role from native
     // markup are left to deal with here.
-    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute, weak role, and role is table");
+    RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
   }
-  
-  // Check for legitimate data table elements or attributes
+
+  // Check for legitimate data table attributes.
   nsAutoString summary;
-  if ((mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
-       !summary.IsEmpty()) || 
-      HasDescendant(NS_LITERAL_STRING("caption"), PR_FALSE) ||
-      HasDescendant(NS_LITERAL_STRING("th")) ||
-      HasDescendant(NS_LITERAL_STRING("thead")) ||
-      HasDescendant(NS_LITERAL_STRING("tfoot")) ||
-      HasDescendant(NS_LITERAL_STRING("colgroup"))) {
-    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has caption, summary, th, thead, tfoot or colgroup -- legitimate table structures");
+  if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
+      !summary.IsEmpty())
+    RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
+
+  // Check for legitimate data table elements.
+  nsAccessible* caption = FirstChild();
+  if (caption && caption->Role() == nsIAccessibleRole::ROLE_CAPTION &&
+      caption->HasChildren()) {
+    RETURN_LAYOUT_ANSWER(false,
+                               "Not empty caption -- legitimate table structures");
   }
+
+  for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
+       childElm = childElm->GetNextSibling()) {
+    if (!childElm->IsHTML())
+      continue;
+
+    if (childElm->Tag() == nsGkAtoms::col ||
+        childElm->Tag() == nsGkAtoms::colgroup ||
+        childElm->Tag() == nsGkAtoms::tfoot ||
+        childElm->Tag() == nsGkAtoms::thead) {
+      RETURN_LAYOUT_ANSWER(false,
+                           "Has col, colgroup, tfoot or thead -- legitimate table structures");
+    }
+
+    if (childElm->Tag() == nsGkAtoms::tbody) {
+      for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
+           rowElm = rowElm->GetNextSibling()) {
+        if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) {
+          for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
+               cellElm = cellElm->GetNextSibling()) {
+            if (cellElm->IsHTML() && cellElm->Tag() == nsGkAtoms::th) {
+              RETURN_LAYOUT_ANSWER(false,
+                                   "Has th -- legitimate table structures");
+            }
+          }
+        }
+      }
+    }
+  }
+
   if (HasDescendant(NS_LITERAL_STRING("table"))) {
-    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has a nested table within it");
+    RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
   }
   
   // If only 1 column or only 1 row, it's for layout
   PRInt32 columns, rows;
   GetColumnCount(&columns);
   if (columns <=1) {
-    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 column");
+    RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
   }
   GetRowCount(&rows);
   if (rows <=1) {
-    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 row");
+    RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
   }
 
   // Check for many columns
   if (columns >= 5) {
-    RETURN_LAYOUT_ANSWER(PR_FALSE, ">=5 columns");
+    RETURN_LAYOUT_ANSWER(false, ">=5 columns");
   }
   
   // Now we know there are 2-4 columns and 2 or more rows
   // Check to see if there are visible borders on the cells
   // XXX currently, we just check the first cell -- do we really need to do more?
   nsCOMPtr<nsIDOMElement> cellElement;
   nsresult rv = GetCellAt(0, 0, *getter_AddRefs(cellElement));
   NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
@@ -1429,17 +1461,17 @@ nsHTMLTableAccessible::IsProbablyForLayo
   NS_ENSURE_TRUE(cellContent, NS_ERROR_FAILURE);
   nsIFrame *cellFrame = cellContent->GetPrimaryFrame();
   if (!cellFrame) {
     return NS_OK;
   }
   nsMargin border;
   cellFrame->GetBorder(border);
   if (border.top && border.bottom && border.left && border.right) {
-    RETURN_LAYOUT_ANSWER(PR_FALSE, "Has nonzero border-width on table cell");
+    RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
   }
 
   /**
    * Rules for non-bordered tables with 2-4 columns and 2+ rows from here on forward
    */
 
   // Check for styled background color across the row
   // Alternating background color is a common way 
@@ -1457,31 +1489,31 @@ nsHTMLTableAccessible::IsProbablyForLayo
 
     nsCOMPtr<nsIDOMCSSStyleDeclaration> styleDecl =
       nsCoreUtils::GetComputedStyleDeclaration(EmptyString(), rowContent);
     NS_ENSURE_TRUE(styleDecl, NS_ERROR_FAILURE);
 
     lastRowColor = color;
     styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), color);
     if (rowCount > 0 && PR_FALSE == lastRowColor.Equals(color)) {
-      RETURN_LAYOUT_ANSWER(PR_FALSE, "2 styles of row background color, non-bordered");
+      RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered");
     }
   }
 
   // Check for many rows
   const PRInt32 kMaxLayoutRows = 20;
   if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data
-    RETURN_LAYOUT_ANSWER(PR_FALSE, ">= kMaxLayoutRows (20) and non-bordered");
+    RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
   }
 
   // Check for very wide table
   nsAutoString styledWidth;
   GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("width"), styledWidth);
   if (styledWidth.EqualsLiteral("100%")) {
-    RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns and 100% width");
+    RETURN_LAYOUT_ANSWER(true, "<=4 columns and 100% width");
   }
   if (styledWidth.Find(NS_LITERAL_STRING("px"))) { // Hardcoded in pixels
     nsIFrame *tableFrame = GetFrame();
     NS_ENSURE_TRUE(tableFrame , NS_ERROR_FAILURE);
     nsSize tableSize  = tableFrame->GetSize();
 
     nsDocAccessible *docAccessible = GetDocAccessible();
     NS_ENSURE_TRUE(docAccessible, NS_ERROR_FAILURE);
@@ -1489,34 +1521,34 @@ nsHTMLTableAccessible::IsProbablyForLayo
     NS_ENSURE_TRUE(docFrame , NS_ERROR_FAILURE);
 
     nsSize docSize = docFrame->GetSize();
     if (docSize.width > 0) {
       PRInt32 percentageOfDocWidth = (100 * tableSize.width) / docSize.width;
       if (percentageOfDocWidth > 95) {
         // 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
         // Probably for layout
-        RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns, width hardcoded in pixels and 95% of document width");
+        RETURN_LAYOUT_ANSWER(true, "<=4 columns, width hardcoded in pixels and 95% of document width");
       }
     }
   }
 
   // Two column rules
   if (rows * columns <= 10) {
-    RETURN_LAYOUT_ANSWER(PR_TRUE, "2-4 columns, 10 cells or less, non-bordered");
+    RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
   }
 
   if (HasDescendant(NS_LITERAL_STRING("embed")) ||
       HasDescendant(NS_LITERAL_STRING("object")) ||
       HasDescendant(NS_LITERAL_STRING("applet")) ||
       HasDescendant(NS_LITERAL_STRING("iframe"))) {
-    RETURN_LAYOUT_ANSWER(PR_TRUE, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
+    RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
   }
 
-  RETURN_LAYOUT_ANSWER(PR_FALSE, "no layout factor strong enough, so will guess data");
+  RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
 }
 
 
 ////////////////////////////////////////////////////////////////////////////////
 // nsHTMLCaptionAccessible
 ////////////////////////////////////////////////////////////////////////////////
 
 Relation
--- a/accessible/tests/mochitest/table/test_layoutguess.html
+++ b/accessible/tests/mochitest/table/test_layoutguess.html
@@ -31,30 +31,28 @@
       testAbsentAttrs("table3", attr);
 
       // table with caption
       testAbsentAttrs("table4", attr);
 
       // table with empty caption
       testAttrs("table4.2", attr, true);
 
-      // table with two captions
-      testAbsentAttrs("table4.3", attr);
-
       // table with th element
       testAbsentAttrs("table5", attr);
 
       // table with thead element
       testAbsentAttrs("table6", attr);
 
       // table with tfoot element
       testAbsentAttrs("table7", attr);
 
-      // table with colgroup element
+      // table with colgroup or col elements
       testAbsentAttrs("table8", attr);
+      testAbsentAttrs("table8.2", attr);
 
       // layout table with nested table
       testAttrs("table9", attr, true);
 
       // layout table with 1 column
       testAttrs("table10", attr, true);
 
       // layout table with 1 row
@@ -82,30 +80,43 @@
       testAttrs("table18", attr, true);
 
       // layout table with embedded iframe
       testAttrs("table19", attr, true);
 
       // tree grid, no layout table
       testAbsentAttrs("table20", attr);
 
+      // layout table containing nested data table (having data structures)
+      testAttrs("table21", attr, true);
+      testAttrs("table21.2", attr, true);
+      testAttrs("table21.3", attr, true);
+      testAttrs("table21.4", attr, true);
+      testAttrs("table21.5", attr, true);
+      testAttrs("table21.6", attr, true);
+
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
 <body>
 
   <a target="_blank"
      href="https://bugzilla.mozilla.org/show_bug.cgi?id=495388"
      title="Don't treat tables that have a landmark role as layout table">
     Mozilla Bug 495388
   </a>
+  <a target="_blank"
+     href="https://bugzilla.mozilla.org/show_bug.cgi?id=690222"
+     title="Data table elements used to determine layout-guess attribute shouldn't be picked from nested tables">
+    Mozilla Bug 690222
+  </a>
 
   <p id="display"></p>
   <div id="content" style="display: none"></div>
   <pre id="test">
   </pre>
 
   <!-- Table with role of grid -->
   <table id="table1" role="grid">
@@ -173,25 +184,16 @@
   <!-- table with empty caption -->
   <table id="table4.2">
     <caption> </caption>
     <tr>
       <td>Cell1</td><td>cell2</td>
     </tr>
   </table>
 
-  <!-- table with two captions -->
-  <table id="table4.3">
-    <caption> </caption>
-    <tr>
-      <td>Cell1</td><td>cell2</td>
-    </tr>
-    <caption>a caption</caption>
-  </table>
-
   <!-- table with th element -->
   <table id="table5">
     <tr>
       <th>Cell1</th><th>cell2</th>
     </tr>
   </table>
 
   <!-- table with thead element -->
@@ -207,20 +209,27 @@
   <table id="table7">
     <tfoot>
       <tr>
         <td>Cell1</td><td>cell2</td>
       </tr>
     </tfoot>
   </table>
 
-  <!-- table with colgroup element -->
+  <!-- table with colgroup and col elements -->
   <table id="table8">
+    <colgroup width="20"></colgroup>
     <tr>
-      <colgroup><td>Cell1</td><td>cell2</td></colgroup>
+      <td>Cell1</td><td>cell2</td>
+    </tr>
+  </table>
+  <table id="table8.2">
+    <col width="20">
+    <tr>
+      <td>Cell1</td><td>cell2</td>
     </tr>
   </table>
 
   <!-- layout table with nested table -->
   <table id="table9">
     <tr>
       <td><table><tr><td>Cell</td></tr></table></td>
     </tr>
@@ -334,10 +343,73 @@
     <tr><td> </td><td> </td><td> </td></tr>
     <tr><td> </td><td> </td><td> </td></tr>
   </table>
 
   <!-- tree grid, no layout table -->
   <table id="table20" role="treegrid">
     <tr role="treeitem"><td>Cell1</td><td>Cell2</td></tr>
   </table>
+
+  <!-- layout table with nested data table containing data table elements -->
+  <table id="table21">
+    <tr>
+      <td>
+        <table>
+          <caption>table</caption>
+          <tr><td>Cell</td></tr>
+        </table>
+      </td>
+    </tr>
+  </table>
+  <table id="table21.2">
+    <tr>
+      <td>
+        <table>
+          <colgroup width="20"></colgroup>
+          <tr><th>Cell</th></tr>
+        </table>
+      </td>
+    </tr>
+  </table>
+  <table id="table21.3">
+    <tr>
+      <td>
+        <table>
+          <col width="20"></col>
+          <tr><th>Cell</th></tr>
+        </table>
+      </td>
+    </tr>
+  </table>
+  <table id="table21.4">
+    <tr>
+      <td>
+        <table>
+          <tr><th>Cell</th></tr>
+        </table>
+      </td>
+    </tr>
+  </table>
+  <table id="table21.5">
+    <tr>
+      <td>
+        <table>
+          <thead>
+            <tr><td>Cell</td></tr>
+          </thead>
+        </table>
+      </td>
+    </tr>
+  </table>
+  <table id="table21.6">
+    <tr>
+      <td>
+        <table>
+          <tfoot>
+            <tr><td>Cell</td></tr>
+          </tfoot>
+        </table>
+      </td>
+    </tr>
+  </table>
 </body>
 </html>
--- a/browser/components/feeds/test/Makefile.in
+++ b/browser/components/feeds/test/Makefile.in
@@ -46,19 +46,17 @@ MODULE		= test_browser_feeds
 XPCSHELL_TESTS	= unit
 
 DIRS = \
 	chrome \
 	$(NULL)
 
 include $(topsrcdir)/config/rules.mk
 
-_TEST_FILES =	test_bug408328.html \
-		bug408328-data.xml \
-		test_bug368464.html \
+_TEST_FILES =	bug408328-data.xml \
 		bug368464-data.xml \
 		test_bug494328.html \
 		bug494328-data.xml \
 		test_bug589543.html \
 		bug589543-data.xml \
 		test_registerHandler.html \
 		$(NULL)
 
--- a/browser/components/feeds/test/chrome/Makefile.in
+++ b/browser/components/feeds/test/chrome/Makefile.in
@@ -46,16 +46,18 @@ include $(topsrcdir)/config/rules.mk
 
 # sample_feed.atom was copied from toolkit/components/places/tests/chrome
 _HTTP_FILES	= \
 		sample_feed.atom \
 		$(NULL)
 
 _CHROME_FILES	= \
 		test_423060.xul \
+		test_bug368464.html \
+		test_bug408328.html \
 		$(NULL)
 
 libs:: $(_HTTP_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
 
 libs:: $(_CHROME_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/chrome/$(relativesrcdir)
 
rename from browser/components/feeds/test/test_bug368464.html
rename to browser/components/feeds/test/chrome/test_bug368464.html
--- a/browser/components/feeds/test/test_bug368464.html
+++ b/browser/components/feeds/test/chrome/test_bug368464.html
@@ -1,32 +1,31 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=368464
 -->
 <head>
   <title>Test that RSS 0.90 isn't sniffed</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=368464">Mozilla Bug 368464</a>
-<p id="display"><iframe id="testFrame" src="bug368464-data.xml"></iframe></p>
+<p id="display"><iframe id="testFrame" src="http://mochi.test:8888/tests/browser/components/feeds/test/bug368464-data.xml"></iframe></p>
 <div id="content" style="display: none">
 
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 368464 **/
 SimpleTest.waitForExplicitFinish();
 
 addLoadEvent(function() {
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
   ok($("testFrame").contentDocument.documentElement.id != "feedHandler",
      "RSS 0.90 shouldn't be sniffed as a feed");
 });
 addLoadEvent(SimpleTest.finish);
 
 </script>
 </pre>
 </body>
rename from browser/components/feeds/test/test_bug408328.html
rename to browser/components/feeds/test/chrome/test_bug408328.html
--- a/browser/components/feeds/test/test_bug408328.html
+++ b/browser/components/feeds/test/chrome/test_bug408328.html
@@ -1,32 +1,31 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=408328
 -->
 <head>
   <title>Test feed preview safe-linkification</title>
-  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408328">Mozilla Bug 408328</a>
-<p id="display"><iframe id="testFrame" src="bug408328-data.xml"></iframe></p>
+<p id="display"><iframe id="testFrame" src="http://mochi.test:8888/tests/browser/components/feeds/test/bug408328-data.xml"></iframe></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 408328 **/
 SimpleTest.waitForExplicitFinish();
 
 addLoadEvent(function() {
-  netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
   var links = $("testFrame").contentDocument.getElementById("feedContent").getElementsByTagName("a");
   is(links.length, 5, "wrong number of linked items in feed preview");
   for (var i = 0; i < links.length; i++) {
     if (links[i].href)
       is(links[i].href, "http://example.org/first", "bad linkified item");
   }
 });
 addLoadEvent(SimpleTest.finish);
--- a/build/mobile/devicemanagerADB.py
+++ b/build/mobile/devicemanagerADB.py
@@ -444,17 +444,27 @@ class DeviceManagerADB(DeviceManager):
     return "Success"
 
   # external function
   # returns:
   #  success: text status from command or callback server
   #  failure: None
   def updateApp(self, appBundlePath, processName=None, destPath=None, ipAddr=None, port=30000):
     return self.runCmd(["install", "-r", appBundlePath]).stdout.read()
-    
+
+  # external function
+  # returns:
+  #  success: time in ms
+  #  failure: None
+  def getCurrentTime(self):
+    timestr = self.runCmd(["shell", "date", "+%s"]).stdout.read().strip()
+    if (not timestr or not timestr.isdigit()):
+        return None
+    return str(int(timestr)*1000)
+
   # Returns information about the device:
   # Directive indicates the information you want to get, your choices are:
   # os - name of the os
   # id - unique id of the device
   # uptime - uptime of the device
   # systime - system time of the device
   # screen - screen resolution
   # memory - memory stats
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -260,30 +260,35 @@ ContentChild::Init(MessageLoop* aIOLoop,
     XRE_InstallX11ErrorHandler();
 #endif
 
     NS_ASSERTION(!sSingleton, "only one ContentChild per child");
 
     Open(aChannel, aParentHandle, aIOLoop);
     sSingleton = this;
 
-#if defined(ANDROID) && defined(MOZ_CRASHREPORTER)
-    PCrashReporterChild* crashreporter = SendPCrashReporterConstructor();
+#ifdef MOZ_CRASHREPORTER
+    SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(),
+                                  XRE_GetProcessType());
+#if defined(ANDROID)
+    PCrashReporterChild* crashreporter = ManagedPCrashReporterChild()[0];
+
     InfallibleTArray<Mapping> mappings;
     const struct mapping_info *info = getLibraryMapping();
     while (info && info->name) {
         mappings.AppendElement(Mapping(nsDependentCString(info->name),
                                        nsDependentCString(info->file_id),
                                        info->base,
                                        info->len,
                                        info->offset));
         info++;
     }
     crashreporter->SendAddLibraryMappings(mappings);
 #endif
+#endif
 
     return true;
 }
 
 void
 ContentChild::InitXPCOM()
 {
     nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
@@ -413,19 +418,24 @@ bool
 ContentChild::DeallocPBrowser(PBrowserChild* iframe)
 {
     TabChild* child = static_cast<TabChild*>(iframe);
     NS_RELEASE(child);
     return true;
 }
 
 PCrashReporterChild*
-ContentChild::AllocPCrashReporter()
+ContentChild::AllocPCrashReporter(const mozilla::dom::NativeThreadId& id,
+                                  const PRUint32& processType)
 {
+#ifdef MOZ_CRASHREPORTER
     return new CrashReporterChild();
+#else
+    return nsnull;
+#endif
 }
 
 bool
 ContentChild::DeallocPCrashReporter(PCrashReporterChild* crashreporter)
 {
     delete crashreporter;
     return true;
 }
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -75,18 +75,21 @@ public:
     }
 
     /* if you remove this, please talk to cjones or dougt */
     virtual bool RecvDummy(Shmem& foo) { return true; }
 
     virtual PBrowserChild* AllocPBrowser(const PRUint32& aChromeFlags);
     virtual bool DeallocPBrowser(PBrowserChild*);
 
-    virtual PCrashReporterChild* AllocPCrashReporter();
-    virtual bool DeallocPCrashReporter(PCrashReporterChild*);
+    virtual PCrashReporterChild*
+    AllocPCrashReporter(const mozilla::dom::NativeThreadId& id,
+                        const PRUint32& processType);
+    virtual bool
+    DeallocPCrashReporter(PCrashReporterChild*);
 
     virtual PMemoryReportRequestChild*
     AllocPMemoryReportRequest();
 
     virtual bool
     DeallocPMemoryReportRequest(PMemoryReportRequestChild* actor);
 
     virtual bool
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -350,37 +350,24 @@ ContentParent::ActorDestroy(ActorDestroy
     if (obs) {
         nsRefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
         props->Init();
 
         if (AbnormalShutdown == why) {
             props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), PR_TRUE);
 
 #ifdef MOZ_CRASHREPORTER
-            nsAutoString dumpID;
-
-            nsCOMPtr<nsILocalFile> crashDump;
-            TakeMinidump(getter_AddRefs(crashDump)) &&
-                CrashReporter::GetIDFromMinidump(crashDump, dumpID);
-
-            props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID);
+            MOZ_ASSERT(ManagedPCrashReporterParent().Length() > 0);
+            CrashReporterParent* crashReporter =
+                    static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
 
-            if (!dumpID.IsEmpty()) {
-                CrashReporter::AnnotationTable notes;
-                notes.Init();
-                notes.Put(NS_LITERAL_CSTRING("ProcessType"), NS_LITERAL_CSTRING("content"));
-
-                char startTime[32];
-                sprintf(startTime, "%lld", static_cast<long long>(mProcessStartTime));
-                notes.Put(NS_LITERAL_CSTRING("StartupTime"),
-                          nsDependentCString(startTime));
-
-                // TODO: Additional per-process annotations.
-                CrashReporter::AppendExtraData(dumpID, notes);
-            }
+            crashReporter->GenerateCrashReport(this, NULL);
+ 
+            nsAutoString dumpID(crashReporter->ChildDumpID());
+            props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID);
 #endif
 
             obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", nsnull);
         }
     }
 
     MessageLoop::current()->
         PostTask(FROM_HERE,
@@ -410,22 +397,29 @@ ContentParent::CreateTestShell()
 }
 
 bool
 ContentParent::DestroyTestShell(TestShellParent* aTestShell)
 {
     return PTestShellParent::Send__delete__(aTestShell);
 }
 
+TestShellParent*
+ContentParent::GetTestShellSingleton()
+{
+    if (!ManagedPTestShellParent().Length())
+        return nsnull;
+    return static_cast<TestShellParent*>(ManagedPTestShellParent()[0]);
+}
+
 ContentParent::ContentParent()
     : mGeolocationWatchID(-1)
     , mRunToCompletionDepth(0)
     , mShouldCallUnblockChild(false)
     , mIsAlive(true)
-    , mProcessStartTime(time(NULL))
     , mSendPermissionUpdates(false)
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content);
     mSubprocess->AsyncLaunch();
     Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle());
 
     nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
@@ -786,19 +780,33 @@ bool
 ContentParent::DeallocPBrowser(PBrowserParent* frame)
 {
   TabParent* parent = static_cast<TabParent*>(frame);
   NS_RELEASE(parent);
   return true;
 }
 
 PCrashReporterParent*
-ContentParent::AllocPCrashReporter()
+ContentParent::AllocPCrashReporter(const NativeThreadId& tid,
+                                   const PRUint32& processType)
 {
+#ifdef MOZ_CRASHREPORTER
   return new CrashReporterParent();
+#else
+  return nsnull;
+#endif
+}
+
+bool
+ContentParent::RecvPCrashReporterConstructor(PCrashReporterParent* actor,
+                                             const NativeThreadId& tid,
+                                             const PRUint32& processType)
+{
+  static_cast<CrashReporterParent*>(actor)->SetChildData(tid, processType);
+  return true;
 }
 
 bool
 ContentParent::DeallocPCrashReporter(PCrashReporterParent* crashreporter)
 {
   delete crashreporter;
   return true;
 }
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -86,24 +86,29 @@ public:
     NS_DECL_NSITHREADOBSERVER
     NS_DECL_NSIDOMGEOPOSITIONCALLBACK
     NS_DECL_NSIDEVICEMOTIONLISTENER
 
     TabParent* CreateTab(PRUint32 aChromeFlags);
 
     TestShellParent* CreateTestShell();
     bool DestroyTestShell(TestShellParent* aTestShell);
+    TestShellParent* GetTestShellSingleton();
 
     void ReportChildAlreadyBlocked();
     bool RequestRunToCompletion();
 
     bool IsAlive();
 
     void SetChildMemoryReporters(const InfallibleTArray<MemoryReport>& report);
 
+    GeckoChildProcessHost* Process() {
+        return mSubprocess;
+    }
+
     bool NeedsPermissionsUpdate() {
         return mSendPermissionUpdates;
     }
 
 protected:
     void OnChannelConnected(int32 pid);
     virtual void ActorDestroy(ActorDestroyReason why);
 
@@ -118,18 +123,22 @@ private:
     ContentParent();
     virtual ~ContentParent();
 
     void Init();
 
     virtual PBrowserParent* AllocPBrowser(const PRUint32& aChromeFlags);
     virtual bool DeallocPBrowser(PBrowserParent* frame);
 
-    virtual PCrashReporterParent* AllocPCrashReporter();
+    virtual PCrashReporterParent* AllocPCrashReporter(const NativeThreadId& tid,
+                                                      const PRUint32& processType);
     virtual bool DeallocPCrashReporter(PCrashReporterParent* crashreporter);
+    virtual bool RecvPCrashReporterConstructor(PCrashReporterParent* actor,
+                                               const NativeThreadId& tid,
+                                               const PRUint32& processType);
 
     virtual PMemoryReportRequestParent* AllocPMemoryReportRequest();
     virtual bool DeallocPMemoryReportRequest(PMemoryReportRequestParent* actor);
 
     virtual PTestShellParent* AllocPTestShell();
     virtual bool DeallocPTestShell(PTestShellParent* shell);
 
     virtual PAudioParent* AllocPAudio(const PRInt32&,
@@ -225,19 +234,20 @@ private:
     // This is a cache of all of the memory reporters
     // registered in the child process.  To update this, one
     // can broadcast the topic "child-memory-reporter-request" using
     // the nsIObserverService.
     nsCOMArray<nsIMemoryReporter> mMemoryReporters;
 
     bool mIsAlive;
     nsCOMPtr<nsIPrefServiceInternal> mPrefService;
-    time_t mProcessStartTime;
 
     bool mSendPermissionUpdates;
 
     nsRefPtr<nsFrameMessageManager> mMessageManager;
+
+    friend class CrashReporterParent;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/ipc/CrashReporterChild.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: set sw=4 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/
+ *
+ * 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 Crash Reporter.
+ *
+ * The Initial Developer of the Original Code is
+ *   The Mozilla Foundation
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Josh Matthews <josh@joshmatthews.net>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+#include "mozilla/plugins/PluginModuleChild.h"
+#include "ContentChild.h"
+#include "CrashReporterChild.h"
+#include "nsXULAppAPI.h"
+
+using mozilla::plugins::PluginModuleChild;
+
+namespace mozilla {
+namespace dom {
+
+/*static*/
+PCrashReporterChild*
+CrashReporterChild::GetCrashReporter()
+{
+  const InfallibleTArray<PCrashReporterChild*>* reporters = nsnull;
+  switch (XRE_GetProcessType()) {
+    case GeckoProcessType_Content: {
+      ContentChild* child = ContentChild::GetSingleton();
+      reporters = &child->ManagedPCrashReporterChild();
+      break;
+    }
+    case GeckoProcessType_Plugin: {
+      PluginModuleChild* child = PluginModuleChild::current();
+      reporters = &child->ManagedPCrashReporterChild();
+      break;
+    }
+    default:
+      break;
+  }
+  if (reporters && reporters->Length() > 0) {
+    return reporters->ElementAt(0);
+  }
+  return nsnull;
+}
+
+}
+}
--- a/dom/ipc/CrashReporterChild.h
+++ b/dom/ipc/CrashReporterChild.h
@@ -31,25 +31,38 @@
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
  * decision by deleting the provisions above and replace them with the notice
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
+
+#ifndef mozilla_dom_CrashReporterChild_h
+#define mozilla_dom_CrashReporterChild_h
+
 #include "mozilla/dom/PCrashReporterChild.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#include "nsXULAppAPI.h"
+#endif
 
 namespace mozilla {
 namespace dom {
 class CrashReporterChild :
     public PCrashReporterChild
 {
  public:
     CrashReporterChild() {
-      MOZ_COUNT_CTOR(CrashReporterChild);
+        MOZ_COUNT_CTOR(CrashReporterChild);
+    }
+    ~CrashReporterChild() {
+        MOZ_COUNT_DTOR(CrashReporterChild);
     }
-    virtual ~CrashReporterChild() {
-      MOZ_COUNT_DTOR(CrashReporterChild);
-    }
+
+    static PCrashReporterChild* GetCrashReporter();
 };
+
 } // namespace dom
 } // namespace mozilla
+
+#endif // mozilla_dom_CrashReporterChild_h
--- a/dom/ipc/CrashReporterParent.cpp
+++ b/dom/ipc/CrashReporterParent.cpp
@@ -32,22 +32,21 @@
  * 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 "CrashReporterParent.h"
-#if defined(MOZ_CRASHREPORTER)
-#include "nsExceptionHandler.h"
-#endif
 
 #include "base/process_util.h"
 
+#include <time.h>
+
 using namespace base;
 
 namespace mozilla {
 namespace dom {
 
 void
 CrashReporterParent::ActorDestroy(ActorDestroyReason why)
 {
@@ -68,20 +67,105 @@ CrashReporterParent::RecvAddLibraryMappi
                                              m.start_address(),
                                              m.mapping_length(),
                                              m.file_offset());
   }
 #endif
   return true;
 }
 
+bool
+CrashReporterParent::RecvAnnotateCrashReport(const nsCString& key,
+                                             const nsCString& data)
+{
+#ifdef MOZ_CRASHREPORTER
+    mNotes.Put(key, data);
+#endif
+    return true;
+}
+
+bool
+CrashReporterParent::RecvAppendAppNotes(const nsCString& data)
+{
+    mAppNotes.Append(data);
+    return true;
+}
+
 CrashReporterParent::CrashReporterParent()
+: mStartTime(time(NULL))
+, mInitialized(false)
 {
     MOZ_COUNT_CTOR(CrashReporterParent);
+
+#ifdef MOZ_CRASHREPORTER
+    mNotes.Init(4);
+#endif
 }
 
 CrashReporterParent::~CrashReporterParent()
 {
     MOZ_COUNT_DTOR(CrashReporterParent);
 }
 
+void
+CrashReporterParent::SetChildData(const NativeThreadId& tid,
+                                  const PRUint32& processType)
+{
+    mInitialized = true;
+    mMainThread = tid;
+    mProcessType = processType;
+}
+
+#ifdef MOZ_CRASHREPORTER
+bool
+CrashReporterParent::GenerateHangCrashReport(const AnnotationTable* processNotes)
+{
+    if (mChildDumpID.IsEmpty())
+        return false;
+
+    GenerateChildData(processNotes);
+
+    CrashReporter::AnnotationTable notes;
+    if (!notes.Init(4))
+        return false;
+    notes.Put(nsDependentCString("HangID"), NS_ConvertUTF16toUTF8(mHangID));
+    if (!CrashReporter::AppendExtraData(mParentDumpID, notes))
+        NS_WARNING("problem appending parent data to .extra");
+    return true;
+}
+
+bool
+CrashReporterParent::GenerateChildData(const AnnotationTable* processNotes)
+{
+    MOZ_ASSERT(mInitialized);
+
+    nsCAutoString type;
+    switch (mProcessType) {
+        case GeckoProcessType_Content:
+            type = NS_LITERAL_CSTRING("content");
+            break;
+        case GeckoProcessType_Plugin:
+            type = NS_LITERAL_CSTRING("plugin");
+            break;
+        default:
+            NS_ERROR("unknown process type");
+            break;
+    }
+    mNotes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
+
+    char startTime[32];
+    sprintf(startTime, "%lld", static_cast<PRInt64>(mStartTime));
+    mNotes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
+
+    if (!mAppNotes.IsEmpty())
+        mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
+
+    bool ret = CrashReporter::AppendExtraData(mChildDumpID, mNotes);
+    if (ret && processNotes)
+        ret = CrashReporter::AppendExtraData(mChildDumpID, *processNotes);
+    if (!ret)
+        NS_WARNING("problem appending child data to .extra");
+    return ret;
+}
+#endif
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/CrashReporterParent.h
+++ b/dom/ipc/CrashReporterParent.h
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * vim: set sw=4 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/
@@ -32,26 +32,167 @@
  * 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 "mozilla/dom/PCrashReporterParent.h"
+#include "mozilla/dom/TabMessageUtils.h"
+#include "nsXULAppAPI.h"
+#include "nsILocalFile.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
 
 namespace mozilla {
 namespace dom {
+class ProcessReporter;
+
 class CrashReporterParent :
     public PCrashReporterParent
 {
+#ifdef MOZ_CRASHREPORTER
+  typedef CrashReporter::AnnotationTable AnnotationTable;
+#endif
 public:
-    CrashReporterParent();
-    virtual ~CrashReporterParent();
+  CrashReporterParent();
+  virtual ~CrashReporterParent();
+
+#ifdef MOZ_CRASHREPORTER
+  /* Attempt to generate a parent/child pair of minidumps from the given
+     toplevel actor in the event of a hang. Returns true if successful,
+     false otherwise.
+  */
+  template<class Toplevel>
+  bool
+  GeneratePairedMinidump(Toplevel* t);
+
+  /* Attempt to create a bare-bones crash report for a hang, along with extra
+     process-specific annotations present in the given AnnotationTable. Returns
+     true if successful, false otherwise.
+  */
+  bool
+  GenerateHangCrashReport(const AnnotationTable* processNotes);
+
+  /* Attempt to create a bare-bones crash report, along with extra process-
+     specific annotations present in the given AnnotationTable. Returns true if
+     successful, false otherwise.
+  */
+  template<class Toplevel>
+  bool
+  GenerateCrashReport(Toplevel* t, const AnnotationTable* processNotes);
+
+  /* Instantiate a new crash reporter actor from a given parent that manages
+     the protocol.
+  */
+  template<class Toplevel>
+  static void CreateCrashReporter(Toplevel* actor);
+#endif
+  /* Initialize this reporter with data from the child process */
+  void
+    SetChildData(const NativeThreadId& id, const PRUint32& processType);
+
+  /* Returns the shared hang ID of a parent/child paired minidump.
+     GeneratePairedMinidump must be called first.
+  */
+  const nsString& HangID() {
+    return mHangID;
+  }
+  /* Returns the ID of the parent minidump.
+     GeneratePairedMinidump must be called first.
+  */
+  const nsString& ParentDumpID() {
+    return mParentDumpID;
+  }
+  /* Returns the ID of the child minidump.
+     GeneratePairedMinidump or GenerateCrashReport must be called first.
+  */
+  const nsString& ChildDumpID() {
+    return mChildDumpID;
+  }
 
  protected:
   virtual void ActorDestroy(ActorDestroyReason why);
 
   virtual bool
     RecvAddLibraryMappings(const InfallibleTArray<Mapping>& m);
+  virtual bool
+    RecvAnnotateCrashReport(const nsCString& key, const nsCString& data);
+  virtual bool
+    RecvAppendAppNotes(const nsCString& data);
+
+#ifdef MOZ_CRASHREPORTER
+  bool
+  GenerateChildData(const AnnotationTable* processNotes);
+
+  AnnotationTable mNotes;
+#endif
+  nsCString mAppNotes;
+  nsString mHangID;
+  nsString mChildDumpID;
+  nsString mParentDumpID;
+  NativeThreadId mMainThread;
+  time_t mStartTime;
+  PRUint32 mProcessType;
+  bool mInitialized;
 };
+
+#ifdef MOZ_CRASHREPORTER
+template<class Toplevel>
+inline bool
+CrashReporterParent::GeneratePairedMinidump(Toplevel* t)
+{
+  CrashReporter::ProcessHandle child;
+#ifdef XP_MACOSX
+  child = t->Process()->GetChildTask();
+#else
+  child = t->OtherProcess();
+#endif
+  nsCOMPtr<nsILocalFile> childDump;
+  nsCOMPtr<nsILocalFile> parentDump;
+  if (CrashReporter::CreatePairedMinidumps(child,
+                                           mMainThread,
+                                           &mHangID,
+                                           getter_AddRefs(childDump),
+                                           getter_AddRefs(parentDump)) &&
+      CrashReporter::GetIDFromMinidump(childDump, mChildDumpID) &&
+      CrashReporter::GetIDFromMinidump(parentDump, mParentDumpID)) {
+    return true;
+  }
+  return false;
+}
+
+template<class Toplevel>
+inline bool
+CrashReporterParent::GenerateCrashReport(Toplevel* t,
+                                         const AnnotationTable* processNotes)
+{
+  nsCOMPtr<nsILocalFile> crashDump;
+  if (t->TakeMinidump(getter_AddRefs(crashDump)) &&
+      CrashReporter::GetIDFromMinidump(crashDump, mChildDumpID)) {
+    return GenerateChildData(processNotes);
+  }
+  return false;
+}
+
+template<class Toplevel>
+/* static */ void
+CrashReporterParent::CreateCrashReporter(Toplevel* actor)
+{
+#ifdef MOZ_CRASHREPORTER
+  NativeThreadId id;
+  PRUint32 processType;
+  PCrashReporterParent* p =
+      actor->CallPCrashReporterConstructor(&id, &processType);
+  if (p) {
+    static_cast<CrashReporterParent*>(p)->SetChildData(id, processType);
+  } else {
+    NS_ERROR("Error creating crash reporter actor");
+  }
+#endif
+}
+
+#endif
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/ipc/Makefile.in
+++ b/dom/ipc/Makefile.in
@@ -60,40 +60,41 @@ EXPORTS_NAMESPACES = mozilla/dom
 EXPORTS_mozilla/dom = \
   ContentChild.h \
   ContentParent.h \
   ContentProcess.h \
   CrashReporterChild.h \
   CrashReporterParent.h \
   TabParent.h \
   TabChild.h \
+  TabMessageUtils.h \
   $(NULL)
 
 CPPSRCS = \
   ContentProcess.cpp \
   ContentParent.cpp \
   ContentChild.cpp \
   CrashReporterParent.cpp \
+  CrashReporterChild.cpp \
   TabParent.cpp \
   TabChild.cpp \
   TabMessageUtils.cpp \
   $(NULL)
 
 ifdef MOZ_SYDNEYAUDIO
 EXPORTS_mozilla/dom += \
   AudioChild.h \
   AudioParent.h \
   $(NULL)
 CPPSRCS += \
   AudioChild.cpp \
   AudioParent.cpp \
   $(NULL)
 endif
 
-
 include $(topsrcdir)/config/config.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES += \
 	-I$(srcdir)/../../content/base/src \
 	-I$(srcdir)/../../content/events/src \
 	-I$(srcdir)/../../toolkit/components/places \
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -42,29 +42,31 @@ include protocol PCrashReporter;
 include protocol PTestShell;
 include protocol PNecko;
 include protocol PExternalHelperApp;
 include protocol PStorage;
 include protocol PMemoryReportRequest;
 
 include "mozilla/chrome/RegistryMessageUtils.h";
 include "mozilla/net/NeckoMessageUtils.h";
+include "mozilla/dom/TabMessageUtils.h";
 
 include "nsGeoPositionIPCSerialiser.h";
 include "PPrefTuple.h";
 
 using GeoPosition;
 using PrefTuple;
 
 using ChromePackage;
 using ResourceMapping;
 using OverrideMapping;
 using IPC::URI;
 using IPC::Permission;
 using mozilla::null_t;
+using mozilla::dom::NativeThreadId;
 using gfxIntSize;
 
 namespace mozilla {
 namespace dom {
 
 // Data required to clone an existing DOMStorageImpl in the parent
 struct StorageClone
 {
@@ -138,22 +140,23 @@ child:
     
     /**
      * Start accessibility engine in content process.
      */
     ActivateA11y();
 
 parent:
     PNecko();
-    PCrashReporter();
     
     PStorage(StorageConstructData data);
 
     PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat);
 
+    sync PCrashReporter(NativeThreadId tid, PRUint32 processType);
+
     // Services remoting
 
     async StartVisitedQuery(URI uri);
     async VisitURI(URI uri, URI referrer, PRUint32 flags);
     async SetURITitle(URI uri, nsString title);
     
     // filepicker remoting
     sync ShowFilePicker(PRInt16 mode, PRInt16 selectedType, bool addToRecentDocs,
--- a/dom/ipc/PCrashReporter.ipdl
+++ b/dom/ipc/PCrashReporter.ipdl
@@ -33,29 +33,32 @@
  * 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 protocol PContent;
+include protocol PPluginModule;
 
 namespace mozilla {
 namespace dom {
 
 struct Mapping {
   nsCString library_name;
   nsCString file_id;
   uintptr_t start_address;
   size_t mapping_length;
   size_t file_offset;
 };
 
 protocol PCrashReporter {
-  manager PContent;
+  manager PContent or PPluginModule;
 parent:
   AddLibraryMappings(Mapping[] m);
+  AnnotateCrashReport(nsCString key, nsCString data);
+  AppendAppNotes(nsCString data);
   __delete__();
 };
 
 }
-}
\ No newline at end of file
+}
--- a/dom/ipc/TabMessageUtils.h
+++ b/dom/ipc/TabMessageUtils.h
@@ -38,26 +38,37 @@
 
 #ifndef TABMESSAGE_UTILS_H
 #define TABMESSAGE_UTILS_H
 
 #include "IPC/IPCMessageUtils.h"
 #include "nsIPrivateDOMEvent.h"
 #include "nsCOMPtr.h"
 
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
 namespace mozilla {
 namespace dom {
 struct RemoteDOMEvent
 {
   nsCOMPtr<nsIPrivateDOMEvent> mEvent;
 };
 
 bool ReadRemoteEvent(const IPC::Message* aMsg, void** aIter,
                      mozilla::dom::RemoteDOMEvent* aResult);
 
+#ifdef MOZ_CRASHREPORTER
+typedef CrashReporter::ThreadId NativeThreadId;
+#else
+// unused in this case
+typedef int32 NativeThreadId;
+#endif
+
 }
 }
 
 namespace IPC {
 
 template<>
 struct ParamTraits<mozilla::dom::RemoteDOMEvent>
 {
@@ -73,13 +84,12 @@ struct ParamTraits<mozilla::dom::RemoteD
     return mozilla::dom::ReadRemoteEvent(aMsg, aIter, aResult);
   }
 
   static void Log(const paramType& aParam, std::wstring* aLog)
   {
   }
 };
 
-
 }
 
 
 #endif
--- a/dom/plugins/ipc/PPluginModule.ipdl
+++ b/dom/plugins/ipc/PPluginModule.ipdl
@@ -35,34 +35,37 @@
  * 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 protocol PPluginIdentifier;
 include protocol PPluginInstance;
 include protocol PPluginScriptableObject;
+include protocol PCrashReporter;
 
 include "npapi.h";
 include "mozilla/plugins/PluginMessageUtils.h";
+include "mozilla/dom/TabMessageUtils.h";
 
 using NPError;
 using NPNVariable;
 using base::FileDescriptor;
-using mozilla::plugins::NativeThreadId;
+using mozilla::dom::NativeThreadId;
 using mac_plugin_interposing::NSCursorInfo;
 using nsID;
 
 namespace mozilla {
 namespace plugins {
 
 rpc protocol PPluginModule
 {
   manages PPluginInstance;
   manages PPluginIdentifier;
+  manages PCrashReporter;
 
 both:
   /**
    * Sending a void string to this constructor creates an int identifier whereas
    * sending a non-void string will create a string identifier. This constructor
    * may be called by either child or parent. If a race occurs by calling the
    * constructor with the same string or int argument then we create two actors
    * and detect the second instance in the child. We prevent the parent's actor
@@ -79,19 +82,18 @@ both:
   // a nested event loop for the current RPC call.
   async ProcessNativeEventsInRPCCall();
 
 child:
   // Forces the child process to update its plugin function table.
   rpc NP_GetEntryPoints()
     returns (NPError rv);
 
-  // Return the plugin's thread ID, if it can be found.
   rpc NP_Initialize()
-    returns (NativeThreadId tid, NPError rv);
+    returns (NPError rv);
 
   rpc PPluginInstance(nsCString aMimeType,
                       uint16_t aMode,
                       nsCString[] aNames,
                       nsCString[] aValues)
     returns (NPError rv);
 
   rpc NP_Shutdown()
@@ -109,16 +111,19 @@ child:
 
   // Windows specific message to set up an audio session in the plugin process
   async SetAudioSessionData(nsID aID,
                             nsString aDisplayName,
                             nsString aIconPath);
 
   async SetParentHangTimeout(uint32_t seconds);
 
+  rpc PCrashReporter()
+    returns (NativeThreadId tid, PRUint32 processType);
+
 parent:
   /**
    * This message is only used on X11 platforms.
    *
    * Send a dup of the plugin process's X socket to the parent
    * process.  In theory, this scheme keeps the plugin's X resources
    * around until after both the plugin process shuts down *and* the
    * parent process closes the dup fd.  This is used to prevent the
@@ -136,18 +141,16 @@ parent:
              bool aBoolVal);
 
   // Wake up and process a few native events.  Periodically called by
   // Gtk-specific code upon detecting that the plugin process has
   // entered a nested event loop.  If the browser doesn't process
   // native events, then "livelock" and some other glitches can occur.
   rpc ProcessSomeEvents();
 
-  sync AppendNotesToCrashReport(nsCString aNotes);
-
   // OS X Specific calls to manage the plugin's window
   // when interposing system calls.
   async PluginShowWindow(uint32_t aWindowId, bool aModal,
                          int32_t aX, int32_t aY,
                          size_t aWidth, size_t aHeight);
   async PluginHideWindow(uint32_t aWindowId);
 
   // OS X Specific calls to allow the plugin to manage the cursor.
--- a/dom/plugins/ipc/PluginMessageUtils.h
+++ b/dom/plugins/ipc/PluginMessageUtils.h
@@ -137,23 +137,16 @@ typedef intptr_t NativeWindowHandle; // 
 #endif
 
 #ifdef XP_WIN
 typedef base::SharedMemoryHandle WindowsSharedMemoryHandle;
 #else
 typedef mozilla::null_t WindowsSharedMemoryHandle;
 #endif
 
-#ifdef MOZ_CRASHREPORTER
-typedef CrashReporter::ThreadId NativeThreadId;
-#else
-// unused in this case
-typedef int32 NativeThreadId;
-#endif
-
 // XXX maybe not the best place for these. better one?
 
 #define VARSTR(v_)  case v_: return #v_
 inline const char* const
 NPPVariableToString(NPPVariable aVar)
 {
     switch (aVar) {
         VARSTR(NPPVpluginNameString);
--- a/dom/plugins/ipc/PluginModuleChild.cpp
+++ b/dom/plugins/ipc/PluginModuleChild.cpp
@@ -61,31 +61,34 @@
 #ifdef MOZ_X11
 # include "mozilla/X11Util.h"
 #endif
 #include "mozilla/plugins/PluginInstanceChild.h"
 #include "mozilla/plugins/StreamNotifyChild.h"
 #include "mozilla/plugins/BrowserStreamChild.h"
 #include "mozilla/plugins/PluginStreamChild.h"
 #include "PluginIdentifierChild.h"
+#include "mozilla/dom/CrashReporterChild.h"
 
 #include "nsNPAPIPlugin.h"
 
 #ifdef XP_WIN
 #include "COMMessageFilter.h"
 #include "nsWindowsDllInterceptor.h"
 #include "mozilla/widget/AudioSession.h"
 #endif
 
 #ifdef MOZ_WIDGET_COCOA
 #include "PluginInterposeOSX.h"
 #include "PluginUtilsOSX.h"
 #endif
 
 using namespace mozilla::plugins;
+using mozilla::dom::CrashReporterChild;
+using mozilla::dom::PCrashReporterChild;
 
 #if defined(XP_WIN)
 const PRUnichar * kFlashFullscreenClass = L"ShockwaveFlashFullScreen";
 const PRUnichar * kMozillaWindowClass = L"MozillaWindowClass";
 #endif
 
 namespace {
 PluginModuleChild* gInstance = nsnull;
@@ -587,17 +590,16 @@ PluginModuleChild::InitGraphics()
     }
 #else
     // may not be necessary on all platforms
 #endif
 #ifdef MOZ_X11
     // Do this after initializing GDK, or GDK will install its own handler.
     XRE_InstallX11ErrorHandler();
 #endif
-
     return true;
 }
 
 void
 PluginModuleChild::DeinitGraphics()
 {
 #ifdef MOZ_WIDGET_QT
     nsQAppInstance::Release();
@@ -700,16 +702,43 @@ PluginModuleChild::RecvSetAudioSessionDa
 
 void
 PluginModuleChild::QuickExit()
 {
     NS_WARNING("plugin process _exit()ing");
     _exit(0);
 }
 
+PCrashReporterChild*
+PluginModuleChild::AllocPCrashReporter(mozilla::dom::NativeThreadId* id,
+                                       PRUint32* processType)
+{
+    return new CrashReporterChild();
+}
+
+bool
+PluginModuleChild::DeallocPCrashReporter(PCrashReporterChild* actor)
+{
+    delete actor;
+    return true;
+}
+
+bool
+PluginModuleChild::AnswerPCrashReporterConstructor(
+        PCrashReporterChild* actor,
+        mozilla::dom::NativeThreadId* id,
+        PRUint32* processType)
+{
+#ifdef MOZ_CRASHREPORTER
+    *id = CrashReporter::CurrentThreadId();
+    *processType = XRE_GetProcessType();
+#endif
+    return true;
+}
+
 void
 PluginModuleChild::ActorDestroy(ActorDestroyReason why)
 {
     if (AbnormalShutdown == why) {
         NS_WARNING("shutting down early because of crash!");
         QuickExit();
     }
 
@@ -1796,27 +1825,21 @@ PluginModuleChild::AnswerNP_GetEntryPoin
     *_retval = mGetEntryPointsFunc(&mFunctions);
     return true;
 #else
 #  error Please implement me for your platform
 #endif
 }
 
 bool
-PluginModuleChild::AnswerNP_Initialize(NativeThreadId* tid, NPError* _retval)
+PluginModuleChild::AnswerNP_Initialize(NPError* _retval)
 {
     PLUGIN_LOG_DEBUG_METHOD;
     AssertPluginThread();
 
-#ifdef MOZ_CRASHREPORTER
-    *tid = CrashReporter::CurrentThreadId();
-#else
-    *tid = 0;
-#endif
-
 #ifdef OS_WIN
     SetEventHooks();
 #endif
 
 #ifdef MOZ_X11
     // Send the parent a dup of our X socket, to act as a proxy
     // reference for our X resources
     int xSocketFd = ConnectionNumber(DefaultXDisplay());
--- a/dom/plugins/ipc/PluginModuleChild.h
+++ b/dom/plugins/ipc/PluginModuleChild.h
@@ -86,42 +86,47 @@
 #endif
 
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_GETENTRYPOINTS) (NPPluginFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGININIT) (const NPNetscapeFuncs* pCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINUNIXINIT) (const NPNetscapeFuncs* pCallbacks, NPPluginFuncs* fCallbacks);
 typedef NS_NPAPIPLUGIN_CALLBACK(NPError, NP_PLUGINSHUTDOWN) (void);
 
 namespace mozilla {
+namespace dom {
+class PCrashReporterChild;
+}
+
 namespace plugins {
 
 #ifdef MOZ_WIDGET_QT
 class NestedLoopTimer;
 static const int kNestedLoopDetectorIntervalMs = 90;
 #endif
 
 class PluginScriptableObjectChild;
 class PluginInstanceChild;
 
 class PluginModuleChild : public PPluginModuleChild
 {
+    typedef mozilla::dom::PCrashReporterChild PCrashReporterChild;
 protected:
     NS_OVERRIDE
     virtual mozilla::ipc::RPCChannel::RacyRPCPolicy
     MediateRPCRace(const Message& parent, const Message& child)
     {
         return MediateRace(parent, child);
     }
 
     NS_OVERRIDE
     virtual bool ShouldContinueFromReplyTimeout();
 
     // Implement the PPluginModuleChild interface
     virtual bool AnswerNP_GetEntryPoints(NPError* rv);
-    virtual bool AnswerNP_Initialize(NativeThreadId* tid, NPError* rv);
+    virtual bool AnswerNP_Initialize(NPError* rv);
 
     virtual PPluginIdentifierChild*
     AllocPPluginIdentifier(const nsCString& aString,
                            const int32_t& aInt,
                            const bool& aTemporary);
 
     virtual bool
     RecvPPluginIdentifierConstructor(PPluginIdentifierChild* actor,
@@ -169,16 +174,26 @@ protected:
     virtual bool
     RecvSetAudioSessionData(const nsID& aId,
                             const nsString& aDisplayName,
                             const nsString& aIconPath);
 
     virtual bool
     RecvSetParentHangTimeout(const uint32_t& aSeconds);
 
+    virtual PCrashReporterChild*
+    AllocPCrashReporter(mozilla::dom::NativeThreadId* id,
+                        PRUint32* processType);
+    virtual bool
+    DeallocPCrashReporter(PCrashReporterChild* actor);
+    virtual bool
+    AnswerPCrashReporterConstructor(PCrashReporterChild* actor,
+                                    mozilla::dom::NativeThreadId* id,
+                                    PRUint32* processType);
+
     virtual void
     ActorDestroy(ActorDestroyReason why);
 
     NS_NORETURN void QuickExit();
 
     NS_OVERRIDE virtual bool
     RecvProcessNativeEventsInRPCCall();
 
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -51,34 +51,37 @@
 
 #include "base/process_util.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/unused.h"
 #include "mozilla/ipc/SyncChannel.h"
 #include "mozilla/plugins/PluginModuleParent.h"
 #include "mozilla/plugins/BrowserStreamParent.h"
+#include "mozilla/dom/PCrashReporterParent.h"
 #include "PluginIdentifierParent.h"
 
 #include "nsAutoPtr.h"
 #include "nsCRT.h"
 #ifdef MOZ_CRASHREPORTER
-#include "nsExceptionHandler.h"
+#include "mozilla/dom/CrashReporterParent.h"
 #endif
 #include "nsNPAPIPlugin.h"
 #include "nsILocalFile.h"
 
 #ifdef XP_WIN
 #include "mozilla/widget/AudioSession.h"
 #endif
 
 using base::KillProcess;
 
 using mozilla::PluginLibrary;
 using mozilla::ipc::SyncChannel;
+using mozilla::dom::PCrashReporterParent;
+using mozilla::dom::CrashReporterParent;
 
 using namespace mozilla;
 using namespace mozilla::plugins;
 using namespace mozilla::plugins::parent;
 
 static const char kChildTimeoutPref[] = "dom.ipc.plugins.timeoutSecs";
 static const char kParentTimeoutPref[] = "dom.ipc.plugins.parentTimeoutSecs";
 static const char kLaunchTimeoutPref[] = "dom.ipc.plugins.processLaunchTimeoutSecs";
@@ -106,29 +109,32 @@ PluginModuleParent::LoadModule(const cha
         // Need to set this so the destructor doesn't complain.
         parent->mShutdown = true;
         return nsnull;
     }
     parent->Open(parent->mSubprocess->GetChannel(),
                  parent->mSubprocess->GetChildProcessHandle());
 
     TimeoutChanged(kChildTimeoutPref, parent);
+
+#ifdef MOZ_CRASHREPORTER
+    CrashReporterParent::CreateCrashReporter(parent.get());
+#endif
+
     return parent.forget();
 }
 
 
 PluginModuleParent::PluginModuleParent(const char* aFilePath)
     : mSubprocess(new PluginProcessParent(aFilePath))
-    , mPluginThread(0)
     , mShutdown(false)
     , mClearSiteDataSupported(false)
     , mGetSitesWithDataSupported(false)
     , mNPNIface(NULL)
     , mPlugin(NULL)
-    , mProcessStartTime(time(NULL))
     , mTaskFactory(this)
 {
     NS_ASSERTION(mSubprocess, "Out of memory!");
 
     if (!mIdentifiers.Init()) {
         NS_ERROR("Out of memory");
     }
 
@@ -159,77 +165,40 @@ PluginModuleParent::~PluginModuleParent(
     }
 
     Preferences::UnregisterCallback(TimeoutChanged, kChildTimeoutPref, this);
     Preferences::UnregisterCallback(TimeoutChanged, kParentTimeoutPref, this);
 }
 
 #ifdef MOZ_CRASHREPORTER
 void
-PluginModuleParent::WritePluginExtraDataForMinidump(const nsAString& id)
+PluginModuleParent::WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes)
 {
     typedef nsDependentCString CS;
 
-    CrashReporter::AnnotationTable notes;
-    if (!notes.Init(32))
-        return;
-
-    notes.Put(CS("ProcessType"), CS("plugin"));
-
-    char startTime[32];
-    sprintf(startTime, "%lld", static_cast<PRInt64>(mProcessStartTime));
-    notes.Put(CS("StartupTime"), CS(startTime));
-
     // Get the plugin filename, try to get just the file leafname
     const std::string& pluginFile = mSubprocess->GetPluginFilePath();
     size_t filePos = pluginFile.rfind(FILE_PATH_SEPARATOR);
     if (filePos == std::string::npos)
         filePos = 0;
     else
         filePos++;
     notes.Put(CS("PluginFilename"), CS(pluginFile.substr(filePos).c_str()));
 
     //TODO: add plugin name and version: bug 539841
     // (as PluginName, PluginVersion)
     notes.Put(CS("PluginName"), CS(""));
     notes.Put(CS("PluginVersion"), CS(""));
 
-    if (!mCrashNotes.IsEmpty())
-        notes.Put(CS("Notes"), CS(mCrashNotes.get()));
-
-    if (!mHangID.IsEmpty())
-        notes.Put(CS("HangID"), NS_ConvertUTF16toUTF8(mHangID));
-
-    if (!CrashReporter::AppendExtraData(id, notes))
-        NS_WARNING("problem appending plugin data to .extra");
-}
-
-void
-PluginModuleParent::WriteExtraDataForHang()
-{
-    // this writes HangID
-    WritePluginExtraDataForMinidump(mPluginDumpID);
-
-    CrashReporter::AnnotationTable notes;
-    if (!notes.Init(4))
-        return;
-
-    notes.Put(nsDependentCString("HangID"), NS_ConvertUTF16toUTF8(mHangID));
-    if (!CrashReporter::AppendExtraData(mBrowserDumpID, notes))
-        NS_WARNING("problem appending browser data to .extra");
+    const nsString& hangID = CrashReporter()->HangID();
+    if (!hangID.IsEmpty())
+        notes.Put(CS("HangID"), NS_ConvertUTF16toUTF8(hangID));
 }
 #endif  // MOZ_CRASHREPORTER
 
-bool
-PluginModuleParent::RecvAppendNotesToCrashReport(const nsCString& aNotes)
-{
-    mCrashNotes.Append(aNotes);
-    return true;
-}
-
 int
 PluginModuleParent::TimeoutChanged(const char* aPref, void* aModule)
 {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thead!");
     if (!strcmp(aPref, kChildTimeoutPref)) {
       // The timeout value used by the parent for children
       PRInt32 timeoutSecs = Preferences::GetInt(kChildTimeoutPref, 0);
       int32 timeoutMs = (timeoutSecs > 0) ? (1000 * timeoutSecs) :
@@ -249,39 +218,26 @@ PluginModuleParent::CleanupFromTimeout()
     if (!mShutdown)
         Close();
 }
 
 bool
 PluginModuleParent::ShouldContinueFromReplyTimeout()
 {
 #ifdef MOZ_CRASHREPORTER
-    nsCOMPtr<nsILocalFile> pluginDump;
-    nsCOMPtr<nsILocalFile> browserDump;
-    CrashReporter::ProcessHandle child;
-#ifdef XP_MACOSX
-    child = mSubprocess->GetChildTask();
-#else
-    child = OtherProcess();
-#endif
-    if (CrashReporter::CreatePairedMinidumps(child,
-                                             mPluginThread,
-                                             &mHangID,
-                                             getter_AddRefs(pluginDump),
-                                             getter_AddRefs(browserDump)) &&
-        CrashReporter::GetIDFromMinidump(pluginDump, mPluginDumpID) &&
-        CrashReporter::GetIDFromMinidump(browserDump, mBrowserDumpID)) {
-
+    CrashReporterParent* crashReporter = CrashReporter();
+    if (crashReporter->GeneratePairedMinidump(this)) {
+        mBrowserDumpID = crashReporter->ParentDumpID();
+        mPluginDumpID = crashReporter->ChildDumpID();
         PLUGIN_LOG_DEBUG(
-            ("generated paired browser/plugin minidumps: %s/%s (ID=%s)",
-             NS_ConvertUTF16toUTF8(mBrowserDumpID).get(),
-             NS_ConvertUTF16toUTF8(mPluginDumpID).get(),
-             NS_ConvertUTF16toUTF8(mHangID).get()));
-    }
-    else {
+                ("generated paired browser/plugin minidumps: %s/%s (ID=%s)",
+                 NS_ConvertUTF16toUTF8(mBrowserDumpID).get(),
+                 NS_ConvertUTF16toUTF8(mPluginDumpID).get(),
+                 NS_ConvertUTF16toUTF8(crashReporter->HangID()).get()));
+    } else {
         NS_WARNING("failed to capture paired minidumps from hang");
     }
 #endif
 
     // this must run before the error notification from the channel,
     // or not at all
     MessageLoop::current()->PostTask(
         FROM_HERE,
@@ -289,31 +245,44 @@ PluginModuleParent::ShouldContinueFromRe
             &PluginModuleParent::CleanupFromTimeout));
 
     if (!KillProcess(OtherProcess(), 1, false))
         NS_WARNING("failed to kill subprocess!");
 
     return false;
 }
 
+#ifdef MOZ_CRASHREPORTER
+CrashReporterParent*
+PluginModuleParent::CrashReporter()
+{
+    MOZ_ASSERT(ManagedPCrashReporterParent().Length() > 0);
+    return static_cast<CrashReporterParent*>(ManagedPCrashReporterParent()[0]);
+}
+#endif
+
 void
 PluginModuleParent::ActorDestroy(ActorDestroyReason why)
 {
     switch (why) {
     case AbnormalShutdown: {
 #ifdef MOZ_CRASHREPORTER
-        nsCOMPtr<nsILocalFile> pluginDump;
-        if (TakeMinidump(getter_AddRefs(pluginDump)) &&
-            CrashReporter::GetIDFromMinidump(pluginDump, mPluginDumpID)) {
+        CrashReporterParent* crashReporter = CrashReporter();
+
+        CrashReporter::AnnotationTable notes;
+        notes.Init(4);
+        WriteExtraDataForMinidump(notes);
+        
+        if (crashReporter->GenerateCrashReport(this, &notes)) {
+            mPluginDumpID = crashReporter->ChildDumpID();
             PLUGIN_LOG_DEBUG(("got child minidump: %s",
                               NS_ConvertUTF16toUTF8(mPluginDumpID).get()));
-            WritePluginExtraDataForMinidump(mPluginDumpID);
         }
         else if (!mPluginDumpID.IsEmpty() && !mBrowserDumpID.IsEmpty()) {
-            WriteExtraDataForHang();
+            crashReporter->GenerateHangCrashReport(&notes);
         }
         else {
             NS_WARNING("[PluginModuleParent::ActorDestroy] abnormal shutdown without minidump!");
         }
 #endif
 
         mShutdown = true;
         // Defer the PluginCrashed method so that we don't re-enter
@@ -776,17 +745,17 @@ PluginModuleParent::NP_Initialize(NPNets
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
-    if (!CallNP_Initialize(&mPluginThread, error)) {
+    if (!CallNP_Initialize(error)) {
         return NS_ERROR_FAILURE;
     }
     else if (*error != NPERR_NO_ERROR) {
         return NS_OK;
     }
 
     SetPluginFuncs(pFuncs);
 
@@ -800,17 +769,17 @@ PluginModuleParent::NP_Initialize(NPNets
 
     mNPNIface = bFuncs;
 
     if (mShutdown) {
         *error = NPERR_GENERIC_ERROR;
         return NS_ERROR_FAILURE;
     }
 
-    if (!CallNP_Initialize(&mPluginThread, error))
+    if (!CallNP_Initialize(error))
         return NS_ERROR_FAILURE;
 
 #if defined XP_WIN && MOZ_WINSDK_TARGETVER >= MOZ_NTDDI_LONGHORN
     // Send the info needed to join the chrome process's audio session to the
     // plugin process
     nsID id;
     nsString sessionName;
     nsString iconPath;
@@ -1101,16 +1070,34 @@ PluginModuleParent::RecvPluginHideWindow
     return true;
 #else
     NS_NOTREACHED(
         "PluginInstanceParent::RecvPluginHideWindow not implemented!");
     return false;
 #endif
 }
 
+PCrashReporterParent*
+PluginModuleParent::AllocPCrashReporter(mozilla::dom::NativeThreadId* id,
+                                        PRUint32* processType)
+{
+#ifdef MOZ_CRASHREPORTER
+    return new CrashReporterParent();
+#else
+    return nsnull;
+#endif
+}
+
+bool
+PluginModuleParent::DeallocPCrashReporter(PCrashReporterParent* actor)
+{
+    delete actor;
+    return true;
+}
+
 bool
 PluginModuleParent::RecvSetCursor(const NSCursorInfo& aCursorInfo)
 {
     PLUGIN_LOG_DEBUG(("%s", FULLFUNCTION));
 #if defined(XP_MACOSX)
     mac_plugin_interposing::parent::OnSetCursor(aCursorInfo);
     return true;
 #else
--- a/dom/plugins/ipc/PluginModuleParent.h
+++ b/dom/plugins/ipc/PluginModuleParent.h
@@ -60,16 +60,21 @@
 #include "nsAutoPtr.h"
 #include "nsDataHashtable.h"
 #include "nsHashKeys.h"
 #include "nsIFileStreams.h"
 #include "nsTObserverArray.h"
 #include "nsITimer.h"
 
 namespace mozilla {
+namespace dom {
+class PCrashReporterParent;
+class CrashReporterParent;
+}
+
 namespace plugins {
 //-----------------------------------------------------------------------------
 
 class BrowserStreamParent;
 
 /**
  * PluginModuleParent
  *
@@ -80,16 +85,18 @@ class BrowserStreamParent;
  * This class /also/ implements a version of the NPN API, because the
  * child process needs to make these calls back into Gecko proper.
  * This class is responsible for "actually" making those function calls.
  */
 class PluginModuleParent : public PPluginModuleParent, PluginLibrary
 {
 private:
     typedef mozilla::PluginLibrary PluginLibrary;
+    typedef mozilla::dom::PCrashReporterParent PCrashReporterParent;
+    typedef mozilla::dom::CrashReporterParent CrashReporterParent;
 
 protected:
 
     virtual PPluginIdentifierParent*
     AllocPPluginIdentifier(const nsCString& aString,
                            const int32_t& aInt,
                            const bool& aTemporary);
 
@@ -179,27 +186,30 @@ protected:
                                       bool* aBoolVal);
 
     NS_OVERRIDE
     virtual bool AnswerProcessSomeEvents();
 
     NS_OVERRIDE virtual bool
     RecvProcessNativeEventsInRPCCall();
 
-    virtual bool
-    RecvAppendNotesToCrashReport(const nsCString& aNotes);
-
     NS_OVERRIDE virtual bool
     RecvPluginShowWindow(const uint32_t& aWindowId, const bool& aModal,
                          const int32_t& aX, const int32_t& aY,
                          const size_t& aWidth, const size_t& aHeight);
 
     NS_OVERRIDE virtual bool
     RecvPluginHideWindow(const uint32_t& aWindowId);
 
+    NS_OVERRIDE virtual PCrashReporterParent*
+    AllocPCrashReporter(mozilla::dom::NativeThreadId* id,
+                        PRUint32* processType);
+    NS_OVERRIDE virtual bool
+    DeallocPCrashReporter(PCrashReporterParent* actor);
+
     NS_OVERRIDE virtual bool
     RecvSetCursor(const NSCursorInfo& aCursorInfo);
 
     NS_OVERRIDE virtual bool
     RecvShowCursor(const bool& aShow);
 
     NS_OVERRIDE virtual bool
     RecvPushCursor(const NSCursorInfo& aCursorInfo);
@@ -297,46 +307,49 @@ private:
     virtual nsresult IsRemoteDrawingCoreAnimation(NPP instance, bool *aDrawing);
 #endif
 #if defined(MOZ_WIDGET_QT) && (MOZ_PLATFORM_MAEMO == 6)
     virtual nsresult HandleGUIEvent(NPP instance, const nsGUIEvent& anEvent,
                                     bool* handled);
 #endif
 
 private:
-    void WritePluginExtraDataForMinidump(const nsAString& id);
-    void WriteExtraDataForHang();
+    CrashReporterParent* CrashReporter();
+
+#ifdef MOZ_CRASHREPORTER
+    void WriteExtraDataForMinidump(CrashReporter::AnnotationTable& notes);
+#endif
     void CleanupFromTimeout();
     static int TimeoutChanged(const char* aPref, void* aModule);
     void NotifyPluginCrashed();
 
-    nsCString mCrashNotes;
     PluginProcessParent* mSubprocess;
     // the plugin thread in mSubprocess
     NativeThreadId mPluginThread;
     bool mShutdown;
     bool mClearSiteDataSupported;
     bool mGetSitesWithDataSupported;
     const NPNetscapeFuncs* mNPNIface;
     nsDataHashtable<nsVoidPtrHashKey, PluginIdentifierParent*> mIdentifiers;
     nsNPAPIPlugin* mPlugin;
-    time_t mProcessStartTime;
     ScopedRunnableMethodFactory<PluginModuleParent> mTaskFactory;
     nsString mPluginDumpID;
     nsString mBrowserDumpID;
     nsString mHangID;
 
 #ifdef OS_MACOSX
     nsCOMPtr<nsITimer> mCATimer;
     nsTObserverArray<PluginInstanceParent*> mCATimerTargets;
 #endif
 
 #ifdef MOZ_X11
     // Dup of plugin's X socket, used to scope its resources to this
     // object instead of the plugin process's lifetime
     ScopedClose mPluginXSocketFdDup;
 #endif
+
+    friend class mozilla::dom::CrashReporterParent;
 };
 
 } // namespace plugins
 } // namespace mozilla
 
 #endif  // ifndef dom_plugins_PluginModuleParent_h
--- a/dom/plugins/ipc/PluginProcessChild.cpp
+++ b/dom/plugins/ipc/PluginProcessChild.cpp
@@ -170,22 +170,10 @@ void
 PluginProcessChild::CleanUp()
 {
 #ifdef XP_WIN
     ::OleUninitialize();
 #endif
     nsRegion::ShutdownStatic();
 }
 
-/* static */
-void
-PluginProcessChild::AppendNotesToCrashReport(const nsCString& aNotes)
-{
-    AssertPluginThread();
-
-    PluginProcessChild* p = PluginProcessChild::current();
-    if (p) {
-        p->mPlugin.SendAppendNotesToCrashReport(aNotes);
-    }
-}
-
 } // namespace plugins
 } // namespace mozilla
--- a/dom/plugins/ipc/PluginProcessChild.h
+++ b/dom/plugins/ipc/PluginProcessChild.h
@@ -56,19 +56,16 @@ public:
     { }
 
     virtual ~PluginProcessChild()
     { }
 
     NS_OVERRIDE virtual bool Init();
     NS_OVERRIDE virtual void CleanUp();
 
-    // For use on the plugin thread.
-    static void AppendNotesToCrashReport(const nsCString& aNotes);
-
 protected:
     static PluginProcessChild* current() {
         return static_cast<PluginProcessChild*>(ProcessChild::current());
     }
 
 private:
     PluginModuleChild mPlugin;
 
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -94,26 +94,28 @@
             <intent-filter>
                 <action android:name="org.mozilla.gecko.ACTION_ALERT_CLICK" />
                 <action android:name="org.mozilla.gecko.ACTION_ALERT_CLEAR" />
             </intent-filter>
         </receiver>
 
         <activity android:name="Restarter"
                   android:process="@ANDROID_PACKAGE_NAME@Restarter"
-                  android:theme="@style/GreyTheme">
+                  android:theme="@style/GreyTheme"
+                  android:excludeFromRecents="true">
           <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" >
+  <activity android:name="CrashReporter"
+            android:label="@string/crash_reporter_title"
+            android:icon="@drawable/crash_reporter"
+            android:excludeFromRecents="true">
           <intent-filter>
             <action android:name="org.mozilla.gecko.reportCrash" />
           </intent-filter>
 	</activity>
 #endif
 
         <activity android:name="LauncherShortcuts"
                   android:label="@string/launcher_shortcuts_title">
--- a/gfx/thebes/GLContextProviderEGL.cpp
+++ b/gfx/thebes/GLContextProviderEGL.cpp
@@ -1672,17 +1672,17 @@ protected:
 };
 
 already_AddRefed<TextureImage>
 GLContextEGL::CreateTextureImage(const nsIntSize& aSize,
                                  TextureImage::ContentType aContentType,
                                  GLenum aWrapMode,
                                  bool aUseNearestFilter)
 {
-    nsRefPtr<TextureImage> t = new gl::TiledTextureImage(this, aSize, aContentType);
+    nsRefPtr<TextureImage> t = new gl::TiledTextureImage(this, aSize, aContentType, aUseNearestFilter);
     return t.forget();
 };
 
 already_AddRefed<TextureImage>
 GLContextEGL::TileGenFunc(const nsIntSize& aSize,
                                  TextureImage::ContentType aContentType,
                                  bool aUseNearestFilter)
 {
--- a/ipc/testshell/TestShellParent.cpp
+++ b/ipc/testshell/TestShellParent.cpp
@@ -141,8 +141,23 @@ TestShellCommandParent::RunCallback(cons
   return JS_TRUE;
 }
 
 void
 TestShellCommandParent::ReleaseCallback()
 {
   mCallback.Release();
 }
+
+bool
+TestShellCommandParent::ExecuteCallback(const nsString& aResponse)
+{
+  return static_cast<TestShellParent*>(Manager())->CommandDone(
+      this, aResponse);
+}
+
+void
+TestShellCommandParent::ActorDestroy(ActorDestroyReason why)
+{
+  if (why == AbnormalShutdown) {
+    ExecuteCallback(EmptyString());
+  }
+}
--- a/ipc/testshell/TestShellParent.h
+++ b/ipc/testshell/TestShellParent.h
@@ -87,19 +87,22 @@ public:
   JSBool SetCallback(JSContext* aCx,
                      jsval aCallback);
 
   JSBool RunCallback(const nsString& aResponse);
 
   void ReleaseCallback();
 
 protected:
+  bool ExecuteCallback(const nsString& aResponse);
+
+  void ActorDestroy(ActorDestroyReason why);
+  
   bool Recv__delete__(const nsString& aResponse) {
-    return static_cast<TestShellParent*>(Manager())->CommandDone(
-      this, aResponse);
+    return ExecuteCallback(aResponse);
   }
 
 private:
   JSContext* mCx;
   nsAutoJSValHolder mCallback;
 };
 
 
--- a/js/src/xpconnect/src/xpcquickstubs.cpp
+++ b/js/src/xpconnect/src/xpcquickstubs.cpp
@@ -721,18 +721,20 @@ xpc_qsDOMString::xpc_qsDOMString(JSConte
     // From the T_DOMSTRING case in XPCConvert::JSData2Native.
     JSString *s = InitOrStringify<traits>(cx, v, pval, nullBehavior,
                                           undefinedBehavior);
     if (!s)
         return;
 
     size_t len;
     const jschar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
-    if (!chars)
+    if (!chars) {
+        mValid = JS_FALSE;
         return;
+    }
 
     new(mBuf) implementation_type(chars, len);
     mValid = JS_TRUE;
 }
 
 xpc_qsACString::xpc_qsACString(JSContext *cx, jsval v, jsval *pval,
                                StringificationBehavior nullBehavior,
                                StringificationBehavior undefinedBehavior)
@@ -767,18 +769,20 @@ xpc_qsAUTF8String::xpc_qsAUTF8String(JSC
     typedef nsCharTraits<PRUnichar> traits;
     // From the T_UTF8STRING  case in XPCConvert::JSData2Native.
     JSString *s = InitOrStringify<traits>(cx, v, pval, eNull, eNull);
     if (!s)
         return;
 
     size_t len;
     const PRUnichar *chars = JS_GetStringCharsZAndLength(cx, s, &len);
-    if (!chars)
+    if (!chars) {
+        mValid = JS_FALSE;
         return;
+    }
 
     new(mBuf) implementation_type(chars, len);
     mValid = JS_TRUE;
 }
 
 static nsresult
 getNative(nsISupports *idobj,
           QITableEntry* entries,
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -936,17 +936,19 @@ function StartCurrentURI(aState)
     if (gURICanvases[gCurrentURL] &&
         (gURLs[0].type == TYPE_REFTEST_EQUAL ||
          gURLs[0].type == TYPE_REFTEST_NOTEQUAL) &&
         gURLs[0].maxAsserts == 0) {
         // Pretend the document loaded --- RecordResult will notice
         // there's already a canvas for this URL
         setTimeout(RecordResult, 0);
     } else {
-        gDumpLog("REFTEST TEST-START | " + gCurrentURL + "\n");
+        var currentTest = gTotalTests - gURLs.length;
+        gDumpLog("REFTEST TEST-START | " + gCurrentURL + " | " + currentTest + " / " + gTotalTests +
+            " (" + Math.floor(100 * (currentTest / gTotalTests)) + "%)\n");
         LogInfo("START " + gCurrentURL);
         var type = gURLs[0].type
         if (TYPE_SCRIPT == type) {
             SendLoadScriptTest(gCurrentURL, gLoadTimeout);
         } else {
             SendLoadTest(type, gCurrentURL, gLoadTimeout);
         }
     }
--- a/mobile/chrome/content/TabsPopup.js
+++ b/mobile/chrome/content/TabsPopup.js
@@ -36,16 +36,19 @@
  * ***** END LICENSE BLOCK ***** */
 
 var TabsPopup = {
   init: function() {
     Elements.tabs.addEventListener("TabOpen", this, true);
     Elements.tabs.addEventListener("TabRemove", this, true);
 
     this._updateTabsCount();
+
+    // Bind resizeHandler so we can pass it to addEventListener/removeEventListener.
+    this.resizeHandler = this.resizeHandler.bind(this);
   },
 
   get box() {
     delete this.box;
     return this.box = document.getElementById("tabs-popup-container");
   },
 
   get list() {
@@ -112,22 +115,17 @@ var TabsPopup = {
       item.tab = aTab;
     }, this)
 
     // Set the box position.
     this.box.hidden = false;
     this.box.anchorTo(this.button, "after_end");
     BrowserUI.pushPopup(this, [this.box, this.button]);
 
-    window.addEventListener("resize", function resizeHandler(aEvent) {
-      if (aEvent.target != window)
-        return;
-      if (!Util.isPortrait())
-        TabsPopup._hidePortraitMenu();
-    }, false);
+    window.addEventListener("resize", this.resizeHandler, false);
   },
 
   toggle: function toggle() {
     if (this.visible)
       this.hide();
     else
       this.show();
   },
@@ -135,17 +133,17 @@ var TabsPopup = {
   get visible() {
     return Util.isPortrait() ? !this.box.hidden : Elements.urlbarState.hasAttribute("tablet_sidebar");
   },
 
   _hidePortraitMenu: function _hidePortraitMenu() {
     if (!this.box.hidden) {
       this.box.hidden = true;
       BrowserUI.popPopup(this);
-      window.removeEventListener("resize", resizeHandler, false);
+      window.removeEventListener("resize", this.resizeHandler, false);
     }
   },
 
   closeTab: function(aTab) {
     messageManager.addMessageListener("Browser:CanUnload:Return", this.closeTabReturn.bind(this));
   },
 
   closeTabReturn: function(aMessage) {
@@ -167,12 +165,19 @@ var TabsPopup = {
     }, 0, this);
   },
 
   _updateTabsCount: function() {
     let cmd = document.getElementById("cmd_showTabs");
     cmd.setAttribute("label", Browser.tabs.length);
   },
 
+  resizeHandler: function resizeHandler(aEvent) {
+    if (aEvent.target != window)
+      return;
+    if (!Util.isPortrait())
+      this._hidePortraitMenu();
+  },
+
   handleEvent: function handleEvent(aEvent) {
     this._updateTabsCount();
   }
 };
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -1351,44 +1351,51 @@ Browser.MainDragger.prototype = {
     let browser = getBrowser();
     let bcr = browser.getBoundingClientRect();
     this._contentView = browser.getViewAt(clientX - bcr.left, clientY - bcr.top);
     this._stopAtSidebar = 0;
 
     let isTablet = Util.isTablet();
     this._panToolbars = !isTablet;
 
+    this._grabSidebar = false;
+    this._canGrabSidebar = false;
     // In landscape portrait mode, swiping from the left margin drags the tab sidebar.
-    this._grabSidebar = isTablet && !Util.isPortrait() &&
-      ((Util.localeDir == Util.LOCALE_DIR_LTR) ?
-       (clientX - bcr.left < 30) :
-       (bcr.right - clientX < 30));
-
-    if (this._grabSidebar)
-      Browser.grabSidebar();
+    if (isTablet && !Util.isPortrait()) {
+      let grabSidebarMargin = TabsPopup.visible ? 30 : 5;
+      // Don't actually grab until we see whether the swipe is horizontal in dragMove.
+      this._canGrabSidebar = ((Util.localeDir == Util.LOCALE_DIR_LTR)
+                           ? (clientX - bcr.left < 30)
+                           : (bcr.right - clientX < 30));
+    }
 
     if (this._sidebarTimeout) {
       clearTimeout(this._sidebarTimeout);
       this._sidebarTimeout = null;
     }
   },
 
   dragStop: function dragStop(dx, dy, scroller) {
     if (this._grabSidebar) {
       Browser.ungrabSidebar();
       return;
     }
+
     if (this._contentView && this._contentView._updateCacheViewport)
       this._contentView._updateCacheViewport();
     this._contentView = null;
     this.dragMove(Browser.snapSidebars(), 0, scroller);
     Browser.tryUnfloatToolbar();
   },
 
   dragMove: function dragMove(dx, dy, scroller, aIsKinetic) {
+    if (this._canGrabSidebar && !this._grabSidebar && dx) {
+      this._grabSidebar = true;
+      Browser.grabSidebar();
+    }
     if (this._grabSidebar) {
       Browser.slideSidebarBy(dx);
       return;
     }
 
     let doffset = new Point(dx, dy);
     let sidebarOffset = null;
 
--- a/testing/mochitest/tests/test_sanitySimpletest.html
+++ b/testing/mochitest/tests/test_sanitySimpletest.html
@@ -60,32 +60,33 @@ function starttest() {
       var div1 = createEl('div', {'id': 'somediv', 'display': 'block'}, "I am a div");
       document.body.appendChild(div1);
       var divObj = this.getElement('somediv');
       is(divObj, div1, 'createEl did not create element as expected');
       is($('somediv'), divObj, '$ helper did not get element as expected');
       is(computedStyle(divObj, 'display'), 'block', 'computedStyle did not get right display value');
       document.body.removeChild(div1);
     
-      //use waitForFocus
+      /* note: expectChildProcessCrash is not being tested here, as it causes wildly variable
+       * run times. It is currently being tested in: 
+       *  dom/plugins/test/test_hanging.html and dom/plugins/test/test_crashing.html
+       */
+
       //note: this also adds a short wait period
       SimpleTest.executeSoon(
         function () {
           //finish() calls a slew of SimpleTest functions
           SimpleTest.finish();
           //call this after finish so we can make sure it works and doesn't hang our process
-          SimpleTest.expectUncaughtException();
-          throw "i am an uncaught exception"
-          /**
-           * Actually testing a crash of a child process ended up with wildly variable runtimes,
-           * on the scale of >1 second, so just make sure we can safely call the method
-           */
-          SimpleTest.expectChildProcessCrash();
           var endTime = new Date();
           SimpleTest.info("Profile::SimpleTestRunTime: " + (endTime-startTime));
+          //expect and throw exception here. Otherwise, any code that follows the throw call will never be executed
+          SimpleTest.expectUncaughtException();
+          //make sure we catch this error
+          throw "i am an uncaught exception"
         }
       );
     }
   );
 };
 //use addLoadEvent
 addLoadEvent(
   function() {
--- a/testing/xpcshell/xpcshell.ini
+++ b/testing/xpcshell/xpcshell.ini
@@ -73,16 +73,19 @@ skip-if = os == "android"
 [include:toolkit/mozapps/shared/test/unit/xpcshell.ini]
 [include:services/crypto/component/tests/unit/xpcshell.ini]
 [include:layout/tools/layout-debug/tests/unit/xpcshell.ini]
 skip-if = !debug
 
 [include:toolkit/crashreporter/test/unit/xpcshell.ini]
 skip-if = os == "linux" || !crashreporter
 
+[include:toolkit/crashreporter/test/unit_ipc/xpcshell.ini]
+skip-if.os == "linux" || !crashreporter
+
 #XXX: we don't actually set os = maemo
 [include:toolkit/crashreporter/client/maemo-unit/xpcshell.ini]
 run-if = os == "maemo"
 
 [include:toolkit/components/commandlines/test/unit_win/xpcshell.ini]
 skip-if = os != "win"
 
 [include:toolkit/components/commandlines/test/unit_unix/xpcshell.ini]
--- a/toolkit/content/tests/chrome/test_arrowpanel.xul
+++ b/toolkit/content/tests/chrome/test_arrowpanel.xul
@@ -5,19 +5,19 @@
 <window title="Arrow Panels"
         style="padding: 10px;"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <script type="application/javascript"
           src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
 
 <stack flex="1">
-  <label id="topleft" value="Top Left" left="15" top="15"/>
+  <label id="topleft" value="Top Left Corner" left="15" top="15"/>
   <label id="topright" value="Top Right" right="15" top="15"/>
-  <label id="bottomleft" value="Bottom Left" left="15" bottom="15"/>
+  <label id="bottomleft" value="Bottom Left Corner" left="15" bottom="15"/>
   <label id="bottomright" value="Bottom Right" right="15" bottom="15"/>
   <!-- Our SimpleTest/TestRunner.js runs tests inside an iframe which sizes are W=500 H=300.
        'left' and 'top' values need to be set so that the panel (popup) has enough room to display on its 4 sides. -->
   <label id="middle" value="+/- Centered" left="225" top="135"/>
   <iframe id="frame" type="content"
           src="data:text/html,&lt;input id='input'&gt;" width="100" height="100" left="225" top="120"/>
 </stack>
 
@@ -30,120 +30,131 @@
 </panel>
 
 <script type="application/javascript">
 <![CDATA[
 
 SimpleTest.waitForExplicitFinish();
 
 var expectedAnchor = null;
-var expectedSide = "", expectedAnchorEdge = "";
+var expectedSide = "", expectedAnchorEdge = "", expectedPack = "";
 var zoomFactor = 1;
 var runNextTest;
 
 function startTest()
 {
   runNextTest = nextTest();
   runNextTest.next();
 }
 
 function nextTest()
 {
   var panel = $("panel");
 
-  function openPopup(position, anchor, expected, anchorEdge)
+  function openPopup(position, anchor, expected, anchorEdge, pack)
   {
     expectedAnchor = anchor instanceof Node ? anchor : $(anchor);
     expectedSide = expected;
     expectedAnchorEdge = anchorEdge;
+    expectedPack = pack;
 
     panel.removeAttribute("side");
     panel.openPopup(expectedAnchor, position, 0, 0, false, false, null);
   }
 
-  openPopup("after_start", "topleft", "top", "left");
+  openPopup("after_start", "topleft", "top", "left", "start");
   yield;
-  openPopup("after_start", "bottomleft", "bottom", "left");
+  openPopup("after_start", "bottomleft", "bottom", "left", "start");
   yield;
-  openPopup("before_start", "topleft", "top", "left");
+  openPopup("before_start", "topleft", "top", "left", "start");
   yield;
-  openPopup("before_start", "bottomleft", "bottom", "left");
+  openPopup("before_start", "bottomleft", "bottom", "left", "start");
   yield;
-  openPopup("after_start", "middle", "top", "left");
+  openPopup("after_start", "middle", "top", "left", "start");
   yield;
-  openPopup("before_start", "middle", "bottom", "left");
+  openPopup("before_start", "middle", "bottom", "left", "start");
   yield;
 
-  openPopup("after_start", "topright", "top", "right");
+  openPopup("after_start", "topright", "top", "right", "end");
   yield;
-  openPopup("after_start", "bottomright", "bottom", "right");
+  openPopup("after_start", "bottomright", "bottom", "right", "end");
+  yield;
+  openPopup("before_start", "topright", "top", "right", "end");
   yield;
-  openPopup("before_start", "topright", "top", "right");
+  openPopup("before_start", "bottomright", "bottom", "right", "end");
   yield;
-  openPopup("before_start", "bottomright", "bottom", "right");
+
+  openPopup("after_end", "middle", "top", "right", "end");
+  yield;
+  openPopup("before_end", "middle", "bottom", "right", "end");
   yield;
 
-  openPopup("start_before", "topleft", "left", "top");
+  openPopup("start_before", "topleft", "left", "top", "start");
+  yield;
+  openPopup("start_before", "topright", "right", "top", "start");
   yield;
-  openPopup("start_before", "topright", "right", "top");
+  openPopup("end_before", "topleft", "left", "top", "start");
   yield;
-  openPopup("end_before", "topleft", "left", "top");
+  openPopup("end_before", "topright", "right", "top", "start");
+  yield;
+  openPopup("start_before", "middle", "right", "top", "start");
   yield;
-  openPopup("end_before", "topright", "right", "top");
+  openPopup("end_before", "middle", "left", "top", "start");
+  yield;
+
+  openPopup("start_before", "bottomleft", "left", "bottom", "end");
   yield;
-  openPopup("start_before", "middle", "right", "top");
+  openPopup("start_before", "bottomright", "right", "bottom", "end");
   yield;
-  openPopup("end_before", "middle", "left", "top");
+  openPopup("end_before", "bottomleft", "left", "bottom", "end");
+  yield;
+  openPopup("end_before", "bottomright", "right", "bottom", "end");
   yield;
 
-  openPopup("start_before", "bottomleft", "left", "bottom");
-  yield;
-  openPopup("start_before", "bottomright", "right", "bottom");
+  openPopup("start_after", "middle", "right", "bottom", "end");
   yield;
-  openPopup("end_before", "bottomleft", "left", "bottom");
-  yield;
-  openPopup("end_before", "bottomright", "right", "bottom");
+  openPopup("end_after", "middle", "left", "bottom", "end");
   yield;
 
-  openPopup("topcenter bottomleft", "bottomleft", "bottom", "center left");
+  openPopup("topcenter bottomleft", "bottomleft", "bottom", "center left", "start");
   yield;
-  openPopup("bottomcenter topleft", "topleft", "top", "center left");
+  openPopup("bottomcenter topleft", "topleft", "top", "center left", "start");
   yield;
-  openPopup("topcenter bottomright", "bottomright", "bottom", "center right");
+  openPopup("topcenter bottomright", "bottomright", "bottom", "center right", "end");
   yield;
-  openPopup("bottomcenter topright", "topright", "top", "center right");
+  openPopup("bottomcenter topright", "topright", "top", "center right", "end");
   yield;
-  openPopup("topcenter bottomleft", "middle", "bottom", "center left");
+  openPopup("topcenter bottomleft", "middle", "bottom", "center left", "start");
   yield;
-  openPopup("bottomcenter topleft", "middle", "top", "center left");
+  openPopup("bottomcenter topleft", "middle", "top", "center left", "start");
   yield;
 
-  openPopup("leftcenter topright", "middle", "right", "center top");
+  openPopup("leftcenter topright", "middle", "right", "center top", "start");
   yield;
-  openPopup("rightcenter bottomleft", "middle", "left", "center bottom");
+  openPopup("rightcenter bottomleft", "middle", "left", "center bottom", "end");
   yield;
 
 /*
   XXXndeakin disable these parts of the test which often cause problems, see bug 626563
 
-  openPopup("after_start", frames[0].document.getElementById("input"), "top", "left");
+  openPopup("after_start", frames[0].document.getElementById("input"), "top", "left", "start");
   yield;
 
   setScale(frames[0], 1.5);
-  openPopup("after_start", frames[0].document.getElementById("input"), "top", "left");
+  openPopup("after_start", frames[0].document.getElementById("input"), "top", "left", "start");
   yield;
 
   setScale(frames[0], 2.5);
-  openPopup("before_start", frames[0].document.getElementById("input"), "bottom", "left");
+  openPopup("before_start", frames[0].document.getElementById("input"), "bottom", "left", "start");
   yield;
 
   setScale(frames[0], 1);
 */
 
-  $("bigpanel").openPopup($("topleft"), "after_start", 0, 0, false, false, null);
+  $("bigpanel").openPopup($("topleft"), "after_start", 0, 0, false, false, null, "start");
   yield;
 
   SimpleTest.finish();
   yield;
 }
 
 function setScale(win, scale)
 {
@@ -216,16 +227,17 @@ function checkPanelPosition(panel)
       break;
   }
 
   is(anchor, expectedAnchor, "anchor");
 
   var arrow = document.getAnonymousElementByAttribute(panel, "anonid", "arrow");
   is(arrow.getAttribute("side"), expectedSide, "panel arrow side");
   is(arrow.hidden, false, "panel hidden");
+  is(arrow.parentNode.pack, expectedPack, "panel arrow pack");
 
   panel.hidePopup();
 }
 
 function checkBigPanel(panel)
 {
   ok(panel.firstChild.getBoundingClientRect().height < 2800, "big panel height");
   panel.hidePopup();
--- a/toolkit/content/widgets/popup.xml
+++ b/toolkit/content/widgets/popup.xml
@@ -310,17 +310,17 @@
           <xul:box class="panel-inner-arrowcontent" xbl:inherits="align,dir,orient,pack" flex="1">
             <children/>
             <xul:box class="panel-inner-arrowcontentfooter" xbl:inherits="footertype" hidden="true"/>
           </xul:box>
         </xul:box>
       </xul:box>
     </content>
     <implementation>
-      <field name="_fadeTimer"/>
+      <field name="_fadeTimer">null</field>
     </implementation>
     <handlers>
       <handler event="popupshowing">
       <![CDATA[
         var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
         var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
         var arrow = document.getAnonymousElementByAttribute(this, "anonid", "arrow");
 
@@ -378,20 +378,20 @@
           else {
             let pack = "";
 
             // We have to guess where to position the arrow given that we don't
             // have access to the parameters passed to |openPopup|.
 
             // If the popup is on the left of the anchor.
             if (smallerTo(popupLeft, anchorLeft, epsilon) && smallerTo(popupRight, anchorRight, epsilon)) {
-              pack = (popupLeft + popupRect.width / 2 < anchorLeft) ? "end" : "start";
+              pack = "end";
             // If the popup is on the right of the anchor.
             } else if (smallerTo(anchorLeft, popupLeft, epsilon) && smallerTo(anchorRight, popupRight, epsilon)) {
-              pack = (popupLeft + popupRect.width / 2 > anchorRight) ? "start" : "end";
+              pack = "start"; //(popupLeft + popupRect.width / 2 > anchorRight) ? "start" : "end";
             // If the popup is not on the right nor on the left.
             // Basically, that means one is above the other and one is bigger
             // than the other.
             // In that case, we can't easily choose a position for the arrow so
             // we have to guess depending on which side the popup is more close to.
             } else {
               pack = (Math.abs(popupLeft - anchorLeft) < Math.abs(popupRight - anchorRight)) ? "start" : "end";
             }
--- a/toolkit/crashreporter/Makefile.in
+++ b/toolkit/crashreporter/Makefile.in
@@ -122,9 +122,11 @@ FORCE_STATIC_LIB = 1
 EXTRA_JS_MODULES = \
   CrashSubmit.jsm \
   $(NULL)
 
 ifdef ENABLE_TESTS
 TOOL_DIRS = test
 endif
 
+include $(topsrcdir)/config/config.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/crashreporter/nsExceptionHandler.cpp
+++ b/toolkit/crashreporter/nsExceptionHandler.cpp
@@ -32,17 +32,21 @@
  * 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 "mozilla/dom/CrashReporterChild.h"
+#include "nsXULAppAPI.h"
+
 #include "nsExceptionHandler.h"
+#include "nsThreadUtils.h"
 
 #if defined(XP_WIN32)
 #ifdef WIN32_LEAN_AND_MEAN
 #undef WIN32_LEAN_AND_MEAN
 #endif
 
 #include "nsIWindowsRegKey.h"
 #include "client/windows/crash_generation/crash_generation_server.h"
@@ -107,16 +111,18 @@ CFStringRef reporterClientAppID = CFSTR(
 #endif
 
 #include "nsIUUIDGenerator.h"
 
 using google_breakpad::CrashGenerationServer;
 using google_breakpad::ClientInfo;
 using mozilla::Mutex;
 using mozilla::MutexAutoLock;
+using mozilla::dom::CrashReporterChild;
+using mozilla::dom::PCrashReporterChild;
 
 namespace CrashReporter {
 
 #ifdef XP_WIN32
 typedef wchar_t XP_CHAR;
 typedef std::wstring xpstring;
 #define CONVERT_UTF16_TO_XP_CHAR(x) x
 #define CONVERT_XP_CHAR_TO_UTF16(x) x
@@ -226,16 +232,20 @@ static ChildMinidumpMap* pidToMinidump;
 // reports
 static const char* kSubprocessBlacklist[] = {
   "FramePoisonBase",
   "FramePoisonSize",
   "StartupTime",
   "URL"
 };
 
+// If annotations are attempted before the crash reporter is enabled,
+// they queue up here.
+class DelayedNote;
+nsTArray<nsAutoPtr<DelayedNote> >* gDelayedAnnotations;
 
 #ifdef XP_MACOSX
 static cpu_type_t pref_cpu_types[2] = {
 #if defined(__i386__)
                                  CPU_TYPE_X86,
 #elif defined(__x86_64__)
                                  CPU_TYPE_X86_64,
 #elif defined(__ppc__)
@@ -796,17 +806,17 @@ nsresult SetExceptionHandler(nsILocalFil
   }
 #endif
 
   return NS_OK;
 }
 
 bool GetEnabled()
 {
-  return gExceptionHandler != nsnull && !gExceptionHandler->IsOutOfProcess();
+  return gExceptionHandler != nsnull;
 }
 
 bool GetMinidumpPath(nsAString& aPath)
 {
   if (!gExceptionHandler)
     return false;
 
   aPath = CONVERT_XP_CHAR_TO_UTF16(gExceptionHandler->dump_path().c_str());
@@ -1090,38 +1100,99 @@ static PLDHashOperator EnumerateEntries(
                                         nsCString entry,
                                         void* userData)
 {
   crashReporterAPIData->Append(key + NS_LITERAL_CSTRING("=") + entry +
                                NS_LITERAL_CSTRING("\n"));
   return PL_DHASH_NEXT;
 }
 
-nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
+// This function is miscompiled with MSVC 2005/2008 when PGO is on.
+#ifdef _MSC_VER
+#pragma optimize("", off)
+#endif
+static nsresult
+EscapeAnnotation(const nsACString& key, const nsACString& data, nsCString& escapedData)
 {
-  if (!GetEnabled())
-    return NS_ERROR_NOT_INITIALIZED;
-
   if (DoFindInReadable(key, NS_LITERAL_CSTRING("=")) ||
       DoFindInReadable(key, NS_LITERAL_CSTRING("\n")))
     return NS_ERROR_INVALID_ARG;
 
   if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
     return NS_ERROR_INVALID_ARG;
 
-  nsCString escapedData(data);
+  escapedData = data;
 
   // escape backslashes
   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\\"),
               NS_LITERAL_CSTRING("\\\\"));
   // escape newlines
   ReplaceChar(escapedData, NS_LITERAL_CSTRING("\n"),
               NS_LITERAL_CSTRING("\\n"));
+  return NS_OK;
+}
+#ifdef _MSC_VER
+#pragma optimize("", on)
+#endif
 
-  nsresult rv = crashReporterAPIData_Hash->Put(key, escapedData);
+class DelayedNote
+{
+ public:
+  DelayedNote(const nsACString& aKey, const nsACString& aData)
+  : mKey(aKey), mData(aData), mType(Annotation) {}
+
+  DelayedNote(const nsACString& aData)
+  : mData(aData), mType(AppNote) {}
+
+  void Run()
+  {
+    if (mType == Annotation) {
+      AnnotateCrashReport(mKey, mData);
+    } else {
+      AppendAppNotesToCrashReport(mData);
+    }
+  }
+  
+ private:
+  nsCString mKey;
+  nsCString mData;
+  enum AnnotationType { Annotation, AppNote } mType;
+};
+
+static void
+EnqueueDelayedNote(DelayedNote* aNote)
+{
+  if (!gDelayedAnnotations) {
+    gDelayedAnnotations = new nsTArray<nsAutoPtr<DelayedNote> >();
+  }
+  gDelayedAnnotations->AppendElement(aNote);
+}
+
+nsresult AnnotateCrashReport(const nsACString& key, const nsACString& data)
+{
+  if (!GetEnabled())
+    return NS_ERROR_NOT_INITIALIZED;
+
+  nsCString escapedData;
+  nsresult rv = EscapeAnnotation(key, data, escapedData);
+  if (NS_FAILED(rv))
+    return rv;
+
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
+    if (!reporter) {
+      EnqueueDelayedNote(new DelayedNote(key, data));
+      return NS_OK;
+    }
+    if (!reporter->SendAnnotateCrashReport(nsCString(key), escapedData))
+      return NS_ERROR_FAILURE;
+    return NS_OK;
+  }
+
+  rv = crashReporterAPIData_Hash->Put(key, escapedData);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // now rebuild the file contents
   crashReporterAPIData->Truncate(0);
   crashReporterAPIData_Hash->EnumerateRead(EnumerateEntries,
                                            crashReporterAPIData);
 
   return NS_OK;
@@ -1130,16 +1201,36 @@ nsresult AnnotateCrashReport(const nsACS
 nsresult AppendAppNotesToCrashReport(const nsACString& data)
 {
   if (!GetEnabled())
     return NS_ERROR_NOT_INITIALIZED;
 
   if (DoFindInReadable(data, NS_LITERAL_CSTRING("\0")))
     return NS_ERROR_INVALID_ARG;
 
+  if (XRE_GetProcessType() != GeckoProcessType_Default) {
+    PCrashReporterChild* reporter = CrashReporterChild::GetCrashReporter();
+    if (!reporter) {
+      EnqueueDelayedNote(new DelayedNote(data));
+      return NS_OK;
+    }
+
+    // Since we don't go through AnnotateCrashReport in the parent process,
+    // we must ensure that the data is escaped and valid before the parent
+    // sees it.
+    nsCString escapedData;
+    nsresult rv = EscapeAnnotation(NS_LITERAL_CSTRING("Notes"), data, escapedData);
+    if (NS_FAILED(rv))
+      return rv;
+
+    if (!reporter->SendAppendAppNotes(escapedData))
+      return NS_ERROR_FAILURE;
+    return NS_OK;
+  }
+
   notesField->Append(data);
   return AnnotateCrashReport(NS_LITERAL_CSTRING("Notes"), *notesField);
 }
 
 // Returns true if found, false if not found.
 bool GetAnnotation(const nsACString& key, nsACString& data)
 {
   if (!gExceptionHandler)
@@ -1869,17 +1960,17 @@ SetRemoteExceptionHandler(const nsACStri
   // crash reporting is disabled
   if (crashPipe.Equals(kNullNotifyPipe))
     return true;
 
   NS_ABORT_IF_FALSE(!gExceptionHandler, "crash client already init'd");
 
   gExceptionHandler = new google_breakpad::
     ExceptionHandler(L"",
-                     NULL,    // no filter callback
+                     FPEFilter,
                      NULL,    // no minidump callback
                      NULL,    // no callback context
                      google_breakpad::ExceptionHandler::HANDLER_ALL,
                      MiniDumpNormal,
                      NS_ConvertASCIItoUTF16(crashPipe).BeginReading(),
                      NULL);
 #ifdef XP_WIN
   gExceptionHandler->set_handle_debug_exceptions(true);
@@ -1920,16 +2011,23 @@ SetRemoteExceptionHandler()
   gExceptionHandler = new google_breakpad::
     ExceptionHandler("",
                      NULL,    // no filter callback
                      NULL,    // no minidump callback
                      NULL,    // no callback context
                      true,    // install signal handlers
                      kMagicChildCrashReportFd);
 
+  if (gDelayedAnnotations) {
+    for (PRUint32 i = 0; i < gDelayedAnnotations->Length(); i++) {
+      gDelayedAnnotations->ElementAt(i)->Run();
+    }
+    delete gDelayedAnnotations;
+  }
+
   // we either do remote or nothing, no fallback to regular crash reporting
   return gExceptionHandler->IsOutOfProcess();
 }
 
 //--------------------------------------------------
 #elif defined(XP_MACOSX)
 // Child-side API
 bool
--- a/toolkit/crashreporter/test/Makefile.in
+++ b/toolkit/crashreporter/test/Makefile.in
@@ -39,17 +39,17 @@ DEPTH		= ../../..
 topsrcdir	= @top_srcdir@
 srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = toolkit/crashreporter/test
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = crashreporter_test
-XPCSHELL_TESTS = unit
+XPCSHELL_TESTS = unit unit_ipc
 
 LIBRARY_NAME = testcrasher
 NO_DIST_INSTALL = 1
 
 FORCE_SHARED_LIB = 1
 
 VPATH += \
   $(srcdir)/../google-breakpad/src/processor/ \
@@ -90,9 +90,10 @@ ifneq (mobile,$(MOZ_BUILD_APP))
   $(NULL)
 
 libs::  $(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)/browser
 endif
 
 libs:: $(SHARED_LIBRARY) $(EXTRA_JS_MODULES)
 	$(INSTALL) $^ $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit/
+	$(INSTALL) $^ $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit_ipc/
 
--- a/toolkit/crashreporter/test/unit/crasher_subprocess_head.js
+++ b/toolkit/crashreporter/test/unit/crasher_subprocess_head.js
@@ -1,17 +1,25 @@
 // enable crash reporting first
 let cwd = Components.classes["@mozilla.org/file/directory_service;1"]
       .getService(Components.interfaces.nsIProperties)
       .get("CurWorkD", Components.interfaces.nsILocalFile);
+
 let crashReporter =
   Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
     .getService(Components.interfaces.nsICrashReporter);
-crashReporter.enabled = true;
-crashReporter.minidumpPath = cwd;
+
+// the crash reporter is already enabled in content processes,
+// and setting the minidump path is not allowed
+let processType = Components.classes["@mozilla.org/xre/runtime;1"].
+      getService(Components.interfaces.nsIXULRuntime).processType;
+if (processType == Components.interfaces.nsIXULRuntime.PROCESS_TYPE_DEFAULT) {
+  crashReporter.enabled = true;
+  crashReporter.minidumpPath = cwd;
+}
 
 let ios = Components.classes["@mozilla.org/network/io-service;1"]
             .getService(Components.interfaces.nsIIOService);
 let protocolHandler = ios.getProtocolHandler("resource")
                         .QueryInterface(Components.interfaces.nsIResProtocolHandler);
 let curDirURI = ios.newFileURI(cwd);
 protocolHandler.setSubstitution("test", curDirURI);
 Components.utils.import("resource://test/CrashTestUtils.jsm");
--- a/toolkit/crashreporter/test/unit/head_crashreporter.js
+++ b/toolkit/crashreporter/test/unit/head_crashreporter.js
@@ -60,16 +60,21 @@ function do_crash(setup, callback, canRe
   }
   catch(ex) {} // on Windows we exit with a -1 status when crashing.
 
   if (!canReturnZero) {
     // should exit with an error (should have crashed)
     do_check_neq(process.exitValue, 0);
   }
 
+  handleMinidump(callback);
+}
+
+function handleMinidump(callback)
+{
   // find minidump
   let minidump = null;
   let en = do_get_cwd().directoryEntries;
   while (en.hasMoreElements()) {
     let f = en.getNext().QueryInterface(Components.interfaces.nsILocalFile);
     if (f.leafName.substr(-4) == ".dmp") {
       minidump = f;
       break;
@@ -96,16 +101,54 @@ function do_crash(setup, callback, canRe
     callback(minidump, extra);
 
   if (minidump.exists())
     minidump.remove(false);
   if (extrafile.exists())
     extrafile.remove(false);
 }
 
+function do_content_crash(setup, callback)
+{
+  do_load_child_test_harness();
+  do_test_pending();
+
+  // Setting the minidump path won't work in the child, so we need to do
+  // that here.
+  let crashReporter =
+      Components.classes["@mozilla.org/toolkit/crash-reporter;1"]
+      .getService(Components.interfaces.nsICrashReporter);
+  crashReporter.minidumpPath = do_get_cwd();
+
+  let headfile = do_get_file("../unit/crasher_subprocess_head.js");
+  let tailfile = do_get_file("../unit/crasher_subprocess_tail.js");
+  if (setup) {
+    if (typeof(setup) == "function")
+      // funky, but convenient
+      setup = "("+setup.toSource()+")();";
+  }
+
+  let handleCrash = function() {
+    try {            
+      handleMinidump(callback);
+    } catch (x) {
+      do_report_unexpected_exception(x);
+    }
+    do_test_finished();
+  };
+  
+  sendCommand("load(\"" + headfile.path.replace(/\\/g, "/") + "\");", function()
+    sendCommand(setup, function()
+      sendCommand("load(\"" + tailfile.path.replace(/\\/g, "/") + "\");",
+        function() do_execute_soon(handleCrash)
+      )
+    )
+  );
+}
+
 // Utility functions for parsing .extra files
 function parseKeyValuePairs(text) {
   var lines = text.split('\n');
   var data = {};
   for (let i = 0; i < lines.length; i++) {
     if (lines[i] == '')
       continue;
 
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/unit_ipc/test_content_annotation.js
@@ -0,0 +1,22 @@
+load("../unit/head_crashreporter.js");
+
+function run_test()
+{
+  if (!("@mozilla.org/toolkit/crash-reporter;1" in Components.classes)) {
+    dump("INFO | test_content_annotation.js | Can't test crashreporter in a non-libxul build.\n");
+    return;
+  }
+
+  // Try crashing with a pure virtual call
+  do_content_crash(function() {
+                     crashType = CrashTestUtils.CRASH_RUNTIMEABORT;
+                     crashReporter.annotateCrashReport("TestKey", "TestValue");
+                     crashReporter.appendAppNotesToCrashReport("!!!foo!!!");
+		   },
+                   function(mdump, extra) {
+                     do_check_eq(extra.TestKey, "TestValue");
+                     do_check_true('StartupTime' in extra);
+                     do_check_true('ProcessType' in extra);
+                     do_check_neq(extra.Notes.indexOf("!!!foo!!!"), -1);
+                   });
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/toolkit/crashreporter/test/unit_ipc/xpcshell.ini
@@ -0,0 +1,5 @@
+[DEFAULT]
+head = head_crashreporter.js
+tail = 
+
+[test_content_annotation.js]
--- a/toolkit/xre/nsEmbedFunctions.cpp
+++ b/toolkit/xre/nsEmbedFunctions.cpp
@@ -356,17 +356,19 @@ XRE_InitChildProcess(int aArgc,
   }
   err = task_set_bootstrap_port(mach_task_self(),
                                 parent_message.GetTranslatedPort(0));
   if (err != KERN_SUCCESS) {
     NS_WARNING("child task_set_bootstrap_port() failed");
     return 1;
   }
 #endif
-  
+
+  SetupErrorHandling(aArgv[0]);  
+
 #if defined(MOZ_CRASHREPORTER)
   if (aArgc < 1)
     return 1;
   const char* const crashReporterArg = aArgv[--aArgc];
   
 #  if defined(XP_WIN) || defined(XP_MACOSX)
   // on windows and mac, |crashReporterArg| is the named pipe on which the
   // server is listening for requests, or "-" if crash reporting is
@@ -387,18 +389,16 @@ XRE_InitChildProcess(int aArgc,
 #  else
 #    error "OOP crash reporting unsupported on this platform"
 #  endif   
 #endif // if defined(MOZ_CRASHREPORTER)
 
   gArgv = aArgv;
   gArgc = aArgc;
 
-  SetupErrorHandling(aArgv[0]);
-  
 #if defined(MOZ_WIDGET_GTK2)
   g_thread_init(NULL);
 #endif
 
 #if defined(MOZ_WIDGET_QT)
   nsQAppInstance::AddRef();
 #endif
 
@@ -707,26 +707,29 @@ XRE_ShutdownChildProcess()
       // that case, we fire off an Exit() here.  If we were indeed
       // above MessagePump::Run(), this Exit() is just superfluous.
       appShell->Exit();
   }
 #endif // XP_MACOSX
 }
 
 namespace {
-TestShellParent* gTestShellParent = nsnull;
+ContentParent* gContentParent; //long-lived, manually refcounted
 TestShellParent* GetOrCreateTestShellParent()
 {
-    if (!gTestShellParent) {
-        ContentParent* parent = ContentParent::GetNewOrUsed();
-        NS_ENSURE_TRUE(parent, nsnull);
-        gTestShellParent = parent->CreateTestShell();
-        NS_ENSURE_TRUE(gTestShellParent, nsnull);
+    if (!gContentParent) {
+        NS_ADDREF(gContentParent = ContentParent::GetNewOrUsed());
+    } else if (!gContentParent->IsAlive()) {
+        return nsnull;
     }
-    return gTestShellParent;
+    TestShellParent* tsp = gContentParent->GetTestShellSingleton();
+    if (!tsp) {
+        tsp = gContentParent->CreateTestShell();
+    }
+    return tsp;
 }
 }
 
 bool
 XRE_SendTestShellCommand(JSContext* aCx,
                          JSString* aCommand,
                          void* aCallback)
 {
@@ -755,20 +758,26 @@ XRE_GetChildGlobalObject(JSContext* aCx,
 {
     TestShellParent* tsp = GetOrCreateTestShellParent();
     return tsp && tsp->GetGlobalJSObject(aCx, aGlobalP);
 }
 
 bool
 XRE_ShutdownTestShell()
 {
-  if (!gTestShellParent)
-    return true;
-  return static_cast<ContentParent*>(gTestShellParent->Manager())->
-    DestroyTestShell(gTestShellParent);
+    if (!gContentParent) {
+        return true;
+    }
+    bool ret = true;
+    if (gContentParent->IsAlive()) {
+        ret = gContentParent->DestroyTestShell(
+            gContentParent->GetTestShellSingleton());
+    }
+    NS_RELEASE(gContentParent);
+    return ret;
 }
 
 #ifdef MOZ_X11
 void
 XRE_InstallX11ErrorHandler()
 {
   InstallX11ErrorHandler();
 }
--- a/toolkit/xre/nsX11ErrorHandler.cpp
+++ b/toolkit/xre/nsX11ErrorHandler.cpp
@@ -33,19 +33,16 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "nsX11ErrorHandler.h"
 
-#include "mozilla/plugins/PluginProcessChild.h"
-using mozilla::plugins::PluginProcessChild;
-
 #include "prenv.h"
 #include "nsXULAppAPI.h"
 #include "nsExceptionHandler.h"
 #include "nsDebug.h"
 
 #include "mozilla/X11Util.h"
 #include <X11/Xlib.h>
 
@@ -151,26 +148,20 @@ X11Error(Display *display, XErrorEvent *
       notes.AppendInt(PRUint32(age));
       notes.Append(" requests ago");
     }
   }
 
 #ifdef MOZ_CRASHREPORTER
   switch (XRE_GetProcessType()) {
   case GeckoProcessType_Default:
+  case GeckoProcessType_Plugin:
+  case GeckoProcessType_Content:
     CrashReporter::AppendAppNotesToCrashReport(notes);
     break;
-  case GeckoProcessType_Plugin:
-    if (CrashReporter::GetEnabled()) {
-      // This is assuming that X operations are performed on the plugin
-      // thread.  If plugins are using X on another thread, then we'll need to
-      // handle that differently.
-      PluginProcessChild::AppendNotesToCrashReport(notes);
-    }
-    break;
   default: 
     ; // crash report notes not supported.
   }
 #endif
 
 #ifdef DEBUG
   // The resource id is unlikely to be useful in a crash report without
   // context of other ids, but add it to the debug console output.
--- a/widget/src/cocoa/TextInputHandler.h
+++ b/widget/src/cocoa/TextInputHandler.h
@@ -476,33 +476,20 @@ protected:
     // Handling native key event
     NSEvent* mKeyEvent;
     // Whether keydown event was consumed by web contents or chrome contents.
     bool mKeyDownHandled;
     // Whether keypress event was dispatched for mKeyEvent.
     bool mKeyPressDispatched;
     // Whether keypress event was consumed by web contents or chrome contents.
     bool mKeyPressHandled;
-    // Whether the key event causes other key events via IME or something.
-    bool mCausedOtherKeyEvents;
 
-    KeyEventState(NSEvent* aNativeKeyEvent) : mKeyEvent(nsnull)
+    KeyEventState() : mKeyEvent(nsnull)
     {
       Clear();
-      Set(aNativeKeyEvent);
-    }
-
-    KeyEventState(const KeyEventState &aOther) : mKeyEvent(nsnull)
-    {
-      Clear();
-      mKeyEvent = [aOther.mKeyEvent retain];
-      mKeyDownHandled = aOther.mKeyDownHandled;
-      mKeyPressDispatched = aOther.mKeyPressDispatched;
-      mKeyPressHandled = aOther.mKeyPressHandled;
-      mCausedOtherKeyEvents = aOther.mCausedOtherKeyEvents;
     }
 
     ~KeyEventState()
     {
       Clear();
     }
 
     void Set(NSEvent* aNativeKeyEvent)
@@ -516,83 +503,46 @@ protected:
     {
       if (mKeyEvent) {
         [mKeyEvent release];
         mKeyEvent = nsnull;
       }
       mKeyDownHandled = false;
       mKeyPressDispatched = false;
       mKeyPressHandled = false;
-      mCausedOtherKeyEvents = false;
     }
 
     bool KeyDownOrPressHandled()
     {
       return mKeyDownHandled || mKeyPressHandled;
     }
-
-  protected:
-    KeyEventState()
-    {
-    }    
   };
 
   /**
    * Helper class for guaranteeing cleaning mCurrentKeyEvent
    */
   class AutoKeyEventStateCleaner
   {
   public:
     AutoKeyEventStateCleaner(TextInputHandlerBase* aHandler) :
       mHandler(aHandler)
     {
     }
 
     ~AutoKeyEventStateCleaner()
     {
-      NS_ASSERTION(mHandler->mCurrentKeyEvents.Length() > 0,
-                   "The key event was removed by manually?");
-      mHandler->mCurrentKeyEvents.RemoveElementAt(0);
+      mHandler->mCurrentKeyEvent.Clear();
     }
   private:
-    nsRefPtr<TextInputHandlerBase> mHandler;
+    TextInputHandlerBase* mHandler;
   };
 
-  /**
-   * mCurrentKeyEvents stores all key events which are being processed.
-   * When we call interpretKeyEvents, IME may generate other key events.
-   * mCurrentKeyEvents[0] is the latest key event.
-   */
-  nsTArray<KeyEventState> mCurrentKeyEvents;
-
-  /**
-   *
-   */
-  KeyEventState* PushKeyEvent(NSEvent* aNativeKeyEvent)
-  {
-    PRUint32 nestCount = mCurrentKeyEvents.Length();
-    for (PRUint32 i = 0; i < nestCount; i++) {
-      // When the key event is caused by another key event, all key events
-      // which are being handled should be marked as "consumed".
-      mCurrentKeyEvents[i].mCausedOtherKeyEvents = true;
-    }
-    KeyEventState keyEventState(aNativeKeyEvent);
-    return mCurrentKeyEvents.InsertElementAt(0, keyEventState);
-  }
-
-  /**
-   * GetCurrentKeyEvent() returns current processing key event.
-   */
-  KeyEventState* GetCurrentKeyEvent()
-  {
-    if (mCurrentKeyEvents.Length() == 0) {
-      return nsnull;
-    }
-    return &mCurrentKeyEvents[0];
-  }
+  // XXX If keydown event was nested, the key event is overwritten by newer
+  //     event.  This is wrong behavior.  Some IMEs are making such situation.
+  KeyEventState mCurrentKeyEvent;
 
   /**
    * IsPrintableChar() checks whether the unicode character is
    * a non-printable ASCII character or not.  Note that this returns
    * TRUE even if aChar is a non-printable UNICODE character.
    *
    * @param aChar                 A unicode character.
    * @return                      TRUE if aChar is a printable ASCII character
@@ -1164,18 +1114,17 @@ public:
    *
    * @return                      TRUE if keypress event for latest native key
    *                              event was handled.  Otherwise, FALSE.
    *                              If this handler isn't handling any key events,
    *                              always returns FALSE.
    */
   bool KeyPressWasHandled()
   {
-    KeyEventState* currentKeyEvent = GetCurrentKeyEvent();
-    return currentKeyEvent && currentKeyEvent->mKeyPressHandled;
+    return mCurrentKeyEvent.mKeyPressHandled;
   }
 
 protected:
   /**
    * DispatchKeyEventForFlagsChanged() dispatches keydown event or keyup event
    * for the aNativeEvent.
    *
    * @param aNativeEvent          A native flagschanged event which you want to
--- a/widget/src/cocoa/TextInputHandler.mm
+++ b/widget/src/cocoa/TextInputHandler.mm
@@ -992,17 +992,17 @@ TextInputHandler::HandleKeyDownEvent(NSE
      "charactersIgnoringModifiers=\"%s\"",
      this, aNativeEvent, GetNativeKeyEventType(aNativeEvent),
      [aNativeEvent keyCode], [aNativeEvent keyCode],
      [aNativeEvent modifierFlags], GetCharacters([aNativeEvent characters]),
      GetCharacters([aNativeEvent charactersIgnoringModifiers])));
 
   nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
 
-  KeyEventState* currentKeyEvent = PushKeyEvent(aNativeEvent);
+  mCurrentKeyEvent.Set(aNativeEvent);
   AutoKeyEventStateCleaner remover(this);
 
   BOOL nonDeadKeyPress = [[aNativeEvent characters] length] > 0;
   if (nonDeadKeyPress && !IsIMEComposing()) {
     NSResponder* firstResponder = [[mView window] firstResponder];
 
     nsKeyEvent keydownEvent(true, NS_KEY_DOWN, mWidget);
     InitKeyEvent(aNativeEvent, keydownEvent);
@@ -1010,32 +1010,32 @@ TextInputHandler::HandleKeyDownEvent(NSE
 #ifndef NP_NO_CARBON
     EventRecord carbonEvent;
     if ([mView pluginEventModel] == NPEventModelCarbon) {
       ConvertCocoaKeyEventToCarbonEvent(aNativeEvent, carbonEvent, true);
       keydownEvent.pluginEvent = &carbonEvent;
     }
 #endif // #ifndef NP_NO_CARBON
 
-    currentKeyEvent->mKeyDownHandled = DispatchEvent(keydownEvent);
+    mCurrentKeyEvent.mKeyDownHandled = DispatchEvent(keydownEvent);
     if (Destroyed()) {
       PR_LOG(gLog, PR_LOG_ALWAYS,
         ("%p TextInputHandler::HandleKeyDownEvent, "
          "widget was destroyed by keydown event", this));
-      return currentKeyEvent->KeyDownOrPressHandled();
+      return mCurrentKeyEvent.KeyDownOrPressHandled();
     }
 
     // The key down event may have shifted the focus, in which
     // case we should not fire the key press.
     // XXX This is a special code only on Cocoa widget, why is this needed?
     if (firstResponder != [[mView window] firstResponder]) {
       PR_LOG(gLog, PR_LOG_ALWAYS,
         ("%p TextInputHandler::HandleKeyDownEvent, "
          "view lost focus by keydown event", this));
-      return currentKeyEvent->KeyDownOrPressHandled();
+      return mCurrentKeyEvent.KeyDownOrPressHandled();
     }
 
     // If this is the context menu key command, send a context menu key event.
     NSUInteger modifierFlags =
       [aNativeEvent modifierFlags] & NSDeviceIndependentModifierFlagsMask;
     if (modifierFlags == NSControlKeyMask &&
         [[aNativeEvent charactersIgnoringModifiers] isEqualToString:@" "]) {
       nsMouseEvent contextMenuEvent(true, NS_CONTEXTMENU,
@@ -1047,17 +1047,41 @@ TextInputHandler::HandleKeyDownEvent(NSE
       bool cmEventHandled = DispatchEvent(contextMenuEvent);
       PR_LOG(gLog, PR_LOG_ALWAYS,
         ("%p TextInputHandler::HandleKeyDownEvent, "
          "context menu event dispatched, handled=%s%s",
          this, TrueOrFalse(cmEventHandled),
          Destroyed() ? " and widget was destroyed" : ""));
       [mView maybeInitContextMenuTracking];
       // Bail, there is nothing else to do here.
-      return (cmEventHandled || currentKeyEvent->KeyDownOrPressHandled());
+      return (cmEventHandled || mCurrentKeyEvent.KeyDownOrPressHandled());
+    }
+
+    nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
+    InitKeyEvent(aNativeEvent, keypressEvent);
+
+    // if this is a non-letter keypress, or the control key is down,
+    // dispatch the keydown to gecko, so that we trap delete,
+    // control-letter combinations etc before Cocoa tries to use
+    // them for keybindings.
+    // XXX This is wrong. IME may be handle the non-letter keypress event as
+    //     its owning shortcut key.  See bug 477291.
+    if ((!keypressEvent.isChar || keypressEvent.isControl) &&
+        !IsIMEComposing()) {
+      if (mCurrentKeyEvent.mKeyDownHandled) {
+        keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
+      }
+      mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent);
+      mCurrentKeyEvent.mKeyPressDispatched = true;
+      if (Destroyed()) {
+        PR_LOG(gLog, PR_LOG_ALWAYS,
+          ("%p TextInputHandler::HandleKeyDownEvent, "
+           "widget was destroyed by keypress event", this));
+        return mCurrentKeyEvent.KeyDownOrPressHandled();
+      }
     }
   }
 
   // Let Cocoa interpret the key events, caching IsIMEComposing first.
   bool wasComposing = IsIMEComposing();
   bool interpretKeyEventsCalled = false;
   if (IsIMEEnabled() || IsASCIICapableOnly()) {
     PR_LOG(gLog, PR_LOG_ALWAYS,
@@ -1069,25 +1093,25 @@ TextInputHandler::HandleKeyDownEvent(NSE
       ("%p TextInputHandler::HandleKeyDownEvent, called interpretKeyEvents",
        this));
   }
 
   if (Destroyed()) {
     PR_LOG(gLog, PR_LOG_ALWAYS,
       ("%p TextInputHandler::HandleKeyDownEvent, widget was destroyed",
        this));
-    return currentKeyEvent->KeyDownOrPressHandled();
+    return mCurrentKeyEvent.KeyDownOrPressHandled();
   }
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TextInputHandler::HandleKeyDownEvent, wasComposing=%s, "
      "IsIMEComposing()=%s",
      this, TrueOrFalse(wasComposing), TrueOrFalse(IsIMEComposing())));
 
-  if (!currentKeyEvent->mKeyPressDispatched && nonDeadKeyPress &&
+  if (!mCurrentKeyEvent.mKeyPressDispatched && nonDeadKeyPress &&
       !wasComposing && !IsIMEComposing()) {
     nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
     InitKeyEvent(aNativeEvent, keypressEvent);
 
     // If we called interpretKeyEvents and this isn't normal character input
     // then IME probably ate the event for some reason. We do not want to
     // send a key press event in that case.
     // TODO:
@@ -1095,35 +1119,34 @@ TextInputHandler::HandleKeyDownEvent(NSE
     // 1. If key events were nested during calling interpretKeyEvents, it means
     //    that IME did something.  Then, we should do nothing.
     // 2. If one or more commands are called like "deleteBackward", we should
     //    dispatch keypress event at that time.  Note that the command may have
     //    been a converted or generated action by IME.  Then, we shouldn't do
     //    our default action for this key.
     if (!(interpretKeyEventsCalled &&
           IsNormalCharInputtingEvent(keypressEvent))) {
-      if (currentKeyEvent->mKeyDownHandled ||
-          currentKeyEvent->mCausedOtherKeyEvents) {
+      if (mCurrentKeyEvent.mKeyDownHandled) {
         keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
       }
-      currentKeyEvent->mKeyPressHandled = DispatchEvent(keypressEvent);
+      mCurrentKeyEvent.mKeyPressHandled = DispatchEvent(keypressEvent);
       PR_LOG(gLog, PR_LOG_ALWAYS,
         ("%p TextInputHandler::HandleKeyDownEvent, keypress event dispatched",
          this));
     }
   }
 
   // Note: mWidget might have become null here. Don't count on it from here on.
 
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TextInputHandler::HandleKeyDownEvent, "
      "keydown handled=%s, keypress handled=%s",
-     this, TrueOrFalse(currentKeyEvent->mKeyDownHandled),
-     TrueOrFalse(currentKeyEvent->mKeyPressHandled)));
-  return currentKeyEvent->KeyDownOrPressHandled();
+     this, TrueOrFalse(mCurrentKeyEvent.mKeyDownHandled),
+     TrueOrFalse(mCurrentKeyEvent.mKeyPressHandled)));
+  return mCurrentKeyEvent.KeyDownOrPressHandled();
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
 }
 
 void
 TextInputHandler::HandleKeyUpEvent(NSEvent* aNativeEvent)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
@@ -1284,27 +1307,23 @@ void
 TextInputHandler::InsertText(NSAttributedString *aAttrString)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (Destroyed()) {
     return;
   }
 
-  KeyEventState* currentKeyEvent = GetCurrentKeyEvent();
-
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TextInputHandler::InsertText, aAttrString=\"%s\", "
      "IsIMEComposing()=%s, IgnoreIMEComposition()=%s, "
      "keyevent=%p, keypressDispatched=%s",
      this, GetCharacters([aAttrString string]), TrueOrFalse(IsIMEComposing()),
-     TrueOrFalse(IgnoreIMEComposition()),
-     currentKeyEvent ? currentKeyEvent->mKeyEvent : nsnull,
-     currentKeyEvent ?
-       TrueOrFalse(currentKeyEvent->mKeyPressDispatched) : "N/A"));
+     TrueOrFalse(IgnoreIMEComposition()), mCurrentKeyEvent.mKeyEvent,
+     TrueOrFalse(mCurrentKeyEvent.mKeyPressDispatched)));
 
   if (IgnoreIMEComposition()) {
     return;
   }
 
   nsString str;
   nsCocoaUtils::GetStringForNSString([aAttrString string], str);
   if (!IsIMEComposing() && str.IsEmpty()) {
@@ -1313,17 +1332,17 @@ TextInputHandler::InsertText(NSAttribute
 
   if (str.Length() != 1 || IsIMEComposing()) {
     InsertTextAsCommittingComposition(aAttrString);
     return;
   }
 
   // Don't let the same event be fired twice when hitting
   // enter/return! (Bug 420502)
-  if (currentKeyEvent && currentKeyEvent->mKeyPressDispatched) {
+  if (mCurrentKeyEvent.mKeyPressDispatched) {
     return;
   }
 
   nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
 
   // Dispatch keypress event with char instead of textEvent
   nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
   keypressEvent.time      = PR_IntervalNow();
@@ -1335,31 +1354,31 @@ TextInputHandler::InsertText(NSAttribute
   // -insertText: they've already been taken into account in creating
   // the input string.
 
   // create event for use by plugins
 #ifndef NP_NO_CARBON
   EventRecord carbonEvent;
 #endif // #ifndef NP_NO_CARBON
 
-  if (currentKeyEvent) {
-    NSEvent* keyEvent = currentKeyEvent->mKeyEvent;
+  if (mCurrentKeyEvent.mKeyEvent) {
+    NSEvent* keyEvent = mCurrentKeyEvent.mKeyEvent;
 
     // XXX The ASCII characters inputting mode of egbridge (Japanese IME)
     // might send the keyDown event with wrong keyboard layout if other
     // keyboard layouts are already loaded. In that case, the native event
     // doesn't match to this gecko event...
 #ifndef NP_NO_CARBON
     if ([mView pluginEventModel] == NPEventModelCarbon) {
       ConvertCocoaKeyEventToCarbonEvent(keyEvent, carbonEvent, true);
       keypressEvent.pluginEvent = &carbonEvent;
     }
 #endif // #ifndef NP_NO_CARBON
 
-    if (currentKeyEvent->mKeyDownHandled) {
+    if (mCurrentKeyEvent.mKeyDownHandled) {
       keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
     }
 
     keypressEvent.isShift = ([keyEvent modifierFlags] & NSShiftKeyMask) != 0;
     if (!IsPrintableChar(keypressEvent.charCode)) {
       keypressEvent.keyCode =
         ComputeGeckoKeyCode([keyEvent keyCode],
                             [keyEvent charactersIgnoringModifiers]);
@@ -1376,59 +1395,34 @@ TextInputHandler::InsertText(NSAttribute
 
   // TODO:
   // If mCurrentKeyEvent.mKeyEvent is null and when we implement textInput
   // event of DOM3 Events, we should dispatch it instead of keypress event.
   bool keyPressHandled = DispatchEvent(keypressEvent);
 
   // Note: mWidget might have become null here. Don't count on it from here on.
 
-  if (currentKeyEvent) {
-    currentKeyEvent->mKeyPressHandled = keyPressHandled;
-    currentKeyEvent->mKeyPressDispatched = true;
+  if (mCurrentKeyEvent.mKeyEvent) {
+    mCurrentKeyEvent.mKeyPressHandled = keyPressHandled;
+    mCurrentKeyEvent.mKeyPressDispatched = true;
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 bool
 TextInputHandler::DoCommandBySelector(const char* aSelector)
 {
-  nsRefPtr<nsChildView> kungFuDeathGrip(mWidget);
-
-  KeyEventState* currentKeyEvent = GetCurrentKeyEvent();
-
   PR_LOG(gLog, PR_LOG_ALWAYS,
     ("%p TextInputHandler::DoCommandBySelector, aSelector=\"%s\", "
-     "Destroyed()=%s, keypressHandled=%s, causedOtherKeyEvents=%s",
+     "Destroyed()=%s, keypressHandled=%s",
      this, aSelector ? aSelector : "", TrueOrFalse(Destroyed()),
-     currentKeyEvent ?
-       TrueOrFalse(currentKeyEvent->mKeyPressHandled) : "N/A",
-     currentKeyEvent ?
-       TrueOrFalse(currentKeyEvent->mCausedOtherKeyEvents) : "N/A"));
-
-  if (currentKeyEvent && !currentKeyEvent->mKeyPressDispatched) {
-    nsKeyEvent keypressEvent(true, NS_KEY_PRESS, mWidget);
-    InitKeyEvent(currentKeyEvent->mKeyEvent, keypressEvent);
-    if (currentKeyEvent->mKeyDownHandled ||
-        currentKeyEvent->mCausedOtherKeyEvents) {
-      keypressEvent.flags |= NS_EVENT_FLAG_NO_DEFAULT;
-    }
-    currentKeyEvent->mKeyPressHandled = DispatchEvent(keypressEvent);
-    currentKeyEvent->mKeyPressDispatched = true;
-    PR_LOG(gLog, PR_LOG_ALWAYS,
-      ("%p TextInputHandler::DoCommandBySelector, keypress event "
-       "dispatched, Destroyed()=%s, keypressHandled=%s",
-       this, TrueOrFalse(Destroyed()),
-       TrueOrFalse(currentKeyEvent->mKeyPressHandled)));
-  }
-
-  return !Destroyed() && currentKeyEvent &&
-         (currentKeyEvent->mKeyPressHandled ||
-          currentKeyEvent->mCausedOtherKeyEvents);
+     TrueOrFalse(mCurrentKeyEvent.mKeyPressHandled)));
+
+  return !Destroyed() && mCurrentKeyEvent.mKeyPressHandled;
 }
 
 
 #pragma mark -
 
 
 /******************************************************************************
  *
--- a/widget/src/windows/TaskbarPreview.cpp
+++ b/widget/src/windows/TaskbarPreview.cpp
@@ -368,27 +368,28 @@ TaskbarPreview::GetWindowHook() {
   nsWindow *window = nsWindow::GetNSWindowPtr(mWnd);
   NS_ASSERTION(window, "Cannot use taskbar previews in an embedded context!");
 
   return window->GetWindowHook();
 }
 
 void
 TaskbarPreview::EnableCustomDrawing(HWND aHWND, bool aEnable) {
+  BOOL enabled = aEnable;
   nsUXThemeData::dwmSetWindowAttributePtr(
       aHWND,
       DWMWA_FORCE_ICONIC_REPRESENTATION,
-      &aEnable,
-      sizeof(aEnable));
+      &enabled,
+      sizeof(enabled));
 
   nsUXThemeData::dwmSetWindowAttributePtr(
       aHWND,
       DWMWA_HAS_ICONIC_BITMAP,
-      &aEnable,
-      sizeof(aEnable));
+      &enabled,
+      sizeof(enabled));
 }
 
 
 nsresult
 TaskbarPreview::UpdateTooltip() {
   NS_ASSERTION(CanMakeTaskbarCalls() && mVisible, "UpdateTooltip called on invisible tab preview");
 
   if (FAILED(mTaskbar->SetThumbnailTooltip(PreviewWindow(), mTooltip.get())))
--- a/widget/src/windows/nsClipboard.cpp
+++ b/widget/src/windows/nsClipboard.cpp
@@ -429,16 +429,18 @@ nsresult nsClipboard::GetNativeDataOffCl
   hres = FillSTGMedium(aDataObject, format, &fe, &stm, TYMED_HGLOBAL);
 
   // Currently this is only handling TYMED_HGLOBAL data
   // For Text, Dibs, Files, and generic data (like HTML)
   if (S_OK == hres) {
     static CLIPFORMAT fileDescriptorFlavorA = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORA ); 
     static CLIPFORMAT fileDescriptorFlavorW = ::RegisterClipboardFormat( CFSTR_FILEDESCRIPTORW ); 
     static CLIPFORMAT fileFlavor = ::RegisterClipboardFormat( CFSTR_FILECONTENTS ); 
+    static CLIPFORMAT preferredDropEffect = ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT);
+
     switch (stm.tymed) {
      case TYMED_HGLOBAL: 
         {
           switch (fe.cfFormat) {
             case CF_TEXT:
               {
                 // Get the data out of the global data handle. The size we return
                 // should not include the null because the other platforms don't
@@ -532,19 +534,27 @@ nsresult nsClipboard::GetNativeDataOffCl
                 PRUint32 allocLen = 0;
                 if ( NS_SUCCEEDED(GetGlobalData(stm.hGlobal, aData, &allocLen)) ) {
                   if ( fe.cfFormat == CF_HTML ) {
                     // CF_HTML is actually UTF8, not unicode, so disregard the assumption
                     // above. We have to check the header for the actual length, and we'll
                     // do that in FindPlatformHTML(). For now, return the allocLen. This
                     // case is mostly to ensure we don't try to call strlen on the buffer.
                     *aLen = allocLen;
+                  } else if (fe.cfFormat == preferredDropEffect) {
+                    // As per the MSDN doc entitled: "Shell Clipboard Formats"
+                    // CFSTR_PREFERREDDROPEFFECT should return a DWORD
+                    // Reference: http://msdn.microsoft.com/en-us/library/bb776902(v=vs.85).aspx
+                    NS_ASSERTION(allocLen == sizeof(DWORD),
+                      "CFSTR_PREFERREDDROPEFFECT should return a DWORD");
+                    *aLen = allocLen;
+                  } else {
+                    *aLen = nsCRT::strlen(reinterpret_cast<PRUnichar*>(*aData)) * 
+                            sizeof(PRUnichar);
                   }
-                  else
-                    *aLen = nsCRT::strlen(reinterpret_cast<PRUnichar*>(*aData)) * sizeof(PRUnichar);
                   result = NS_OK;
                 }
               }
             } break;
           } // switch
         } break;
 
       case TYMED_GDI: 
--- a/widget/src/windows/nsNativeDragTarget.cpp
+++ b/widget/src/windows/nsNativeDragTarget.cpp
@@ -57,17 +57,18 @@ static NS_DEFINE_IID(kIDragServiceIID, N
 // This is cached for Leave notification
 static POINTL gDragLastPoint;
 
 /*
  * class nsNativeDragTarget
  */
 nsNativeDragTarget::nsNativeDragTarget(nsIWidget * aWnd)
   : m_cRef(0), 
-    mEffectsAllowed(DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK), 
+    mEffectsAllowed(DROPEFFECT_MOVE | DROPEFFECT_COPY | DROPEFFECT_LINK),
+    mEffectsPreferred(DROPEFFECT_NONE),
     mTookOwnRef(PR_FALSE), mWindow(aWnd), mDropTargetHelper(nsnull)
 {
   mHWnd = (HWND)mWindow->GetNativeData(NS_NATIVE_WINDOW);
 
   /*
    * Create/Get the DragService that we have implemented
    */
   CallGetService(kCDragServiceCID, &mDragService);
@@ -122,54 +123,62 @@ STDMETHODIMP_(ULONG) nsNativeDragTarget:
   delete this;
   return 0;
 }
 
 void
 nsNativeDragTarget::GetGeckoDragAction(DWORD grfKeyState, LPDWORD pdwEffect,
                                        PRUint32 * aGeckoAction)
 {
-  // If a window is disabled or a modal window is on top of 
-  // it (which implies it is disabled), then we should not allow dropping.
+  // If a window is disabled or a modal window is on top of it
+  // (which implies it is disabled), then we should not allow dropping.
   bool isEnabled;
   if (NS_SUCCEEDED(mWindow->IsEnabled(&isEnabled)) && !isEnabled) {
     *pdwEffect = DROPEFFECT_NONE;
     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE;
+    return;
   }
-  // Only a single effect should be specified outgoing for the parameter pdwEffect.
-  // When Shift and Control are pressed, we should specify a LINK effect.
-  else if (!mMovePreferred && (grfKeyState & MK_CONTROL) && 
-      (grfKeyState & MK_SHIFT) && (mEffectsAllowed & DROPEFFECT_LINK)) {
-    *pdwEffect = DROPEFFECT_LINK;
-    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
+
+  // If the user explicitly uses a modifier key, they want the associated action
+  // Shift + Control -> LINK, Shift -> MOVE, Ctrl -> COPY
+  DWORD desiredEffect = DROPEFFECT_NONE;
+  if ((grfKeyState & MK_CONTROL) && (grfKeyState & MK_SHIFT)) {
+    desiredEffect = DROPEFFECT_LINK;
+  } else if (grfKeyState & MK_SHIFT) {
+    desiredEffect = DROPEFFECT_MOVE;
+  } else if (grfKeyState & MK_CONTROL) {
+    desiredEffect = DROPEFFECT_COPY;
   }
-  // When Shift is pressed we should specify a MOVE effect.
-  else if ((mEffectsAllowed & DROPEFFECT_MOVE) && 
-           (mMovePreferred || (grfKeyState & MK_SHIFT))) {
+
+  // Determine the desired effect from what is allowed and preferred.
+  if (!(desiredEffect &= mEffectsAllowed)) {
+    // No modifier key effect is set which is also allowed, check
+    // the preference of the data.
+    desiredEffect = mEffectsPreferred & mEffectsAllowed;
+    if (!desiredEffect) {
+      // No preference is set, so just fall back to the allowed effect itself
+      desiredEffect = mEffectsAllowed;
+    }
+  }
+
+  // Otherwise we should specify the first available effect 
+  // from MOVE, COPY, or LINK.
+  if (desiredEffect & DROPEFFECT_MOVE) {
     *pdwEffect = DROPEFFECT_MOVE;
     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_MOVE;
-  }
-  // When Control is pressed we should specify a COPY effect.
-  else if ((mEffectsAllowed & DROPEFFECT_COPY) && (grfKeyState & MK_CONTROL)) {
+  } else if (desiredEffect & DROPEFFECT_COPY) {
     *pdwEffect = DROPEFFECT_COPY;
     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY;
-  }
-  // Otherwise we should specify the first available effect from MOVE, COPY, or LINK.
-  else if (mEffectsAllowed & DROPEFFECT_MOVE) {
-    *pdwEffect = DROPEFFECT_MOVE;
-    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_MOVE;
-  }
-  else if (mEffectsAllowed & DROPEFFECT_COPY) {
-    *pdwEffect = DROPEFFECT_COPY;
-    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_COPY;
-  }
-  else if (mEffectsAllowed & DROPEFFECT_LINK) {
+  } else if (desiredEffect & DROPEFFECT_LINK) {
     *pdwEffect = DROPEFFECT_LINK;
     *aGeckoAction = nsIDragService::DRAGDROP_ACTION_LINK;
-  }
+  } else {
+    *pdwEffect = DROPEFFECT_NONE;
+    *aGeckoAction = nsIDragService::DRAGDROP_ACTION_NONE;
+  } 
 }
 
 inline
 bool
 IsKeyDown(char key)
 {
   return GetKeyState(key) < 0;
 }
@@ -270,25 +279,21 @@ nsNativeDragTarget::DragEnter(LPDATAOBJE
   // outside app).
   mDragService->StartDragSession();
 
   void* tempOutData = nsnull;
   PRUint32 tempDataLen = 0;
   nsresult loadResult = nsClipboard::GetNativeDataOffClipboard(
       pIDataSource, 0, ::RegisterClipboardFormat(CFSTR_PREFERREDDROPEFFECT), nsnull, &tempOutData, &tempDataLen);
   if (NS_SUCCEEDED(loadResult) && tempOutData) {
-    NS_ASSERTION(tempDataLen == 2, "Expected word size");
-    WORD preferredEffect = *((WORD*)tempOutData);
-
-    // Mask effect coming from function call with effect preferred by the source.
-    mMovePreferred = (preferredEffect & DROPEFFECT_MOVE) != 0;
-
+    mEffectsPreferred = *((DWORD*)tempOutData);
     nsMemory::Free(tempOutData);
   } else {
-    mMovePreferred = PR_FALSE;
+    // We have no preference if we can't obtain it
+    mEffectsPreferred = DROPEFFECT_NONE;
   }
 
   // Set the native data object into drag service
   //
   // This cast is ok because in the constructor we created a
   // the actual implementation we wanted, so we know this is
   // a nsDragService. It should be a private interface, though.
   nsDragService * winDragService =
--- a/widget/src/windows/nsNativeDragTarget.h
+++ b/widget/src/windows/nsNativeDragTarget.h
@@ -108,17 +108,17 @@ protected:
                    POINTL pt, DWORD* pdwEffect);
   void DispatchDragDropEvent(PRUint32 aType, POINTL pt);
   void AddLinkSupportIfCanBeGenerated(LPDATAOBJECT aIDataSource);
 
   // Native Stuff
   ULONG            m_cRef;      // reference count
   HWND             mHWnd;
   DWORD            mEffectsAllowed;
-  bool             mMovePreferred;
+  DWORD            mEffectsPreferred;
   bool             mTookOwnRef;
 
   // Gecko Stuff
   nsIWidget      * mWindow;
   nsIDragService * mDragService;
 
   // Drag target helper 
   IDropTargetHelper * mDropTargetHelper;