Bug 860421 - Fix intermittent failure in chat tests. r=markh, a=test-only
authorShane Caraveo <scaraveo@mozilla.com>
Thu, 04 Jul 2013 19:21:27 -0700
changeset 148408 e7118e2596b150f07d010c576b035b5b22571a3c
parent 148407 229a2402debc6bc0ee9dcf8f789a99775cc64c1a
child 148409 0082355f37cafd7c405190dfc032813f52b46466
push id2790
push userryanvm@gmail.com
push dateThu, 22 Aug 2013 14:51:41 +0000
treeherdermozilla-beta@e0cc79b0d1e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmarkh, test-only
bugs860421
milestone24.0
Bug 860421 - Fix intermittent failure in chat tests. r=markh, a=test-only
browser/base/content/socialchat.xml
browser/base/content/test/social/Makefile.in
browser/base/content/test/social/browser_social_chatwindow.js
browser/base/content/test/social/browser_social_chatwindow_resize.js
browser/base/content/test/social/head.js
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -615,17 +615,17 @@
         }
         // else: achievement unlocked - we are pixel-perfect!
         ]]></body>
       </method>
 
       <method name="handleEvent">
         <parameter name="aEvent"/>
         <body><![CDATA[
-          if (aEvent.type == "resize") {
+          if (aEvent.type == "resize" && aEvent.eventPhase == aEvent.AT_TARGET) {
             this.resize();
           }
         ]]></body>
       </method>
 
       <method name="_getDragTarget">
         <parameter name="event"/>
         <body><![CDATA[
--- a/browser/base/content/test/social/Makefile.in
+++ b/browser/base/content/test/social/Makefile.in
@@ -22,16 +22,17 @@ MOCHITEST_BROWSER_FILES = \
                  browser_social_perwindowPB.js \
                  browser_social_toolbar.js \
                  browser_social_markButton.js \
                  browser_social_sidebar.js \
                  browser_social_flyout.js \
                  browser_social_mozSocial_API.js \
                  browser_social_isVisible.js \
                  browser_social_chatwindow.js \
+                 browser_social_chatwindow_resize.js \
                  browser_social_chatwindowfocus.js \
                  browser_social_multiprovider.js \
                  browser_social_errorPage.js \
                  browser_social_window.js \
                  social_activate.html \
                  social_activate_iframe.html \
                  browser_share.js \
                  social_panel.html \
--- a/browser/base/content/test/social/browser_social_chatwindow.js
+++ b/browser/base/content/test/social/browser_social_chatwindow.js
@@ -282,57 +282,16 @@ var tests = {
           is(chatbar.selectedChat, second, "second chat is selected");
           closeAllChats();
           next();
         });
       });
     });
   },
 
