Bug 779923 - implement social activity flyout panel. r=jaws
authorShane Caraveo
Thu, 23 Aug 2012 17:11:02 -0700
changeset 105409 772c3eed0cfb3c568f9d885c8be8fb8249e502a5
parent 105408 5734825a4f1f1c74017261c6d7afacfa2de5094f
child 105410 012b2f6825ee5cdb164306949775b5c5acc79b5f
push id55
push usershu@rfrn.org
push dateThu, 30 Aug 2012 01:33:09 +0000
reviewersjaws
bugs779923
milestone17.0a1
Bug 779923 - implement social activity flyout panel. r=jaws
browser/base/content/browser-social.js
browser/base/content/browser.xul
browser/base/content/test/Makefile.in
browser/base/content/test/browser_social_flyout.js
browser/base/content/test/social_flyout.html
browser/base/content/test/social_sidebar.html
browser/base/content/test/social_worker.js
browser/themes/gnomestripe/browser.css
browser/themes/pinstripe/browser.css
browser/themes/pinstripe/jar.mn
browser/themes/pinstripe/social/panelarrow-down.png
browser/themes/pinstripe/social/panelarrow-horiz.png
browser/themes/pinstripe/social/panelarrow-up.png
browser/themes/winstripe/browser.css
toolkit/components/social/MozSocialAPI.jsm
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -36,16 +36,17 @@ let SocialUI = {
         // Exceptions here sometimes don't get reported properly, report them
         // manually :(
         try {
           this.updateToggleCommand();
           SocialShareButton.updateButtonHiddenState();
           SocialToolbar.updateButtonHiddenState();
           SocialSidebar.updateSidebar();
           SocialChatBar.update();
+          SocialFlyout.unload();
         } catch (e) {
           Components.utils.reportError(e);
           throw e;
         }
         break;
       case "social:ambient-notification-changed":
         SocialToolbar.updateButton();
         break;
@@ -174,16 +175,126 @@ let SocialChatBar = {
       this.chatbar.newChat(aProvider, aURL, aCallback);
   },
   update: function() {
     if (!this.canShow)
       this.chatbar.removeAll();
   }
 }
 
