forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			1092 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			1092 lines
		
	
	
	
		
			42 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| // 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/.
 | |
| 
 | |
| // The minimum sizes for the auto-resize panel code.
 | |
| const PANEL_MIN_HEIGHT = 100;
 | |
| const PANEL_MIN_WIDTH = 330;
 | |
| 
 | |
| let SocialUI = {
 | |
|   // Called on delayed startup to initialize UI
 | |
|   init: function SocialUI_init() {
 | |
|     Services.obs.addObserver(this, "social:pref-changed", false);
 | |
|     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
 | |
|     Services.obs.addObserver(this, "social:profile-changed", false);
 | |
|     Services.obs.addObserver(this, "social:recommend-info-changed", false);
 | |
|     Services.obs.addObserver(this, "social:frameworker-error", false);
 | |
| 
 | |
|     Services.prefs.addObserver("social.sidebar.open", this, false);
 | |
|     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 | |
|     Services.prefs.addObserver("social.active", this, false);
 | |
| 
 | |
|     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler, true, true);
 | |
| 
 | |
|     // Called when we enter DOM full-screen mode.
 | |
|     window.addEventListener("mozfullscreenchange", function () {
 | |
|       SocialSidebar.update();
 | |
|       SocialChatBar.update();
 | |
|     });
 | |
| 
 | |
|     this.updateActiveBroadcaster();
 | |
|     Social.init(this._providerReady.bind(this));
 | |
|   },
 | |
| 
 | |
|   // Called on window unload
 | |
|   uninit: function SocialUI_uninit() {
 | |
|     Services.obs.removeObserver(this, "social:pref-changed");
 | |
|     Services.obs.removeObserver(this, "social:ambient-notification-changed");
 | |
|     Services.obs.removeObserver(this, "social:profile-changed");
 | |
|     Services.obs.removeObserver(this, "social:recommend-info-changed");
 | |
|     Services.obs.removeObserver(this, "social:frameworker-error");
 | |
| 
 | |
|     Services.prefs.removeObserver("social.sidebar.open", this);
 | |
|     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
 | |
|     Services.prefs.removeObserver("social.active", this);
 | |
|   },
 | |
| 
 | |