-  // resize and collapse testing.
-  testBrowserResize: function(next, mode) {
-    let chats = document.getElementById("pinnedchats");
-    let port = Social.provider.getWorkerPort();
-    port.postMessage({topic: "test-init"});
-    get3ChatsForCollapsing(mode || "normal", function(first, second, third) {
-      let chatWidth = chats.getTotalChildWidth(first);
-      ok(chatWidth, "have a chatwidth");
-      let popupWidth = getPopupWidth();
-      ok(popupWidth, "have a popupwidth");
-      info("starting resize tests - each chat's width is " + chatWidth +
-           " and the popup width is " + popupWidth);
-      // Note that due to a difference between "device", "app" and "css" pixels
-      // we allow use 2 pixels as the minimum size difference.
-      resizeAndCheckWidths(first, second, third, [
-        [chatWidth-2, 1, "to < 1 chat width - only last should be visible."],
-        [chatWidth+2, 1, "2 pixels more then one fully exposed (not counting popup) - still only 1."],
-        [chatWidth+popupWidth+2, 1, "2 pixels more than one fully exposed (including popup) - still only 1."],
-        [chatWidth*2-2, 1, "second not showing by 2 pixels (not counting popup) - only 1 exposed."],
-        [chatWidth*2+popupWidth-2, 1, "second not showing by 2 pixelx (including popup) - only 1 exposed."],
-        [chatWidth*2+popupWidth+2, 2, "big enough to fit 2 - nub remains visible as first is still hidden"],
-        [chatWidth*3+popupWidth-2, 2, "one smaller than the size necessary to display all three - first still hidden"],
-        [chatWidth*3+popupWidth+2, 3, "big enough to fit all - all exposed (which removes the nub)"],
-        [chatWidth*3+2, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."],
-        [chatWidth*3-2, 2, "2 pixels less and the first is again collapsed (and the nub re-appears)"],
-        [chatWidth*2+popupWidth+2, 2, "back down to just big enough to fit 2"],
-        [chatWidth*2+popupWidth-2, 1, "back down to just not enough to fit 2"],
-        [chatWidth*3+popupWidth+2, 3, "now a large jump to make all 3 visible (ie, affects 2)"],
-        [chatWidth*1.5, 1, "and a large jump back down to 1 visible (ie, affects 2)"],
-      ], function() {
-        closeAllChats();
-        port.close();
-        next();
-      });
-    });
-  },
-
-  testBrowserResizeMinimized: function(next) {
-    this.testBrowserResize(next, "minimized");
-  },
-
   testShowWhenCollapsed: function(next) {
     let port = Social.provider.getWorkerPort();
     port.postMessage({topic: "test-init"});
     get3ChatsForCollapsing("normal", function(first, second, third) {
       let chatbar = window.SocialChatBar.chatbar;
       chatbar.showChat(first);
       ok(!first.collapsed, "first should no longer be collapsed");
       ok(second.collapsed ||  third.collapsed, false, "one of the others should be collapsed");
@@ -506,164 +465,8 @@ var tests = {
                            next,
                            "chat windows didn't close");
           break;
       }
     }
     port.postMessage({topic: "test-worker-chat", data: chatUrl});
   },
 }