+function sizeSocialPanelToContent(iframe) {
+  // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
+  // Need to handle dynamic sizing
+  let doc = iframe.contentDocument;
+  if (!doc) {
+    return;
+  }
+  // "notif" is an implementation detail that we should get rid of
+  // eventually
+  let body = doc.getElementById("notif") || doc.body;
+  if (!body || !body.firstChild) {
+    return;
+  }
+
+  let [height, width] = [body.firstChild.offsetHeight || 300, 330];
+  iframe.style.width = width + "px";
+  iframe.style.height = height + "px";
+}
+
+let SocialFlyout = {
+  get panel() {
+    return document.getElementById("social-flyout-panel");
+  },
+
+  dispatchPanelEvent: function(name) {
+    let doc = this.panel.firstChild.contentDocument;
+    let evt = doc.createEvent("CustomEvent");
+    evt.initCustomEvent(name, true, true, {});
+    doc.documentElement.dispatchEvent(evt);
+  },
+
+  _createFrame: function() {
+    let panel = this.panel;
+    if (!Social.provider || panel.firstChild)
+      return;
+    // create and initialize the panel for this window
+    let iframe = document.createElement("iframe");
+    iframe.setAttribute("type", "content");
+    iframe.setAttribute("flex", "1");
+    iframe.setAttribute("origin", Social.provider.origin);
+    panel.appendChild(iframe);
+  },
+
+  unload: function() {
+    let panel = this.panel;
+    if (!panel.firstChild)
+      return
+    panel.removeChild(panel.firstChild);
+  },
+
+  onShown: function(aEvent) {
+    let iframe = this.panel.firstChild;
+    iframe.docShell.isActive = true;
+    iframe.docShell.isAppTab = true;
+    if (iframe.contentDocument.readyState == "complete") {
+      this.dispatchPanelEvent("socialFrameShow");
+    } else {
+      // first time load, wait for load and dispatch after load
+      iframe.addEventListener("load", function panelBrowserOnload(e) {
+        iframe.removeEventListener("load", panelBrowserOnload, true);
+        setTimeout(function() {
+          SocialFlyout.dispatchPanelEvent("socialFrameShow");
+        }, 0);
+      }, true);
+    }
+  },
+
+  onHidden: function(aEvent) {
+    this.panel.firstChild.docShell.isActive = false;
+    this.dispatchPanelEvent("socialFrameHide");
+  },
+
+  open: function(aURL, yOffset, aCallback) {
+    if (!Social.provider)
+      return;
+    let panel = this.panel;
+    if (!panel.firstChild)
+      this._createFrame();
+    panel.hidden = false;
+    let iframe = panel.firstChild;
+
+    let src = iframe.getAttribute("src");
+    if (src != aURL) {
+      iframe.addEventListener("load", function documentLoaded() {
+        iframe.removeEventListener("load", documentLoaded, true);
+        sizeSocialPanelToContent(iframe);
+        if (aCallback) {
+          try {
+            aCallback(iframe.contentWindow);
+          } catch(e) {
+            Cu.reportError(e);
+          }
+        }
+      }, true);
+      iframe.setAttribute("src", aURL);
+    }
+    else if (aCallback) {
+      try {
+        aCallback(iframe.contentWindow);
+      } catch(e) {
+        Cu.reportError(e);
+      }
+    }
+
+    sizeSocialPanelToContent(iframe);
+    let anchor = document.getElementById("social-sidebar-browser");
+    panel.openPopup(anchor, "start_before", 0, yOffset, false, false);
+  }
+}
+
 let SocialShareButton = {
   // Called once, after window load, when the Social.provider object is initialized
   init: function SSB_init() {
     this.updateButtonHiddenState();
     this.updateProfileInfo();
   },
 
   updateProfileInfo: function SSB_updateProfileInfo() {
@@ -392,45 +503,24 @@ var SocialToolbar = {
   },
 
   showAmbientPopup: function SocialToolbar_showAmbientPopup(iconContainer) {
     let iconImage = iconContainer.firstChild;
     let panel = document.getElementById("social-notification-panel");
     let notifBox = document.getElementById("social-notification-box");
     let notificationFrame = document.getElementById(iconImage.getAttribute("notificationFrameId"));
 
-    function sizePanelToContent() {
-      // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
-      // Need to handle dynamic sizing
-      let doc = notificationFrame.contentDocument;
-      if (!doc) {
-        return;
-      }
-      // "notif" is an implementation detail that we should get rid of
-      // eventually
-      let body = doc.getElementById("notif") || doc.body;
-      if (!body || !body.firstChild) {
-        return;
-      }
-
-      // Clear dimensions on all browsers so the panel size will
-      // only use the selected browser.
-      let frameIter = notifBox.firstElementChild;
-      while (frameIter) {
-        frameIter.collapsed = (frameIter != notificationFrame);
-        frameIter = frameIter.nextElementSibling;
-      }
-
-      let [height, width] = [body.firstChild.offsetHeight || 300, 330];
-      notificationFrame.style.width = width + "px";
-      notificationFrame.style.height = height + "px";
+    // Clear dimensions on all browsers so the panel size will
+    // only use the selected browser.
+    let frameIter = notifBox.firstElementChild;
+    while (frameIter) {
+      frameIter.collapsed = (frameIter != notificationFrame);
+      frameIter = frameIter.nextElementSibling;
     }
 
-    sizePanelToContent();
-
     function dispatchPanelEvent(name) {
       let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
       evt.initCustomEvent(name, true, true, {});
       notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
     }
 
     panel.addEventListener("popuphidden", function onpopuphiding() {
       panel.removeEventListener("popuphidden", onpopuphiding);
@@ -440,21 +530,23 @@ var SocialToolbar = {
     });
 
     panel.addEventListener("popupshown", function onpopupshown() {
       panel.removeEventListener("popupshown", onpopupshown);
       SocialToolbar.button.setAttribute("open", "true");
       notificationFrame.docShell.isActive = true;
       notificationFrame.docShell.isAppTab = true;
       if (notificationFrame.contentDocument.readyState == "complete") {
+        sizeSocialPanelToContent(notificationFrame);
         dispatchPanelEvent("socialFrameShow");
       } else {
         // first time load, wait for load and dispatch after load
         notificationFrame.addEventListener("load", function panelBrowserOnload(e) {
           notificationFrame.removeEventListener("load", panelBrowserOnload, true);
+          sizeSocialPanelToContent(notificationFrame);
           setTimeout(function() {
             dispatchPanelEvent("socialFrameShow");
           }, 0);
         }, true);
       }
     });
 
     panel.openPopup(iconImage, "bottomcenter topleft", 0, 0, false, false);
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -266,16 +266,23 @@
                 command="Social:UnsharePage"/>
 #endif
       </hbox>
     </panel>
 
     <panel id="social-notification-panel" type="arrow" hidden="true" noautofocus="true">
       <box id="social-notification-box" flex="1"></box>
     </panel>
+    <panel id="social-flyout-panel"
+           onpopupshown="SocialFlyout.onShown()"
+           onpopuphidden="SocialFlyout.onHidden()"
+           type="arrow"
+           hidden="true"
+           noautofocus="true"
+           position="topcenter topright"/>
 
     <menupopup id="inspector-node-popup">
       <menuitem id="inspectorHTMLCopyInner"
                 label="&inspectorHTMLCopyInner.label;"
                 accesskey="&inspectorHTMLCopyInner.accesskey;"
                 command="Inspector:CopyInner"/>
       <menuitem id="inspectorHTMLCopyOuter"
                 label="&inspectorHTMLCopyOuter.label;"
--- a/browser/base/content/test/Makefile.in
+++ b/browser/base/content/test/Makefile.in
@@ -257,22 +257,24 @@ endif
                  browser_middleMouse_inherit.js \
                  redirect_bug623155.sjs \
                  browser_tabDrop.js \
                  browser_lastAccessedTab.js \
                  browser_bug734076.js \
                  browser_social_toolbar.js \
                  browser_social_shareButton.js \
                  browser_social_sidebar.js \
+                 browser_social_flyout.js \
                  browser_social_mozSocial_API.js \
                  browser_social_isVisible.js \
                  browser_social_chatwindow.js \
                  social_panel.html \
                  social_sidebar.html \
                  social_chat.html \
+                 social_flyout.html \
                  social_window.html \
                  social_worker.js \
                  $(NULL)
 
 ifneq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 _BROWSER_FILES += \
 		browser_bug462289.js \
 		$(NULL)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/browser_social_flyout.js
@@ -0,0 +1,48 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+function test() {
+  waitForExplicitFinish();
+
+  let manifest = { // normal provider
+    name: "provider 1",
+    origin: "https://example.com",
+    sidebarURL: "https://example.com/browser/browser/base/content/test/social_sidebar.html",
+    workerURL: "https://example.com/browser/browser/base/content/test/social_worker.js",
+    iconURL: "chrome://branding/content/icon48.png"
+  };
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    runSocialTests(tests, undefined, undefined, finishcb);
+  });
+}
+
+var tests = {
+  testOpenCloseFlyout: function(next) {
+    let panel = document.getElementById("social-flyout-panel");
+    let port = Social.provider.port;
+    ok(port, "provider has a port");
+    port.onmessage = function (e) {
+      let topic = e.data.topic;
+      switch (topic) {
+        case "got-sidebar-message":
+          port.postMessage({topic: "test-flyout-open"});
+          break;
+        case "got-flyout-visibility":
+          if (e.data.result == "hidden") {
+            ok(true, "flyout visibility is 'hidden'");
+            next();
+          } else if (e.data.result == "shown") {
+            ok(true, "flyout visibility is 'shown");
+            panel.hidePopup();
+          }
+          break;
+        case "got-flyout-message":
+          ok(e.data.result == "ok", "got flyout message");
+          break;
+      }
+    }
+    port.postMessage({topic: "test-init"});
+  }
+}
+
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social_flyout.html
@@ -0,0 +1,22 @@
+<html>
+  <head>
+    <meta charset="utf-8">
+    <script>
+      function pingWorker() {
+        var port = navigator.mozSocial.getWorker().port;
+        port.postMessage({topic: "flyout-message", result: "ok"});
+      }
+      window.addEventListener("socialFrameShow", function(e) {
+        var port = navigator.mozSocial.getWorker().port;
+        port.postMessage({topic: "flyout-visibility", result: "shown"});
+      }, false);
+      window.addEventListener("socialFrameHide", function(e) {
+        var port = navigator.mozSocial.getWorker().port;
+        port.postMessage({topic: "flyout-visibility", result: "hidden"});
+      }, false);
+    </script>
+  </head>
+  <body onload="pingWorker();">
+    <p>This is a test social flyout panel.</p>
+  </body>
+</html>
--- a/browser/base/content/test/social_sidebar.html
+++ b/browser/base/content/test/social_sidebar.html
@@ -3,16 +3,19 @@
     <meta charset="utf-8">
     <script>
       var win;
       function pingWorker() {
         var port = navigator.mozSocial.getWorker().port;
         port.onmessage = function(e) {
           var topic = e.data.topic;
           switch (topic) {
+            case "test-flyout-open":
+              navigator.mozSocial.openPanel("social_flyout.html");
+              break;
             case "test-chatbox-open":
               navigator.mozSocial.openChatWindow("social_chat.html", function(chatwin) {
                 port.postMessage({topic: "chatbox-opened", result: chatwin ? "ok" : "failed"});
               });
               break;
             case "test-service-window":
               win = navigator.mozSocial.openServiceWindow("social_window.html", "test-service-window", "width=300,height=300");
               break;
--- a/browser/base/content/test/social_worker.js
+++ b/browser/base/content/test/social_worker.js
@@ -49,16 +49,25 @@ onconnect = function(e) {
         sidebarPort.postMessage({topic:"test-chatbox-open"});
         break;
       case "chatbox-message":
         testPort.postMessage({topic:"got-chatbox-message", result: event.data.result});
         break;
       case "chatbox-visibility":
         testPort.postMessage({topic:"got-chatbox-visibility", result: event.data.result});
         break;
+      case "test-flyout-open":
+        sidebarPort.postMessage({topic:"test-flyout-open"});
+        break;
+      case "flyout-message":
+        testPort.postMessage({topic:"got-flyout-message", result: event.data.result});
+        break;
+      case "flyout-visibility":
+        testPort.postMessage({topic:"got-flyout-visibility", result: event.data.result});
+        break;
       case "social.initialize":
         // This is the workerAPI port, respond and set up a notification icon.
         port.postMessage({topic: "social.initialize-response"});
         let profile = {
           portrait: "https://example.com/portrait.jpg",
           userName: "trickster",
           displayName: "Kuma Lisa",
           profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
--- a/browser/themes/gnomestripe/browser.css
+++ b/browser/themes/gnomestripe/browser.css
@@ -2736,24 +2736,16 @@ stack[anonid=browserStack][responsivemod
   cursor: pointer;
   min-width: 0;
   margin: 0 6px;
 }
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
-#social-notification-panel {
-  min-height: 100px;
-  min-width: 100px;
-  max-height: 600px;
-  max-width: 400px;
-}
-
-
 .chat-status-icon {
   max-height: 16px;
   max-width: 16px;
   padding: 0;
 }
 
 .chat-toolbarbutton {
   -moz-appearance: none;
--- a/browser/themes/pinstripe/browser.css
+++ b/browser/themes/pinstripe/browser.css
@@ -3425,58 +3425,16 @@ stack[anonid=browserStack][responsivemod
 }
 
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
 /* === end of social toolbar provider menu === */
 
-/* === start of social toolbar panels === */
-
-#social-notification-panel {
-  min-height: 100px;
-  min-width: 240px;
-  max-height: 600px;
-  max-width: 400px;
-}
-
-#social-notification-panel .panel-arrowcontent {
-  margin: -4px 0 0 0;
-  padding: 0;
-  border-radius: 0px;
-  background: white;
-}
-
-#social-notification-panel .panel-arrow[side="top"] {
-  list-style-image: url("chrome://browser/skin/social/panelarrow-up.png");
-  margin-top: -4px;
-  margin-bottom: 3px;
-  height: 21px;
-}
-
-#social-notification-panel .panel-arrow[side="bottom"] {
-  list-style-image: url("chrome://browser/skin/social/panelarrow-down.png");
-  margin-top: -5px;
-}
-
-#social-notification-panel .panel-arrow[side="left"] {
-  list-style-image: url("chrome://browser/skin/social/panelarrow-horiz.png");
-  margin-right: -1px;
-  -moz-transform: scaleX(-1);
-}
-
-#social-notification-panel .panel-arrow[side="right"] {
-  list-style-image: url("chrome://browser/skin/social/panelarrow-horiz.png");
-  margin-left: -1px;
-}
-
-/* === end of social toolbar panels === */
-
-
 .chat-status-icon {
   max-height: 16px;
   max-width: 16px;
   padding: 0;
 }
 
 .chat-toolbarbutton {
   -moz-appearance: none;
--- a/browser/themes/pinstripe/jar.mn
+++ b/browser/themes/pinstripe/jar.mn
@@ -105,19 +105,16 @@ browser.jar:
   skin/classic/browser/preferences/Options-sync.png         (preferences/Options-sync.png)
 #endif
   skin/classic/browser/preferences/saveFile.png             (preferences/saveFile.png)
 * skin/classic/browser/preferences/preferences.css          (preferences/preferences.css)
   skin/classic/browser/preferences/in-content/preferences.css (preferences/in-content/preferences.css)
   skin/classic/browser/preferences/applications.css         (preferences/applications.css)
   skin/classic/browser/preferences/aboutPermissions.css     (preferences/aboutPermissions.css)
   skin/classic/browser/social/social.png                    (social/social.png)
-  skin/classic/browser/social/panelarrow-down.png           (social/panelarrow-down.png)
-  skin/classic/browser/social/panelarrow-horiz.png          (social/panelarrow-horiz.png)
-  skin/classic/browser/social/panelarrow-up.png             (social/panelarrow-up.png)
   skin/classic/browser/tabbrowser/alltabs-box-bkgnd-icon.png             (tabbrowser/alltabs-box-bkgnd-icon.png)
   skin/classic/browser/tabbrowser/newtab.png                             (tabbrowser/newtab.png)
   skin/classic/browser/tabbrowser/connecting.png                         (tabbrowser/connecting.png)
   skin/classic/browser/tabbrowser/loading.png                            (tabbrowser/loading.png)
   skin/classic/browser/tabbrowser/tab-arrow-left.png                     (tabbrowser/tab-arrow-left.png)
   skin/classic/browser/tabbrowser/tab-arrow-right.png                    (tabbrowser/tab-arrow-right.png)
   skin/classic/browser/tabbrowser/tabbar-bottom-bg-active.png            (tabbrowser/tabbar-bottom-bg-active.png)
   skin/classic/browser/tabbrowser/tabbar-bottom-bg-inactive.png          (tabbrowser/tabbar-bottom-bg-inactive.png)
deleted file mode 100644
index 0224ebdf153bec5d7125f88bb9494d6d36dfbeae..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index ed59312c14252fe9c7f37e902285d652cbb09359..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 451b83b3b9bfde3ffba768feba91566bea92210d..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
--- a/browser/themes/winstripe/browser.css
+++ b/browser/themes/winstripe/browser.css
@@ -3439,24 +3439,16 @@ stack[anonid=browserStack][responsivemod
   cursor: pointer;
   min-width: 0;
   margin: 0 6px;
 }
 #social-statusarea-username:hover {
   text-decoration: underline;
 }
 
-#social-notification-panel {
-  min-height: 100px;
-  min-width: 100px;
-  max-height: 600px;
-  max-width: 400px;
-}
-
-
 .chat-status-icon {
   max-height: 16px;
   max-width: 16px;
   padding: 0;
 }
 
 .chat-toolbarbutton {
   -moz-appearance: none;
--- a/toolkit/components/social/MozSocialAPI.jsm
+++ b/toolkit/components/social/MozSocialAPI.jsm
@@ -114,16 +114,31 @@ function attachToWindow(provider, target
       enumerable: true,
       configurable: true,
       writable: true,
       value: function(toURL, callback) {
         let url = targetWindow.document.documentURIObject.resolve(toURL);
         openChatWindow(getChromeWindow(targetWindow), provider, url, callback);
       }
     },
+    openPanel: {
+      enumerable: true,
+      configurable: true,
+      writable: true,
+      value: function(toURL, offset, callback) {
+        let chromeWindow = getChromeWindow(targetWindow);
+        if (!chromeWindow.SocialFlyout)
+          return;
+        let url = targetWindow.document.documentURIObject.resolve(toURL);
+        let fullURL = ensureProviderOrigin(provider, url);
+        if (!fullURL)
+          return;
+        chromeWindow.SocialFlyout.open(fullURL, offset, callback);
+      }
+    },
     getAttention: {
       enumerable: true,
       configurable: true,
       writable: true,
       value: function() {
         getChromeWindow(targetWindow).getAttention();
       }
     },