|   showProfile: function SocialUI_showProfile() {
 | |
|     if (this.haveLoggedInUser())
 | |
|       openUILinkIn(Social.provider.profile.profileURL, "tab");
 | |
|     else {
 | |
|       // XXX Bug 789585 will implement an API for provider-specified login pages.
 | |
|       openUILinkIn(Social.provider.origin, "tab");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   observe: function SocialUI_observe(subject, topic, data) {
 | |
|     switch (topic) {
 | |
|       case "social:pref-changed":
 | |
|         // Exceptions here sometimes don't get reported properly, report them
 | |
|         // manually :(
 | |
|         try {
 | |
|           this.updateToggleCommand();
 | |
|           SocialShareButton.updateButtonHiddenState();
 | |
|           SocialToolbar.updateButtonHiddenState();
 | |
|           SocialSidebar.update();
 | |
|           SocialChatBar.update();
 | |
|           SocialFlyout.unload();
 | |
|           SocialMenu.populate();
 | |
|         } catch (e) {
 | |
|           Components.utils.reportError(e);
 | |
|           throw e;
 | |
|         }
 | |
|         break;
 | |
|       case "social:ambient-notification-changed":
 | |
|         SocialToolbar.updateButton();
 | |
|         SocialMenu.populate();
 | |
|         break;
 | |
|       case "social:profile-changed":
 | |
|         SocialToolbar.updateProfile();
 | |
|         SocialShareButton.updateProfileInfo();
 | |
|         SocialChatBar.update();
 | |
|         break;
 | |
|       case "social:recommend-info-changed":
 | |
|         SocialShareButton.updateShareState();
 | |
|         break;
 | |
|       case "social:frameworker-error":
 | |
|         if (Social.provider) {
 | |
|           Social.errorState = "frameworker-error";
 | |
|           SocialSidebar.setSidebarErrorMessage("frameworker-error");
 | |
|         }
 | |
|         break;
 | |
|       case "nsPref:changed":
 | |
|         this.updateActiveBroadcaster();
 | |
|         this.updateToggleCommand();
 | |
|         SocialSidebar.update();
 | |
|         SocialToolbar.updateButton();
 | |
|         SocialMenu.populate();
 | |
|         break;
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   get toggleCommand() {
 | |
|     return document.getElementById("Social:Toggle");
 | |
|   },
 | |
| 
 | |
|   // Called once Social.jsm's provider has been set
 | |
|   _providerReady: function SocialUI_providerReady() {
 | |
|     // If we couldn't find a provider, nothing to do here.
 | |
|     if (!Social.provider)
 | |
|       return;
 | |
| 
 | |
|     this.updateToggleCommand();
 | |
| 
 | |
|     // The View->Sidebar and Menubar->Tools menu.
 | |
|     for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
 | |
|       document.getElementById(id).setAttribute("label", Social.provider.name);
 | |
| 
 | |
|     SocialToolbar.init();
 | |
|     SocialShareButton.init();
 | |
|     SocialSidebar.init();
 | |
|     SocialMenu.populate();
 | |
|   },
 | |
| 
 | |
|   updateToggleCommand: function SocialUI_updateToggleCommand() {
 | |
|     if (!Social.provider)
 | |
|       return;
 | |
| 
 | |
|     let toggleCommand = this.toggleCommand;
 | |
|     // We only need to update the command itself - all our menu items use it.
 | |
|     let enabled = Services.prefs.getBoolPref("social.enabled");
 | |
|     let label = gNavigatorBundle.getFormattedString(enabled ? "social.turnOff.label" : "social.turnOn.label",
 | |
|                                                     [Social.provider.name]);
 | |
|     let accesskey = gNavigatorBundle.getString(enabled ? "social.turnOff.accesskey" : "social.turnOn.accesskey");
 | |
|     toggleCommand.setAttribute("label", label);
 | |
|     toggleCommand.setAttribute("accesskey", accesskey);
 | |
|     toggleCommand.setAttribute("hidden", Social.active ? "false" : "true");
 | |
|   },
 | |
| 
 | |
|   updateActiveBroadcaster: function SocialUI_updateActiveBroadcaster() {
 | |
|     let broadcaster = document.getElementById("socialActiveBroadcaster");
 | |
|     broadcaster.hidden = !Social.active;
 | |
|   },
 | |
| 
 | |
|   // This handles "ActivateSocialFeature" events fired against content documents
 | |
|   // in this window.
 | |
|   _activationEventHandler: function SocialUI_activationHandler(e) {
 | |
|     // Nothing to do if Social is already enabled, or we don't have a provider
 | |
|     // to enable yet.
 | |
|     if (Social.enabled || !Social.provider)
 | |
|       return;
 | |
| 
 | |
|     let targetDoc = e.target;
 | |
| 
 | |
|     // Event must be fired against the document
 | |
|     if (!(targetDoc instanceof HTMLDocument))
 | |
|       return;
 | |
| 
 | |
|     // Ignore events fired in background tabs
 | |
|     if (targetDoc.defaultView.top != content)
 | |
|       return;
 | |
| 
 | |
|     // Check that the associated document's origin is in our whitelist
 | |
|     let prePath = targetDoc.documentURIObject.prePath;
 | |
|     let whitelist = Services.prefs.getCharPref("social.activation.whitelist");
 | |
|     if (whitelist.split(",").indexOf(prePath) == -1)
 | |
|       return;
 | |
| 
 | |
|     // If the last event was received < 1s ago, ignore this one
 | |
|     let now = Date.now();
 | |
|     if (now - Social.lastEventReceived < 1000)
 | |
|       return;
 | |
|     Social.lastEventReceived = now;
 | |
| 
 | |
|     // Enable the social functionality, and indicate that it was activated
 | |
|     Social.active = true;
 | |
| 
 | |
|     // Show a warning, allow undoing the activation
 | |
|     let description = document.getElementById("social-activation-message");
 | |
|     let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
 | |
|     let message = gNavigatorBundle.getFormattedString("social.activated.description",
 | |
|                                                       [Social.provider.name, brandShortName]);
 | |
|     description.value = message;
 | |
| 
 | |
|     SocialUI.notificationPanel.hidden = false;
 | |
| 
 | |
|     setTimeout(function () {
 | |
|       SocialUI.notificationPanel.openPopup(SocialToolbar.button, "bottomcenter topright");
 | |
|     }.bind(this), 0);
 | |
|   },
 | |
| 
 | |
|   get notificationPanel() {
 | |
|     return document.getElementById("socialActivatedNotification")
 | |
|   },
 | |
| 
 | |
|   undoActivation: function SocialUI_undoActivation() {
 | |
|     Social.active = false;
 | |
|     this.notificationPanel.hidePopup();
 | |
|   },
 | |
| 
 | |
|   haveLoggedInUser: function SocialUI_haveLoggedInUser() {
 | |
|     return !!(Social.provider && Social.provider.profile && Social.provider.profile.userName);
 | |
|   },
 | |
| 
 | |
|   closeSocialPanelForLinkTraversal: function (target, linkNode) {
 | |
|     // No need to close the panel if this traversal was not retargeted
 | |
|     if (target == "" || target == "_self")
 | |
|       return;
 | |
| 
 | |
|     // Check to see whether this link traversal was in a social panel
 | |
|     let win = linkNode.ownerDocument.defaultView;
 | |
|     let container = win.QueryInterface(Ci.nsIInterfaceRequestor)
 | |
|                                   .getInterface(Ci.nsIWebNavigation)
 | |
|                                   .QueryInterface(Ci.nsIDocShell)
 | |
|                                   .chromeEventHandler;
 | |
|     let containerParent = container.parentNode;
 | |
|     if (containerParent.classList.contains("social-panel") &&
 | |
|         containerParent instanceof Ci.nsIDOMXULPopupElement) {
 | |
|       containerParent.hidePopup();
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   disableWithConfirmation: function SocialUI_disableWithConfirmation() {
 | |
|     let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
 | |
|     let dialogTitle = gNavigatorBundle.getFormattedString("social.remove.confirmationOK",
 | |
|                                                           [Social.provider.name]);
 | |
|     let text = gNavigatorBundle.getFormattedString("social.remove.confirmationLabel",
 | |
|                                                    [Social.provider.name, brandShortName]);
 | |
|     let okButtonText = dialogTitle;
 | |
| 
 | |
|     let ps = Services.prompt;
 | |
|     let flags = ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
 | |
|                 ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1 +
 | |
|                 ps.BUTTON_POS_0_DEFAULT;
 | |
| 
 | |
|     let confirmationIndex = ps.confirmEx(null, dialogTitle, text, flags,
 | |
|                                          okButtonText, null, null, null, {});
 | |
|     if (confirmationIndex == 0)
 | |
|       Social.active = false;
 | |
|   }
 | |
| }
 | |
| 
 | |
| let SocialChatBar = {
 | |
|   get chatbar() {
 | |
|     return document.getElementById("pinnedchats");
 | |
|   },
 | |
|   // Whether the chatbar is available for this window.  Note that in full-screen
 | |
|   // mode chats are available, but not shown.
 | |
|   get isAvailable() {
 | |
|     if (!SocialUI.haveLoggedInUser())
 | |
|       return false;
 | |
|     let docElem = document.documentElement;
 | |
|     let chromeless = docElem.getAttribute("disablechrome") ||
 | |
|                      docElem.getAttribute("chromehidden").indexOf("extrachrome") >= 0;
 | |
|     return Social.uiVisible && !chromeless;
 | |
|   },
 | |
|   openChat: function(aProvider, aURL, aCallback, aMode) {
 | |
|     if (this.isAvailable)
 | |
|       this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
 | |
|   },
 | |
|   update: function() {
 | |
|     if (!this.isAvailable)
 | |
|       this.chatbar.removeAll();
 | |
|     else {
 | |
|       this.chatbar.hidden = document.mozFullScreen;
 | |
|     }
 | |
|   },
 | |
|   focus: function SocialChatBar_focus() {
 | |
|     this.chatbar.focus();
 | |
|   }
 | |
| }
 | |
| 
 | |
| function sizeSocialPanelToContent(panel, iframe) {
 | |
|   // FIXME: bug 764787: Maybe we can use nsIDOMWindowUtils.getRootBounds() here?
 | |
|   let doc = iframe.contentDocument;
 | |
|   if (!doc || !doc.body) {
 | |
|     return;
 | |
|   }
 | |
|   let body = doc.body;
 | |
|   // offsetHeight/Width don't include margins, so account for that.
 | |
|   let cs = doc.defaultView.getComputedStyle(body);
 | |
|   let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom);
 | |
|   let height = Math.max(computedHeight, PANEL_MIN_HEIGHT);
 | |
|   let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight);
 | |
|   let width = Math.max(computedWidth, PANEL_MIN_WIDTH);
 | |
|   let wDiff = width - iframe.getBoundingClientRect().width;
 | |
|   // A panel resize will move the right margin - if that is where the anchor
 | |
|   // arrow is, the arrow will be mis-aligned from the anchor.  So we move the
 | |
|   // popup to compensate for that.  See bug 799014.
 | |
|   if (wDiff !== 0 && panel.getAttribute("side") == "right") {
 | |
|     let box = panel.boxObject;
 | |
|     panel.moveTo(box.screenX - wDiff, box.screenY);
 | |
|   }
 | |
|   iframe.style.height = height + "px";
 | |
|   iframe.style.width = width + "px";
 | |
| }
 | |
| 
 | |
| function DynamicResizeWatcher() {
 | |
|   this._mutationObserver = null;
 | |
| }
 | |
| 
 | |
| DynamicResizeWatcher.prototype = {
 | |
|   start: function DynamicResizeWatcher_start(panel, iframe) {
 | |
|     this.stop(); // just in case...
 | |
|     let doc = iframe.contentDocument;
 | |
|     this._mutationObserver = new iframe.contentWindow.MutationObserver(function(mutations) {
 | |
|       sizeSocialPanelToContent(panel, iframe);
 | |
|     });
 | |
|     // Observe anything that causes the size to change.
 | |
|     let config = {attributes: true, characterData: true, childList: true, subtree: true};
 | |
|     this._mutationObserver.observe(doc, config);
 | |
|     // and since this may be setup after the load event has fired we do an
 | |
|     // initial resize now.
 | |
|     sizeSocialPanelToContent(panel, iframe);
 | |
|   },
 | |
|   stop: function DynamicResizeWatcher_stop() {
 | |
|     if (this._mutationObserver) {
 | |
|       try {
 | |
|         this._mutationObserver.disconnect();
 | |
|       } catch (ex) {
 | |
|         // may get "TypeError: can't access dead object" which seems strange,
 | |
|         // but doesn't seem to indicate a real problem, so ignore it...
 | |
|       }
 | |
|       this._mutationObserver = null;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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("class", "social-panel-frame");
 | |
|     iframe.setAttribute("flex", "1");
 | |
|     iframe.setAttribute("origin", Social.provider.origin);
 | |
|     panel.appendChild(iframe);
 | |
|   },
 | |
| 
 | |
|   setUpProgressListener: function SF_setUpProgressListener() {
 | |
|     if (!this._progressListenerSet) {
 | |
|       this._progressListenerSet = true;
 | |
|       // Force a layout flush by calling .clientTop so
 | |
|       // that the docShell of this frame is created
 | |
|       this.panel.firstChild.clientTop;
 | |
|       this.panel.firstChild.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
 | |
|                                     .getInterface(Ci.nsIWebProgress)
 | |
|                                     .addProgressListener(new SocialErrorListener("flyout"),
 | |
|                                                          Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
 | |
|                                                          Ci.nsIWebProgress.NOTIFY_LOCATION);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   setFlyoutErrorMessage: function SF_setFlyoutErrorMessage() {
 | |
|     let iframe = this.panel.firstChild;
 | |
|     if (!iframe)
 | |
|       return;
 | |
| 
 | |
|     iframe.removeAttribute("src");
 | |
|     iframe.webNavigation.loadURI("about:socialerror?mode=compactInfo", null, null, null, null);
 | |
|     sizeSocialPanelToContent(iframe);
 | |
|   },
 | |
| 
 | |
|   unload: function() {
 | |
|     let panel = this.panel;
 | |
|     panel.hidePopup();
 | |
|     if (!panel.firstChild)
 | |
|       return
 | |
|     panel.removeChild(panel.firstChild);
 | |
|   },
 | |
| 
 | |
|   onShown: function(aEvent) {
 | |
|     let panel = this.panel;
 | |
|     let iframe = panel.firstChild;
 | |
|     this._dynamicResizer = new DynamicResizeWatcher();
 | |
|     iframe.docShell.isActive = true;
 | |
|     iframe.docShell.isAppTab = true;
 | |
|     if (iframe.contentDocument.readyState == "complete") {
 | |
|       this._dynamicResizer.start(panel, iframe);
 | |
|       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._dynamicResizer.start(panel, iframe);
 | |
|           SocialFlyout.dispatchPanelEvent("socialFrameShow");
 | |
|         }, 0);
 | |
|       }, true);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onHidden: function(aEvent) {
 | |
|     this._dynamicResizer.stop();
 | |
|     this._dynamicResizer = null;
 | |
|     this.panel.firstChild.docShell.isActive = false;
 | |
|     this.dispatchPanelEvent("socialFrameHide");
 | |
|   },
 | |
| 
 | |
|   open: function(aURL, yOffset, aCallback) {
 | |
|     // Hide any other social panels that may be open.
 | |
|     document.getElementById("social-notification-panel").hidePopup();
 | |
| 
 | |
|     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);
 | |
|         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(panel, iframe);
 | |
|     let anchor = document.getElementById("social-sidebar-browser");
 | |
|     if (panel.state == "open") {
 | |
|       // this is painful - there is no way to say "move to a new anchor offset",
 | |
|       // only "move to new screen pos".  So we remember the last yOffset,
 | |
|       // calculate the adjustment needed to the new yOffset, then calc the
 | |
|       // screen Y position.
 | |
|       let yAdjust = yOffset - this.yOffset;
 | |
|       let box = panel.boxObject;
 | |
|       panel.moveTo(box.screenX, box.screenY + yAdjust);
 | |
|     } else {
 | |
|       panel.openPopup(anchor, "start_before", 0, yOffset, false, false);
 | |
|       this.setUpProgressListener();
 | |
|     }
 | |
|     this.yOffset = yOffset;
 | |
|   }
 | |
| }
 | |
| 
 | |
| 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() {
 | |
|     let profileRow = document.getElementById("unsharePopupHeader");
 | |
|     let profile = Social.provider.profile;
 | |
|     if (profile && profile.displayName) {
 | |
|       profileRow.hidden = false;
 | |
|       let portrait = document.getElementById("socialUserPortrait");
 | |
|       portrait.setAttribute("src", profile.portrait || "chrome://global/skin/icons/information-32.png");
 | |
|       let displayName = document.getElementById("socialUserDisplayName");
 | |
|       displayName.setAttribute("label", profile.displayName);
 | |
|     } else {
 | |
|       profileRow.hidden = true;
 | |
|       this.updateButtonHiddenState();
 | |
|       return;
 | |
|     }
 | |
|     this.updateShareState();
 | |
|   },
 | |
| 
 | |
|   get shareButton() {
 | |
|     return document.getElementById("share-button");
 | |
|   },
 | |
|   get unsharePopup() {
 | |
|     return document.getElementById("unsharePopup");
 | |
|   },
 | |
| 
 | |
|   dismissUnsharePopup: function SSB_dismissUnsharePopup() {
 | |
|     this.unsharePopup.hidePopup();
 | |
|   },
 | |
| 
 | |
|   canSharePage: function SSB_canSharePage(aURI) {
 | |
|     // We only allow sharing of http or https
 | |
|     return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
 | |
|   },
 | |
| 
 | |
|   updateButtonHiddenState: function SSB_updateButtonHiddenState() {
 | |
|     let shareButton = this.shareButton;
 | |
|     if (shareButton)
 | |
|       shareButton.hidden = !Social.uiVisible || Social.provider.recommendInfo == null ||
 | |
|                            !SocialUI.haveLoggedInUser() ||
 | |
|                            !this.canSharePage(gBrowser.currentURI);
 | |
|   },
 | |
| 
 | |
|   onClick: function SSB_onClick(aEvent) {
 | |
|     if (aEvent.button != 0)
 | |
|       return;
 | |
| 
 | |
|     // Don't bubble to the textbox, to avoid unwanted selection of the address.
 | |
|     aEvent.stopPropagation();
 | |
| 
 | |
|     this.sharePage();
 | |
|   },
 | |
| 
 | |
|   panelShown: function SSB_panelShown(aEvent) {
 | |
|     function updateElement(id, attrs) {
 | |
|       let el = document.getElementById(id);
 | |
|       Object.keys(attrs).forEach(function(attr) {
 | |
|         el.setAttribute(attr, attrs[attr]);
 | |
|       });
 | |
|     }
 | |
|     let continueSharingButton = document.getElementById("unsharePopupContinueSharingButton");
 | |
|     continueSharingButton.focus();
 | |
|     let recommendInfo = Social.provider.recommendInfo;
 | |
|     updateElement("unsharePopupContinueSharingButton",
 | |
|                   {label: recommendInfo.messages.unshareCancelLabel,
 | |
|                    accesskey: recommendInfo.messages.unshareCancelAccessKey});
 | |
|     updateElement("unsharePopupStopSharingButton",
 | |
|                   {label: recommendInfo.messages.unshareConfirmLabel,
 | |
|                   accesskey: recommendInfo.messages.unshareConfirmAccessKey});
 | |
|     updateElement("socialUserPortrait",
 | |
|                   {"aria-label": recommendInfo.messages.portraitLabel});
 | |
|     updateElement("socialUserRecommendedText",
 | |
|                   {value: recommendInfo.messages.unshareLabel});
 | |
|   },
 | |
| 
 | |
|   sharePage: function SSB_sharePage() {
 | |
|     this.unsharePopup.hidden = false;
 | |
| 
 | |
|     let uri = gBrowser.currentURI;
 | |
|     if (!Social.isPageShared(uri)) {
 | |
|       Social.sharePage(uri);
 | |
|       this.updateShareState();
 | |
|     } else {
 | |
|       this.unsharePopup.openPopup(this.shareButton, "bottomcenter topright");
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   unsharePage: function SSB_unsharePage() {
 | |
|     Social.unsharePage(gBrowser.currentURI);
 | |
|     this.updateShareState();
 | |
|     this.dismissUnsharePopup();
 | |
|   },
 | |
| 
 | |
|   updateShareState: function SSB_updateShareState() {
 | |
|     // we might have been called due to a location change, and the new location
 | |
|     // might change the state of "can this url be shared"
 | |
|     this.updateButtonHiddenState();
 | |
| 
 | |
|     let shareButton = this.shareButton;
 | |
|     let currentPageShared = shareButton && !shareButton.hidden && Social.isPageShared(gBrowser.currentURI);
 | |
| 
 | |
|     let recommendInfo = Social.provider ? Social.provider.recommendInfo : null;
 | |
|     // Provide a11y-friendly notification of share.
 | |
|     let status = document.getElementById("share-button-status");
 | |
|     if (status) {
 | |
|       // XXX - this should also be capable of reflecting that the page was
 | |
|       // unshared (ie, it needs to manage three-states: (1) nothing done, (2)
 | |
|       // shared, (3) shared then unshared)
 | |
|       // Note that we *do* have an appropriate string from the provider for
 | |
|       // this (recommendInfo.messages.unsharedLabel) but currently lack a way of
 | |
|       // tracking this state)
 | |
|       let statusString = currentPageShared && recommendInfo ?
 | |
|                            recommendInfo.messages.sharedLabel : "";
 | |
|       status.setAttribute("value", statusString);
 | |
|     }
 | |
| 
 | |
|     // Update the share button, if present
 | |
|     if (!shareButton || shareButton.hidden)
 | |
|       return;
 | |
| 
 | |
|     let imageURL;
 | |
|     if (currentPageShared) {
 | |
|       shareButton.setAttribute("shared", "true");
 | |
|       shareButton.setAttribute("tooltiptext", recommendInfo.messages.unshareTooltip);
 | |
|       imageURL = recommendInfo.images.unshare;
 | |
|     } else {
 | |
|       shareButton.removeAttribute("shared");
 | |
|       shareButton.setAttribute("tooltiptext", recommendInfo.messages.shareTooltip);
 | |
|       imageURL = recommendInfo.images.share;
 | |
|     }
 | |
|     shareButton.src = imageURL;
 | |
|   }
 | |
| };
 | |
| 
 | |
| var SocialMenu = {
 | |
|   populate: function SocialMenu_populate() {
 | |
|     let submenu = document.getElementById("menu_social-statusarea-popup");
 | |
|     let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem");
 | |
|     while (ambientMenuItems.length)
 | |
|       submenu.removeChild(ambientMenuItems.item(0));
 | |
| 
 | |
|     let separator = document.getElementById("socialAmbientMenuSeparator");
 | |
|     separator.hidden = true;
 | |
|     let provider = Social.provider;
 | |
|     if (provider && provider.enabled) {
 | |
|       let iconNames = Object.keys(provider.ambientNotificationIcons);
 | |
|       for (let name of iconNames) {
 | |
|         let icon = provider.ambientNotificationIcons[name];
 | |
|         if (!icon.label || !icon.menuURL)
 | |
|           continue;
 | |
|         separator.hidden = false;
 | |
|         let menuitem = document.createElement("menuitem");
 | |
|         menuitem.setAttribute("label", icon.label);
 | |
|         menuitem.classList.add("ambient-menuitem");
 | |
|         menuitem.addEventListener("command", function() {
 | |
|           openUILinkIn(icon.menuURL, "tab");
 | |
|         }, false);
 | |
|         submenu.insertBefore(menuitem, separator);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| var SocialToolbar = {
 | |
|   // Called once, after window load, when the Social.provider object is initialized
 | |
|   init: function SocialToolbar_init() {
 | |
|     this.button.setAttribute("image", Social.provider.iconURL);
 | |
| 
 | |
|     let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
 | |
|     let label = gNavigatorBundle.getFormattedString("social.remove.label",
 | |
|                                                     [brandShortName]);
 | |
|     let accesskey = gNavigatorBundle.getString("social.remove.accesskey");
 | |
| 
 | |
|     let removeCommand = document.getElementById("Social:Remove");
 | |
|     removeCommand.setAttribute("label", label);
 | |
|     removeCommand.setAttribute("accesskey", accesskey);
 | |
| 
 | |
|     this.updateButton();
 | |
|     this.updateProfile();
 | |
|     this._dynamicResizer = new DynamicResizeWatcher();
 | |
|   },
 | |
| 
 | |
|   get button() {
 | |
|     return document.getElementById("social-provider-button");
 | |
|   },
 | |
| 
 | |
|   updateButtonHiddenState: function SocialToolbar_updateButtonHiddenState() {
 | |
|     let tbi = document.getElementById("social-toolbar-item");
 | |
|     let socialEnabled = Social.enabled;
 | |
|     for (let className of ["social-statusarea-separator", "social-statusarea-user"]) {
 | |
|       for (let element of document.getElementsByClassName(className))
 | |
|         element.hidden = !socialEnabled;
 | |
|     }
 | |
|     let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
 | |
|     toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
 | |
| 
 | |
|     if (!SocialUI.haveLoggedInUser() || !socialEnabled) {
 | |
|       let parent = document.getElementById("social-notification-panel");
 | |
|       while (parent.hasChildNodes())
 | |
|         parent.removeChild(parent.firstChild);
 | |
| 
 | |
|       while (tbi.lastChild != tbi.firstChild)
 | |
|         tbi.removeChild(tbi.lastChild);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   updateProfile: function SocialToolbar_updateProfile() {
 | |
|     // Profile may not have been initialized yet, since it depends on a worker
 | |
|     // response. In that case we'll be called again when it's available, via
 | |
|     // social:profile-changed
 | |
|     let profile = Social.provider.profile || {};
 | |
|     let userPortrait = profile.portrait || "chrome://global/skin/icons/information-32.png";
 | |
| 
 | |
|     let userDetailsBroadcaster = document.getElementById("socialBroadcaster_userDetails");
 | |
|     let loggedInStatusValue = profile.userName ?
 | |
|                               profile.userName :
 | |
|                               userDetailsBroadcaster.getAttribute("notLoggedInLabel");;
 | |
| 
 | |
|     // "image" and "label" are used by Mac's native menus that do not render the menuitem's children
 | |
|     // elements. "src" and "value" are used by the image/label children on the other platforms.
 | |
|     userDetailsBroadcaster.setAttribute("src", userPortrait);
 | |
|     userDetailsBroadcaster.setAttribute("image", userPortrait);
 | |
| 
 | |
|     userDetailsBroadcaster.setAttribute("value", loggedInStatusValue);
 | |
|     userDetailsBroadcaster.setAttribute("label", loggedInStatusValue);
 | |
|   },
 | |
| 
 | |
|   updateButton: function SocialToolbar_updateButton() {
 | |
|     this.updateButtonHiddenState();
 | |
|     let provider = Social.provider;
 | |
|     let icons = provider.ambientNotificationIcons;
 | |
|     let iconNames = Object.keys(icons);
 | |
|     let iconBox = document.getElementById("social-toolbar-item");
 | |
|     let panel = document.getElementById("social-notification-panel");
 | |
|     panel.hidden = false;
 | |
| 
 | |
|     let command = document.getElementById("Social:ToggleNotifications");
 | |
|     command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
 | |
| 
 | |
|     const CACHE_PREF_NAME = "social.cached.notificationIcons";
 | |
|     // provider.profile == undefined means no response yet from the provider
 | |
|     // to tell us whether the user is logged in or not.
 | |
|     if (!Social.provider || !Social.provider.enabled ||
 | |
|         (!SocialUI.haveLoggedInUser() && provider.profile !== undefined)) {
 | |
|       // Either no enabled provider, or there is a provider and it has
 | |
|       // responded with a profile and the user isn't loggedin.  The icons
 | |
|       // etc have already been removed by updateButtonHiddenState, so we want
 | |
|       // to nuke any cached icons we have and get out of here!
 | |
|       Services.prefs.clearUserPref(CACHE_PREF_NAME);
 | |
|       return;
 | |
|     }
 | |
|     if (Social.provider.profile === undefined) {
 | |
|       // provider has not told us about the login state yet - see if we have
 | |
|       // a cached version for this provider.
 | |
|       let cached;
 | |
|       try {
 | |
|         cached = JSON.parse(Services.prefs.getCharPref(CACHE_PREF_NAME));
 | |
|       } catch (ex) {}
 | |
|       if (cached && cached.provider == Social.provider.origin && cached.data) {
 | |
|         icons = cached.data;
 | |
|         iconNames = Object.keys(icons);
 | |
|         // delete the counter data as it is almost certainly stale.
 | |
|         for each(let name in iconNames) {
 | |
|           icons[name].counter = '';
 | |
|         }
 | |
|       }
 | |
|     } else {
 | |
|       // We have a logged in user - save the current set of icons back to the
 | |
|       // "cache" so we can use them next startup.
 | |
|       Services.prefs.setCharPref(CACHE_PREF_NAME,
 | |
|                                  JSON.stringify({provider: Social.provider.origin,
 | |
|                                                  data: icons}));
 | |
|     }
 | |
| 
 | |
|     let notificationFrames = document.createDocumentFragment();
 | |
|     let iconContainers = document.createDocumentFragment();
 | |
| 
 | |
|     let createdFrames = [];
 | |
| 
 | |
|     for each(let name in iconNames) {
 | |
|       let icon = icons[name];
 | |
| 
 | |
|       let notificationFrameId = "social-status-" + icon.name;
 | |
|       let notificationFrame = document.getElementById(notificationFrameId);
 | |
|       if (!notificationFrame) {
 | |
|         notificationFrame = document.createElement("iframe");
 | |
|         notificationFrame.setAttribute("type", "content");
 | |
|         notificationFrame.setAttribute("class", "social-panel-frame");
 | |
|         notificationFrame.setAttribute("id", notificationFrameId);
 | |
|         notificationFrame.setAttribute("mozbrowser", "true");
 | |
|         // work around bug 793057 - by making the panel roughly the final size
 | |
|         // we are more likely to have the anchor in the correct position.
 | |
|         notificationFrame.style.width = PANEL_MIN_WIDTH + "px";
 | |
| 
 | |
|         createdFrames.push(notificationFrame);
 | |
|         notificationFrames.appendChild(notificationFrame);
 | |
|       }
 | |
|       notificationFrame.setAttribute("origin", provider.origin);
 | |
|       if (notificationFrame.getAttribute("src") != icon.contentPanel)
 | |
|         notificationFrame.setAttribute("src", icon.contentPanel);
 | |
| 
 | |
|       let iconId = "social-notification-icon-" + icon.name;
 | |
|       let imageId = iconId + "-image";
 | |
|       let labelId = iconId + "-label";
 | |
|       let stackId = iconId + "-stack";
 | |
|       let stack = document.getElementById(stackId);
 | |
|       let image, label;
 | |
|       if (stack) {
 | |
|         image = document.getElementById(imageId);
 | |
|         label = document.getElementById(labelId);
 | |
|       } else {
 | |
|         let box = document.createElement("box");
 | |
|         box.classList.add("toolbarbutton-1");
 | |
|         box.setAttribute("id", iconId);
 | |
|         // Use the accessibility menuitem label as tooltiptext.
 | |
|         if (icon.label)
 | |
|           box.setAttribute("tooltiptext", icon.label);
 | |
|         box.addEventListener("mousedown", function (e) {
 | |
|           if (e.button == 0)
 | |
|             SocialToolbar.showAmbientPopup(box);
 | |
|         }, false);
 | |
|         box.setAttribute("notificationFrameId", notificationFrameId);
 | |
|         stack = document.createElement("stack");
 | |
|         stack.setAttribute("id", stackId);
 | |
|         stack.classList.add("social-notification-icon-stack");
 | |
|         stack.classList.add("toolbarbutton-icon");
 | |
|         image = document.createElement("image");
 | |
|         image.setAttribute("id", imageId);
 | |
|         image.classList.add("social-notification-icon-image");
 | |
|         image = stack.appendChild(image);
 | |
|         label = document.createElement("label");
 | |
|         label.setAttribute("id", labelId);
 | |
|         label.classList.add("social-notification-icon-label");
 | |
|         let hbox = document.createElement("hbox");
 | |
|         hbox.classList.add("social-notification-icon-hbox");
 | |
|         hbox.setAttribute("align", "start");
 | |
|         hbox.setAttribute("pack", "end");
 | |
|         label = hbox.appendChild(label);
 | |
|         stack.appendChild(hbox);
 | |
|         box.appendChild(stack);
 | |
|         iconContainers.appendChild(box);
 | |
|       }
 | |
| 
 | |
|       let labelValue = icon.counter || "";
 | |
|       // Only update the value attribute if it has changed to reduce layout changes.
 | |
|       if (!label.hasAttribute("value") || label.getAttribute("value") != labelValue)
 | |
|         label.setAttribute("value", labelValue);
 | |
| 
 | |
|       image.style.listStyleImage = "url(" + icon.iconURL + ")";
 | |
|     }
 | |
|     panel.appendChild(notificationFrames);
 | |
|     iconBox.appendChild(iconContainers);
 | |
| 
 | |
|     for (let frame of createdFrames) {
 | |
|       if (frame.docShell) {
 | |
|         frame.docShell.isActive = false;
 | |
|         frame.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
 | |
|                       .getInterface(Ci.nsIWebProgress)
 | |
|                       .addProgressListener(new SocialErrorListener("notification-panel"),
 | |
|                                            Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
 | |
|                                            Ci.nsIWebProgress.NOTIFY_LOCATION);
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   showAmbientPopup: function SocialToolbar_showAmbientPopup(aToolbarButtonBox) {
 | |
|     // Hide any other social panels that may be open.
 | |
|     SocialFlyout.panel.hidePopup();
 | |
| 
 | |
|     let panel = document.getElementById("social-notification-panel");
 | |
|     let notificationFrameId = aToolbarButtonBox.getAttribute("notificationFrameId");
 | |
|     let notificationFrame = document.getElementById(notificationFrameId);
 | |
| 
 | |
|     // Clear dimensions on all browsers so the panel size will
 | |
|     // only use the selected browser.
 | |
|     let frameIter = panel.firstElementChild;
 | |
|     while (frameIter) {
 | |
|       frameIter.collapsed = (frameIter != notificationFrame);
 | |
|       frameIter = frameIter.nextElementSibling;
 | |
|     }
 | |
| 
 | |
|     function dispatchPanelEvent(name) {
 | |
|       let evt = notificationFrame.contentDocument.createEvent("CustomEvent");
 | |
|       evt.initCustomEvent(name, true, true, {});
 | |
|       notificationFrame.contentDocument.documentElement.dispatchEvent(evt);
 | |
|     }
 | |
| 
 | |
|     let dynamicResizer = this._dynamicResizer;
 | |
|     panel.addEventListener("popuphidden", function onpopuphiding() {
 | |
|       panel.removeEventListener("popuphidden", onpopuphiding);
 | |
|       aToolbarButtonBox.removeAttribute("open");
 | |
|       dynamicResizer.stop();
 | |
|       notificationFrame.docShell.isActive = false;
 | |
|       dispatchPanelEvent("socialFrameHide");
 | |
|     });
 | |
| 
 | |
|     panel.addEventListener("popupshown", function onpopupshown() {
 | |
|       panel.removeEventListener("popupshown", onpopupshown);
 | |
|       aToolbarButtonBox.setAttribute("open", "true");
 | |
|       notificationFrame.docShell.isActive = true;
 | |
|       notificationFrame.docShell.isAppTab = true;
 | |
|       if (notificationFrame.contentDocument.readyState == "complete") {
 | |
|         dynamicResizer.start(panel, 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);
 | |
|           dynamicResizer.start(panel, notificationFrame);
 | |
|           setTimeout(function() {
 | |
|             dispatchPanelEvent("socialFrameShow");
 | |
|           }, 0);
 | |
|         }, true);
 | |
|       }
 | |
|     });
 | |
| 
 | |
|     let imageId = aToolbarButtonBox.getAttribute("id") + "-image";
 | |
|     let toolbarButtonImage = document.getElementById(imageId);
 | |
|     panel.openPopup(toolbarButtonImage, "bottomcenter topright", 0, 0, false, false);
 | |
|   },
 | |
| 
 | |
|   setPanelErrorMessage: function SocialToolbar_setPanelErrorMessage(aNotificationFrame) {
 | |
|     if (!aNotificationFrame)
 | |
|       return;
 | |
| 
 | |
|     let src = aNotificationFrame.getAttribute("src");
 | |
|     aNotificationFrame.removeAttribute("src");
 | |
|     aNotificationFrame.webNavigation.loadURI("about:socialerror?mode=tryAgainOnly&url=" +
 | |
|                                              encodeURIComponent(src), null, null, null, null);
 | |
|     sizeSocialPanelToContent(aNotificationFrame);
 | |
|   }
 | |
| }
 | |
| 
 | |
| var SocialSidebar = {
 | |
|   // Called once, after window load, when the Social.provider object is initialized
 | |
|   init: function SocialSidebar_init() {
 | |
|     let sbrowser = document.getElementById("social-sidebar-browser");
 | |
|     this.errorListener = new SocialErrorListener("sidebar");
 | |
|     this.configureSidebarDocShell(sbrowser.docShell);
 | |
|     this.update();
 | |
|   },
 | |
| 
 | |
|   configureSidebarDocShell: function SocialSidebar_configureDocShell(aDocShell) {
 | |
|     // setting isAppTab causes clicks on untargeted links to open new tabs
 | |
|     aDocShell.isAppTab = true;
 | |
|     aDocShell.QueryInterface(Ci.nsIWebProgress)
 | |
|              .addProgressListener(SocialSidebar.errorListener,
 | |
|                                   Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
 | |
|                                   Ci.nsIWebProgress.NOTIFY_LOCATION);
 | |
|   },
 | |
| 
 | |
|   // Whether the sidebar can be shown for this window.
 | |
|   get canShow() {
 | |
|     return Social.uiVisible && Social.provider.sidebarURL && !this.chromeless;
 | |
|   },
 | |
| 
 | |
|   // Whether this is a "chromeless window" (e.g. popup window). We don't show
 | |
|   // the sidebar in these windows.
 | |
|   get chromeless() {
 | |
|     let docElem = document.documentElement;
 | |
|     return docElem.getAttribute('disablechrome') ||
 | |
|            docElem.getAttribute('chromehidden').contains("toolbar");
 | |
|   },
 | |
| 
 | |
|   // Whether the user has toggled the sidebar on (for windows where it can appear)
 | |
|   get opened() {
 | |
|     return Services.prefs.getBoolPref("social.sidebar.open") && !document.mozFullScreen;
 | |
|   },
 | |
| 
 | |
|   setSidebarVisibilityState: function(aEnabled) {
 | |
|     let sbrowser = document.getElementById("social-sidebar-browser");
 | |
|     // it's possible we'll be called twice with aEnabled=false so let's
 | |
|     // just assume we may often be called with the same state.
 | |
|     if (aEnabled == sbrowser.docShellIsActive)
 | |
|       return;
 | |
|     sbrowser.docShellIsActive = aEnabled;
 | |
|     let evt = sbrowser.contentDocument.createEvent("CustomEvent");
 | |
|     evt.initCustomEvent(aEnabled ? "socialFrameShow" : "socialFrameHide", true, true, {});
 | |
|     sbrowser.contentDocument.documentElement.dispatchEvent(evt);
 | |
|   },
 | |
| 
 | |
|   update: function SocialSidebar_update() {
 | |
|     clearTimeout(this._unloadTimeoutId);
 | |
|     // Hide the toggle menu item if the sidebar cannot appear
 | |
|     let command = document.getElementById("Social:ToggleSidebar");
 | |
|     command.setAttribute("hidden", this.canShow ? "false" : "true");
 | |
| 
 | |
|     // Hide the sidebar if it cannot appear, or has been toggled off.
 | |
|     // Also set the command "checked" state accordingly.
 | |
|     let hideSidebar = !this.canShow || !this.opened;
 | |
|     let broadcaster = document.getElementById("socialSidebarBroadcaster");
 | |
|     broadcaster.hidden = hideSidebar;
 | |
|     command.setAttribute("checked", !hideSidebar);
 | |
| 
 | |
|     let sbrowser = document.getElementById("social-sidebar-browser");
 | |
|     if (hideSidebar) {
 | |
|       sbrowser.removeEventListener("load", SocialSidebar._loadListener, true);
 | |
|       this.setSidebarVisibilityState(false);
 | |
|       // If we've been disabled, unload the sidebar content immediately;
 | |
|       // if the sidebar was just toggled to invisible, wait a timeout
 | |
|       // before unloading.
 | |
|       if (!this.canShow) {
 | |
|         this.unloadSidebar();
 | |
|       } else {
 | |
|         this._unloadTimeoutId = setTimeout(
 | |
|           this.unloadSidebar,
 | |
|           Services.prefs.getIntPref("social.sidebar.unload_timeout_ms")
 | |
|         );
 | |
|       }
 | |
|     } else {
 | |
|       if (Social.errorState == "frameworker-error") {
 | |
|         SocialSidebar.setSidebarErrorMessage("frameworker-error");
 | |
|         return;
 | |
|       }
 | |
| 
 | |
|       // Make sure the right sidebar URL is loaded
 | |
|       if (sbrowser.getAttribute("origin") != Social.provider.origin) {
 | |
|         sbrowser.setAttribute("origin", Social.provider.origin);
 | |
|         sbrowser.setAttribute("src", Social.provider.sidebarURL);
 | |
|         sbrowser.addEventListener("load", SocialSidebar._loadListener, true);
 | |
|       } else {
 | |
|         this.setSidebarVisibilityState(true);
 | |
|       }
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   _loadListener: function SocialSidebar_loadListener() {
 | |
|     let sbrowser = document.getElementById("social-sidebar-browser");
 | |
|     sbrowser.removeEventListener("load", SocialSidebar._loadListener, true);
 | |
|     SocialSidebar.setSidebarVisibilityState(true);
 | |
|   },
 | |
| 
 | |
|   unloadSidebar: function SocialSidebar_unloadSidebar() {
 | |
|     let sbrowser = document.getElementById("social-sidebar-browser");
 | |
|     if (!sbrowser.hasAttribute("origin"))
 | |
|       return;
 | |
| 
 | |
|     sbrowser.removeAttribute("origin");
 | |
|     sbrowser.setAttribute("src", "about:blank");
 | |
|     SocialFlyout.unload();
 | |
|   },
 | |
| 
 | |
|   _unloadTimeoutId: 0,
 | |
| 
 | |
|   setSidebarErrorMessage: function(aType) {
 | |
|     let sbrowser = document.getElementById("social-sidebar-browser");
 | |
|     switch (aType) {
 | |
|       case "sidebar-error":
 | |
|         let url = encodeURIComponent(Social.provider.sidebarURL);
 | |
|         sbrowser.loadURI("about:socialerror?mode=tryAgain&url=" + url, null, null);
 | |
|         break;
 | |
| 
 | |
|       case "frameworker-error":
 | |
|         sbrowser.setAttribute("src", "about:socialerror?mode=workerFailure");
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Error handling class used to listen for network errors in the social frames
 | |
| // and replace them with a social-specific error page
 | |
| function SocialErrorListener(aType) {
 | |
|   this.type = aType;
 | |
| }
 | |
| 
 | |
| SocialErrorListener.prototype = {
 | |
|   QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
 | |
|                                          Ci.nsISupportsWeakReference,
 | |
|                                          Ci.nsISupports]),
 | |
| 
 | |
|   onStateChange: function SPL_onStateChange(aWebProgress, aRequest, aState, aStatus) {
 | |
|     let failure = false;
 | |
|     if ((aState & Ci.nsIWebProgressListener.STATE_STOP)) {
 | |
|       if (aRequest instanceof Ci.nsIHttpChannel) {
 | |
|         try {
 | |
|           // Change the frame to an error page on 4xx (client errors)
 | |
|           // and 5xx (server errors)
 | |
|           failure = aRequest.responseStatus >= 400 &&
 | |
|                     aRequest.responseStatus < 600;
 | |
|         } catch (e) {}
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     // Calling cancel() will raise some OnStateChange notifications by itself,
 | |
|     // so avoid doing that more than once
 | |
|     if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
 | |
|       aRequest.cancel(Components.results.NS_BINDING_ABORTED);
 | |
|       this.setErrorMessage(aWebProgress);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onLocationChange: function SPL_onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
 | |
|     let failure = aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE;
 | |
|     if (failure && Social.errorState != "frameworker-error") {
 | |
|       aRequest.cancel(Components.results.NS_BINDING_ABORTED);
 | |
|       window.setTimeout(function(self) {
 | |
|         self.setErrorMessage(aWebProgress);
 | |
|       }, 0, this);
 | |
|     }
 | |
|   },
 | |
| 
 | |
|   onProgressChange: function SPL_onProgressChange() {},
 | |
|   onStatusChange: function SPL_onStatusChange() {},
 | |
|   onSecurityChange: function SPL_onSecurityChange() {},
 | |
| 
 | |
|   setErrorMessage: function(aWebProgress) {
 | |
|     switch (this.type) {
 | |
|       case "flyout":
 | |
|         SocialFlyout.setFlyoutErrorMessage();
 | |
|         break;
 | |
| 
 | |
|       case "sidebar":
 | |
|         SocialSidebar.setSidebarErrorMessage("sidebar-error");
 | |
|         break;
 | |
| 
 | |
|       case "notification-panel":
 | |
|         let frame = aWebProgress.QueryInterface(Ci.nsIDocShell)
 | |
|                                 .chromeEventHandler;
 | |
|         SocialToolbar.setPanelErrorMessage(frame);
 | |
|         break;
 | |
|     }
 | |
|   }
 | |
| };
 | 