-
-// And lots of helpers for the resize tests.
-function get3ChatsForCollapsing(mode, cb) {
-  // We make one chat, then measure its size.  We then resize the browser to
-  // ensure a second can be created fully visible but a third can not - then
-  // create the other 2.  first will will be collapsed, second fully visible
-  // and the third also visible and the "selected" one.
-  // To make our life easier we don't go via the worker and ports so we get
-  // more control over creation *and* to make the code much simpler.  We
-  // assume the worker/port stuff is individually tested above.
-  let chatbar = window.SocialChatBar.chatbar;
-  let chatWidth = undefined;
-  let num = 0;
-  is(chatbar.childNodes.length, 0, "chatbar starting empty");
-  is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
-
-  makeChat(mode, "first chat", function() {
-    // got the first one.
-    checkPopup();
-    ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
-    // we kinda cheat here and get the width of the first chat, assuming
-    // that all future chats will have the same width when open.
-    chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
-    let desired = chatWidth * 2.5;
-    resizeWindowToChatAreaWidth(desired, function(sizedOk) {
-      ok(sizedOk, "can't do any tests without this width");
-      checkPopup();
-      makeChat(mode, "second chat", function() {
-        is(chatbar.childNodes.length, 2, "now have 2 chats");
-        checkPopup();
-        // and create the third.
-        makeChat(mode, "third chat", function() {
-          is(chatbar.childNodes.length, 3, "now have 3 chats");
-          checkPopup();
-          // XXX - this is a hacky implementation detail around the order of
-          // the chats.  Ideally things would be a little more sane wrt the
-          // other in which the children were created.
-          let second = chatbar.childNodes[2];
-          let first = chatbar.childNodes[1];
-          let third = chatbar.childNodes[0];
-          ok(first.collapsed && !second.collapsed && !third.collapsed, "collapsed state as promised");
-          is(chatbar.selectedChat, third, "third is selected as promised")
-          info("have 3 chats for collapse testing - starting actual test...");
-          cb(first, second, third);
-        }, mode);
-      }, mode);
-    });
-  }, mode);
-}
-
-function makeChat(mode, uniqueid, cb) {
-  const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
-  let provider = Social.provider;
-  window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
-    // we can't callback immediately or we might close the chat during
-    // this event which upsets the implementation - it is only 1/2 way through
-    // handling the load event.
-    chat.document.title = uniqueid;
-    executeSoon(cb);
-  }, mode);
-}
-
-function checkPopup() {
-  // popup only showing if any collapsed popup children.
-  let chatbar = window.SocialChatBar.chatbar;
-  let numCollapsed = 0;
-  for (let chat of chatbar.childNodes) {
-    if (chat.collapsed) {
-      numCollapsed += 1;
-      // and it have a menuitem weakmap
-      is(chatbar.menuitemMap.get(chat).nodeName, "menuitem", "collapsed chat has a menu item");
-    } else {
-      ok(!chatbar.menuitemMap.has(chat), "open chat has no menu item");
-    }
-  }
-  is(chatbar.menupopup.parentNode.collapsed, numCollapsed == 0, "popup matches child collapsed state");
-  is(chatbar.menupopup.childNodes.length, numCollapsed, "popup has correct count of children");
-  // todo - check each individual elt is what we expect?
-}
-
-// Resize the main window so the chat area's boxObject is |desired| wide.
-// Does a callback passing |true| if the window is now big enough or false
-// if we couldn't resize large enough to satisfy the test requirement.
-function resizeWindowToChatAreaWidth(desired, cb) {
-  let current = window.SocialChatBar.chatbar.getBoundingClientRect().width;
-  let delta = desired - current;
-  info("resizing window so chat area is " + desired + " wide, currently it is "
-       + current + ".  Screen avail is " + window.screen.availWidth
-       + ", current outer width is " + window.outerWidth);
-
-  // WTF?  Sometimes we will get fractional values due to the - err - magic
-  // of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference.
-  let widthDeltaCloseEnough = function(d) {
-    return Math.abs(d) < 2;
-  }
-
-  // attempting to resize by (0,0), unsurprisingly, doesn't cause a resize
-  // event - so just callback saying all is well.
-  if (widthDeltaCloseEnough(delta)) {
-    cb(true);
-    return;
-  }
-  // On lo-res screens we may already be maxed out but still smaller than the
-  // requested size, so asking to resize up also will not cause a resize event.
-  // So just callback now saying the test must be skipped.
-  if (window.screen.availWidth - window.outerWidth < delta) {
-    info("skipping this as screen available width is less than necessary");
-    cb(false);
-    return;
-  }
-  // Otherwise we request resize and expect a resize event
-  window.addEventListener("resize", function resize_handler() {
-    // we did resize - but did we get far enough to be able to continue?
-    let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
-    let sizedOk = widthDeltaCloseEnough(newSize - desired);
-    if (!sizedOk) {
-      return;
-    }
-    window.removeEventListener("resize", resize_handler);
-    executeSoon(function() {
-      cb(sizedOk);
-    });
-  });
-  window.resizeBy(delta, 0);
-}
-
-function resizeAndCheckWidths(first, second, third, checks, cb) {
-  if (checks.length == 0) {
-    cb(); // nothing more to check!
-    return;
-  }
-  let [width, numExpectedVisible, why] = checks.shift();
-  info("Check: " + why);
-  info("resizing window to " + width + ", expect " + numExpectedVisible + " visible items");
-  resizeWindowToChatAreaWidth(width, function(sizedOk) {
-    checkPopup();
-    if (sizedOk) {
-      let numVisible = [first, second, third].filter(function(item) !item.collapsed).length;
-      is(numVisible, numExpectedVisible, "correct number of chats visible");
-    }
-    resizeAndCheckWidths(first, second, third, checks, cb);
-  });
-}
-
-function getPopupWidth() {
-  let popup = window.SocialChatBar.chatbar.menupopup;
-  ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
-  let cs = document.defaultView.getComputedStyle(popup.parentNode);
-  let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
-  return popup.parentNode.getBoundingClientRect().width + margins;
-}
-
-function closeAllChats() {
-  let chatbar = window.SocialChatBar.chatbar;
-  chatbar.removeAll();
-}
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_chatwindow_resize.js
@@ -0,0 +1,90 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function test() {
+  requestLongerTimeout(2); // only debug builds seem to need more time...
+  waitForExplicitFinish();
+
+  let manifest = { // normal provider
+    name: "provider 1",
+    origin: "https://example.com",
+    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
+    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+    iconURL: "https://example.com/browser/browser/base/content/test/moz.png",
+    // added for test purposes
+    chatURL: "https://example.com/browser/browser/base/content/test/social/social_chat.html"
+  };
+  let oldwidth = window.outerWidth; // we futz with these, so we restore them
+  let oldleft = window.screenX;
+  window.moveTo(0, window.screenY)
+  let postSubTest = function(cb) {
+    let chats = document.getElementById("pinnedchats");
+    ok(chats.children.length == 0, "no chatty children left behind");
+    cb();
+  };
+
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    port.postMessage({topic: "test-init"});
+    // we require a logged in user for chats, wait for that
+    waitForCondition(function() {
+      let sbrowser = document.getElementById("social-sidebar-browser");
+      return Social.provider &&
+             Social.provider.profile &&
+             Social.provider.profile.displayName &&
+             sbrowser.docShellIsActive;
+    }, function() {
+      // executeSoon to let the browser UI observers run first
+      runSocialTests(tests, undefined, postSubTest, function() {
+        window.moveTo(oldleft, window.screenY)
+        window.resizeTo(oldwidth, window.outerHeight);
+        port.close();
+        finishcb();
+      });
+    },
+    "waitForProviderLoad: provider profile was not set");
+  });
+}
+
+var tests = {
+
+  // resize and collapse testing.
+  testBrowserResize: function(next, mode) {
+    let chats = document.getElementById("pinnedchats");
+    get3ChatsForCollapsing(mode || "normal", function(first, second, third) {
+      let chatWidth = chats.getTotalChildWidth(first);
+      ok(chatWidth, "have a chatwidth");
+      let popupWidth = getPopupWidth();
+      ok(popupWidth, "have a popupwidth");
+      info("starting resize tests - each chat's width is " + chatWidth +
+           " and the popup width is " + popupWidth);
+      // Note that due to a difference between "device", "app" and "css" pixels
+      // we allow use 2 pixels as the minimum size difference.
+      resizeAndCheckWidths(first, second, third, [
+        [chatWidth-2, 1, "to < 1 chat width - only last should be visible."],
+        [chatWidth+2, 1, "2 pixels more then one fully exposed (not counting popup) - still only 1."],
+        [chatWidth+popupWidth+2, 1, "2 pixels more than one fully exposed (including popup) - still only 1."],
+        [chatWidth*2-2, 1, "second not showing by 2 pixels (not counting popup) - only 1 exposed."],
+        [chatWidth*2+popupWidth-2, 1, "second not showing by 2 pixelx (including popup) - only 1 exposed."],
+        [chatWidth*2+popupWidth+2, 2, "big enough to fit 2 - nub remains visible as first is still hidden"],
+        [chatWidth*3+popupWidth-2, 2, "one smaller than the size necessary to display all three - first still hidden"],
+        [chatWidth*3+popupWidth+2, 3, "big enough to fit all - all exposed (which removes the nub)"],
+        [chatWidth*3+2, 3, "now the nub is hidden we can resize back down to chatWidth*3 before overflow."],
+        [chatWidth*3-2, 2, "2 pixels less and the first is again collapsed (and the nub re-appears)"],
+        [chatWidth*2+popupWidth+2, 2, "back down to just big enough to fit 2"],
+        [chatWidth*2+popupWidth-2, 1, "back down to just not enough to fit 2"],
+        [chatWidth*3+popupWidth+2, 3, "now a large jump to make all 3 visible (ie, affects 2)"],
+        [chatWidth*1.5, 1, "and a large jump back down to 1 visible (ie, affects 2)"],
+      ], function() {
+        closeAllChats();
+        next();
+      });
+    });
+  },
+
+  testBrowserResizeMinimized: function(next) {
+    this.testBrowserResize(next);
+  }
+}
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -338,8 +338,185 @@ function selectBrowserTab(tab, callback)
 function loadIntoTab(tab, url, callback) {
   tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
     tab.linkedBrowser.removeEventListener("load", tabLoad, true);
     executeSoon(function() {callback(tab)});
   }, true);
   tab.linkedBrowser.loadURI(url);
 }
 
+
+// chat test help functions
+
+// And lots of helpers for the resize tests.
+function get3ChatsForCollapsing(mode, cb) {
+  // We make one chat, then measure its size.  We then resize the browser to
+  // ensure a second can be created fully visible but a third can not - then
+  // create the other 2.  first will will be collapsed, second fully visible
+  // and the third also visible and the "selected" one.
+  // To make our life easier we don't go via the worker and ports so we get
+  // more control over creation *and* to make the code much simpler.  We
+  // assume the worker/port stuff is individually tested above.
+  let chatbar = window.SocialChatBar.chatbar;
+  let chatWidth = undefined;
+  let num = 0;
+  is(chatbar.childNodes.length, 0, "chatbar starting empty");
+  is(chatbar.menupopup.childNodes.length, 0, "popup starting empty");
+
+  makeChat(mode, "first chat", function() {
+    // got the first one.
+    checkPopup();
+    ok(chatbar.menupopup.parentNode.collapsed, "menu selection isn't visible");
+    // we kinda cheat here and get the width of the first chat, assuming
+    // that all future chats will have the same width when open.
+    chatWidth = chatbar.calcTotalWidthOf(chatbar.selectedChat);
+    let desired = chatWidth * 2.5;
+    resizeWindowToChatAreaWidth(desired, function(sizedOk) {
+      ok(sizedOk, "can't do any tests without this width");
+      checkPopup();
+      makeChat(mode, "second chat", function() {
+        is(chatbar.childNodes.length, 2, "now have 2 chats");
+        checkPopup();
+        // and create the third.
+        makeChat(mode, "third chat", function() {
+          is(chatbar.childNodes.length, 3, "now have 3 chats");
+          checkPopup();
+          // XXX - this is a hacky implementation detail around the order of
+          // the chats.  Ideally things would be a little more sane wrt the
+          // other in which the children were created.
+          let second = chatbar.childNodes[2];
+          let first = chatbar.childNodes[1];
+          let third = chatbar.childNodes[0];
+          ok(first.collapsed && !second.collapsed && !third.collapsed, "collapsed state as promised");
+          is(chatbar.selectedChat, third, "third is selected as promised")
+          info("have 3 chats for collapse testing - starting actual test...");
+          cb(first, second, third);
+        }, mode);
+      }, mode);
+    });
+  }, mode);
+}
+
+function makeChat(mode, uniqueid, cb) {
+  info("making a chat window '" + uniqueid +"'");
+  const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
+  let provider = Social.provider;
+  let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
+    info("chat window has opened");
+    // we can't callback immediately or we might close the chat during
+    // this event which upsets the implementation - it is only 1/2 way through
+    // handling the load event.
+    chat.document.title = uniqueid;
+    executeSoon(cb);
+  }, mode);
+  if (!isOpened) {
+    ok(false, "unable to open chat window, no provider? more failures to come");
+    executeSoon(cb);
+  }
+}
+
+function checkPopup() {
+  // popup only showing if any collapsed popup children.
+  let chatbar = window.SocialChatBar.chatbar;
+  let numCollapsed = 0;
+  for (let chat of chatbar.childNodes) {
+    if (chat.collapsed) {
+      numCollapsed += 1;
+      // and it have a menuitem weakmap
+      is(chatbar.menuitemMap.get(chat).nodeName, "menuitem", "collapsed chat has a menu item");
+    } else {
+      ok(!chatbar.menuitemMap.has(chat), "open chat has no menu item");
+    }
+  }
+  is(chatbar.menupopup.parentNode.collapsed, numCollapsed == 0, "popup matches child collapsed state");
+  is(chatbar.menupopup.childNodes.length, numCollapsed, "popup has correct count of children");
+  // todo - check each individual elt is what we expect?
+}
+// Resize the main window so the chat area's boxObject is |desired| wide.
+// Does a callback passing |true| if the window is now big enough or false
+// if we couldn't resize large enough to satisfy the test requirement.
+function resizeWindowToChatAreaWidth(desired, cb, count = 0) {
+  let current = window.SocialChatBar.chatbar.getBoundingClientRect().width;
+  let delta = desired - current;
+  info(count + ": resizing window so chat area is " + desired + " wide, currently it is "
+       + current + ".  Screen avail is " + window.screen.availWidth
+       + ", current outer width is " + window.outerWidth);
+
+  // WTF?  Sometimes we will get fractional values due to the - err - magic
+  // of DevPointsPerCSSPixel etc, so we allow a couple of pixels difference.
+  let widthDeltaCloseEnough = function(d) {
+    return Math.abs(d) < 2;
+  }
+
+  // attempting to resize by (0,0), unsurprisingly, doesn't cause a resize
+  // event - so just callback saying all is well.
+  if (widthDeltaCloseEnough(delta)) {
+    info(count + ": skipping this as screen width is close enough");
+    executeSoon(function() {
+      cb(true);
+    });
+    return;
+  }
+  // On lo-res screens we may already be maxed out but still smaller than the
+  // requested size, so asking to resize up also will not cause a resize event.
+  // So just callback now saying the test must be skipped.
+  if (window.screen.availWidth - window.outerWidth < delta) {
+    info(count + ": skipping this as screen available width is less than necessary");
+    executeSoon(function() {
+      cb(false);
+    });
+    return;
+  }
+  function resize_handler(event) {
+    // for whatever reason, sometimes we get called twice for different event
+    // phases, only handle one of them.
+    if (event.eventPhase != event.AT_TARGET)
+      return;
+    // we did resize - but did we get far enough to be able to continue?
+    let newSize = window.SocialChatBar.chatbar.getBoundingClientRect().width;
+    let sizedOk = widthDeltaCloseEnough(newSize - desired);
+    if (!sizedOk)
+      return;
+    window.removeEventListener("resize", resize_handler);
+    info(count + ": resized window width is " + newSize);
+    executeSoon(function() {
+      cb(sizedOk);
+    });
+  }
+  // Otherwise we request resize and expect a resize event
+  window.addEventListener("resize", resize_handler);
+  window.resizeBy(delta, 0);
+}
+
+function resizeAndCheckWidths(first, second, third, checks, cb) {
+  if (checks.length == 0) {
+    cb(); // nothing more to check!
+    return;
+  }
+  let count = checks.length;
+  let [width, numExpectedVisible, why] = checks.shift();
+  info("<< Check " + count + ": " + why);
+  info(count + ": " + "resizing window to " + width + ", expect " + numExpectedVisible + " visible items");
+  resizeWindowToChatAreaWidth(width, function(sizedOk) {
+    checkPopup();
+    ok(sizedOk, count+": window resized correctly");
+    if (sizedOk) {
+      let numVisible = [first, second, third].filter(function(item) !item.collapsed).length;
+      is(numVisible, numExpectedVisible, count + ": " + "correct number of chats visible");
+    }
+    info(">> Check " + count);
+    resizeAndCheckWidths(first, second, third, checks, cb);
+  }, count);
+}
+
+function getPopupWidth() {
+  let popup = window.SocialChatBar.chatbar.menupopup;
+  ok(!popup.parentNode.collapsed, "asking for popup width when it is visible");
+  let cs = document.defaultView.getComputedStyle(popup.parentNode);
+  let margins = parseInt(cs.marginLeft) + parseInt(cs.marginRight);
+  return popup.parentNode.getBoundingClientRect().width + margins;
+}
+
+function closeAllChats() {
+  let chatbar = window.SocialChatBar.chatbar;
+  chatbar.removeAll();
+}
+