diff options
Diffstat (limited to 'toolkit/mozapps/extensions/content/extensions.xml')
-rw-r--r-- | toolkit/mozapps/extensions/content/extensions.xml | 2008 |
1 files changed, 2008 insertions, 0 deletions
diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml new file mode 100644 index 000000000..b49645cf0 --- /dev/null +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -0,0 +1,2008 @@ +<?xml version="1.0"?> +<!-- 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/. --> + + +<!DOCTYPE page [ +<!ENTITY % extensionsDTD SYSTEM "chrome://mozapps/locale/extensions/extensions.dtd"> +%extensionsDTD; +]> + +<!-- import-globals-from extensions.js --> + +<bindings id="addonBindings" + xmlns="http://www.mozilla.org/xbl" + xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" + xmlns:xbl="http://www.mozilla.org/xbl"> + + + <!-- Rating - displays current/average rating, allows setting user rating --> + <binding id="rating"> + <content> + <xul:image class="star" + onmouseover="document.getBindingParent(this)._hover(1);" + onclick="document.getBindingParent(this).userRating = 1;"/> + <xul:image class="star" + onmouseover="document.getBindingParent(this)._hover(2);" + onclick="document.getBindingParent(this).userRating = 2;"/> + <xul:image class="star" + onmouseover="document.getBindingParent(this)._hover(3);" + onclick="document.getBindingParent(this).userRating = 3;"/> + <xul:image class="star" + onmouseover="document.getBindingParent(this)._hover(4);" + onclick="document.getBindingParent(this).userRating = 4;"/> + <xul:image class="star" + onmouseover="document.getBindingParent(this)._hover(5);" + onclick="document.getBindingParent(this).userRating = 5;"/> + </content> + + <implementation> + <constructor><![CDATA[ + this._updateStars(); + ]]></constructor> + + <property name="stars" readonly="true"> + <getter><![CDATA[ + return document.getAnonymousNodes(this); + ]]></getter> + </property> + + <property name="averageRating"> + <getter><![CDATA[ + if (this.hasAttribute("averagerating")) + return this.getAttribute("averagerating"); + return -1; + ]]></getter> + <setter><![CDATA[ + this.setAttribute("averagerating", val); + if (this.showRating == "average") + this._updateStars(); + ]]></setter> + </property> + + <property name="userRating"> + <getter><![CDATA[ + if (this.hasAttribute("userrating")) + return this.getAttribute("userrating"); + return -1; + ]]></getter> + <setter><![CDATA[ + if (this.showRating != "user") + return; + this.setAttribute("userrating", val); + if (this.showRating == "user") + this._updateStars(); + ]]></setter> + </property> + + <property name="showRating"> + <getter><![CDATA[ + if (this.hasAttribute("showrating")) + return this.getAttribute("showrating"); + return "average"; + ]]></getter> + <setter><![CDATA[ + if (val != "average" || val != "user") + throw Components.Exception("Invalid value", Components.results.NS_ERROR_ILLEGAL_VALUE); + this.setAttribute("showrating", val); + this._updateStars(); + ]]></setter> + </property> + + <method name="_updateStars"> + <body><![CDATA[ + var stars = this.stars; + var rating = this[this.showRating + "Rating"]; + // average ratings can be non-whole numbers, round them so they + // match to their closest star + rating = Math.round(rating); + for (let i = 0; i < stars.length; i++) + stars[i].setAttribute("on", rating > i); + ]]></body> + </method> + + <method name="_hover"> + <parameter name="aScore"/> + <body><![CDATA[ + if (this.showRating != "user") + return; + var stars = this.stars; + for (let i = 0; i < stars.length; i++) + stars[i].setAttribute("on", i <= (aScore -1)); + ]]></body> + </method> + + </implementation> + + <handlers> + <handler event="mouseout"> + this._updateStars(); + </handler> + </handlers> + </binding> + + <!-- Download progress - shows graphical progress of download and any + related status message. --> + <binding id="download-progress"> + <content> + <xul:stack flex="1"> + <xul:hbox flex="1"> + <xul:hbox class="start-cap"/> + <xul:progressmeter anonid="progress" class="progress" flex="1" + min="0" max="100"/> + <xul:hbox class="end-cap"/> + </xul:hbox> + <xul:hbox class="status-container"> + <xul:spacer flex="1"/> + <xul:label anonid="status" class="status"/> + <xul:spacer flex="1"/> + <xul:button anonid="cancel-btn" class="cancel" + tooltiptext="&progress.cancel.tooltip;" + oncommand="document.getBindingParent(this).cancel();"/> + </xul:hbox> + </xul:stack> + </content> + + <implementation> + <constructor><![CDATA[ + var progress = 0; + if (this.hasAttribute("progress")) + progress = parseInt(this.getAttribute("progress")); + this.progress = progress; + ]]></constructor> + + <field name="_progress"> + document.getAnonymousElementByAttribute(this, "anonid", "progress"); + </field> + <field name="_cancel"> + document.getAnonymousElementByAttribute(this, "anonid", "cancel-btn"); + </field> + <field name="_status"> + document.getAnonymousElementByAttribute(this, "anonid", "status"); + </field> + + <property name="progress"> + <getter><![CDATA[ + return this._progress.value; + ]]></getter> + <setter><![CDATA[ + this._progress.value = val; + if (val == this._progress.max) + this.setAttribute("complete", true); + else + this.removeAttribute("complete"); + ]]></setter> + </property> + + <property name="maxProgress"> + <getter><![CDATA[ + return this._progress.max; + ]]></getter> + <setter><![CDATA[ + if (val == -1) { + this._progress.mode = "undetermined"; + } else { + this._progress.mode = "determined"; + this._progress.max = val; + } + this.setAttribute("mode", this._progress.mode); + ]]></setter> + </property> + + <property name="status"> + <getter><![CDATA[ + return this._status.value; + ]]></getter> + <setter><![CDATA[ + this._status.value = val; + ]]></setter> + </property> + + <method name="cancel"> + <body><![CDATA[ + this.mInstall.cancel(); + ]]></body> + </method> + </implementation> + </binding> + + + <!-- Sorters - displays and controls the sort state of a list. --> + <binding id="sorters"> + <content orient="horizontal"> + <xul:button anonid="name-btn" class="sorter" + label="&sort.name.label;" tooltiptext="&sort.name.tooltip;" + oncommand="this.parentNode._handleChange('name');"/> + <xul:button anonid="date-btn" class="sorter" + label="&sort.dateUpdated.label;" + tooltiptext="&sort.dateUpdated.tooltip;" + oncommand="this.parentNode._handleChange('updateDate');"/> + <xul:button anonid="price-btn" class="sorter" hidden="true" + label="&sort.price.label;" + tooltiptext="&sort.price.tooltip;" + oncommand="this.parentNode._handleChange('purchaseAmount');"/> + <xul:button anonid="relevance-btn" class="sorter" hidden="true" + label="&sort.relevance.label;" + tooltiptext="&sort.relevance.tooltip;" + oncommand="this.parentNode._handleChange('relevancescore');"/> + </content> + + <implementation> + <constructor><![CDATA[ + if (!this.hasAttribute("sortby")) + this.setAttribute("sortby", "name"); + + if (this.getAttribute("showrelevance") == "true") + this._btnRelevance.hidden = false; + + if (this.getAttribute("showprice") == "true") + this._btnPrice.hidden = false; + + this._refreshState(); + ]]></constructor> + + <field name="handler">null</field> + <field name="_btnName"> + document.getAnonymousElementByAttribute(this, "anonid", "name-btn"); + </field> + <field name="_btnDate"> + document.getAnonymousElementByAttribute(this, "anonid", "date-btn"); + </field> + <field name="_btnPrice"> + document.getAnonymousElementByAttribute(this, "anonid", "price-btn"); + </field> + <field name="_btnRelevance"> + document.getAnonymousElementByAttribute(this, "anonid", "relevance-btn"); + </field> + + <property name="sortBy"> + <getter><![CDATA[ + return this.getAttribute("sortby"); + ]]></getter> + <setter><![CDATA[ + if (val != this.sortBy) { + this.setAttribute("sortBy", val); + this._refreshState(); + } + ]]></setter> + </property> + + <property name="ascending"> + <getter><![CDATA[ + return (this.getAttribute("ascending") == "true"); + ]]></getter> + <setter><![CDATA[ + val = !!val; + if (val != this.ascending) { + this.setAttribute("ascending", val); + this._refreshState(); + } + ]]></setter> + </property> + + <property name="showrelevance"> + <getter><![CDATA[ + return (this.getAttribute("showrelevance") == "true"); + ]]></getter> + <setter><![CDATA[ + val = !!val; + this.setAttribute("showrelevance", val); + this._btnRelevance.hidden = !val; + ]]></setter> + </property> + + <property name="showprice"> + <getter><![CDATA[ + return (this.getAttribute("showprice") == "true"); + ]]></getter> + <setter><![CDATA[ + val = !!val; + this.setAttribute("showprice", val); + this._btnPrice.hidden = !val; + ]]></setter> + </property> + + <method name="setSort"> + <parameter name="aSort"/> + <parameter name="aAscending"/> + <body><![CDATA[ + var sortChanged = false; + if (aSort != this.sortBy) { + this.setAttribute("sortby", aSort); + sortChanged = true; + } + + aAscending = !!aAscending; + if (this.ascending != aAscending) { + this.setAttribute("ascending", aAscending); + sortChanged = true; + } + + if (sortChanged) + this._refreshState(); + ]]></body> + </method> + + <method name="_handleChange"> + <parameter name="aSort"/> + <body><![CDATA[ + const ASCENDING_SORT_FIELDS = ["name", "purchaseAmount"]; + + // Toggle ascending if sort by is not changing, otherwise + // name sorting defaults to ascending, others to descending + if (aSort == this.sortBy) + this.ascending = !this.ascending; + else + this.setSort(aSort, ASCENDING_SORT_FIELDS.indexOf(aSort) >= 0); + ]]></body> + </method> + + <method name="_refreshState"> + <body><![CDATA[ + var sortBy = this.sortBy; + var checkState = this.ascending ? 2 : 1; + + if (sortBy == "name") { + this._btnName.checkState = checkState; + this._btnName.checked = true; + } else { + this._btnName.checkState = 0; + this._btnName.checked = false; + } + + if (sortBy == "updateDate") { + this._btnDate.checkState = checkState; + this._btnDate.checked = true; + } else { + this._btnDate.checkState = 0; + this._btnDate.checked = false; + } + + if (sortBy == "purchaseAmount") { + this._btnPrice.checkState = checkState; + this._btnPrice.checked = true; + } else { + this._btnPrice.checkState = 0; + this._btnPrice.checked = false; + } + + if (sortBy == "relevancescore") { + this._btnRelevance.checkState = checkState; + this._btnRelevance.checked = true; + } else { + this._btnRelevance.checkState = 0; + this._btnRelevance.checked = false; + } + + if (this.handler && "onSortChanged" in this.handler) + this.handler.onSortChanged(sortBy, this.ascending); + ]]></body> + </method> + </implementation> + </binding> + + + <!-- Categories list - displays the list of categories on the left pane. --> + <binding id="categories-list" + extends="chrome://global/content/bindings/richlistbox.xml#richlistbox"> + <implementation> + <!-- This needs to be overridden to allow the fancy animation while not + allowing that item to be selected when hiding. --> + <method name="_canUserSelect"> + <parameter name="aItem"/> + <body> + <![CDATA[ + if (aItem.hasAttribute("disabled") && + aItem.getAttribute("disabled") == "true") + return false; + var style = document.defaultView.getComputedStyle(aItem, ""); + return style.display != "none" && style.visibility == "visible"; + ]]> + </body> + </method> + </implementation> + </binding> + + + <!-- Category item - an item in the category list. --> + <binding id="category" + extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> + <content align="center"> + <xul:image anonid="icon" class="category-icon"/> + <xul:label anonid="name" class="category-name" flex="1" xbl:inherits="value=name"/> + <xul:label anonid="badge" class="category-badge" xbl:inherits="value=count"/> + </content> + + <implementation> + <constructor><![CDATA[ + if (!this.hasAttribute("count")) + this.setAttribute("count", 0); + ]]></constructor> + + <property name="badgeCount"> + <getter><![CDATA[ + return this.getAttribute("count"); + ]]></getter> + <setter><![CDATA[ + if (this.getAttribute("count") == val) + return; + + this.setAttribute("count", val); + var event = document.createEvent("Events"); + event.initEvent("CategoryBadgeUpdated", true, true); + this.dispatchEvent(event); + ]]></setter> + </property> + </implementation> + </binding> + + + <!-- Creator link - Name of a user/developer, providing a link if relevant. --> + <binding id="creator-link"> + <content> + <xul:label anonid="label" value="&addon.createdBy.label;"/> + <xul:label anonid="creator-link" class="creator-link text-link"/> + <xul:label anonid="creator-name" class="creator-name"/> + </content> + + <implementation> + <constructor><![CDATA[ + if (this.hasAttribute("nameonly") && + this.getAttribute("nameonly") == "true") { + this._label.hidden = true; + } + ]]></constructor> + + <field name="_label"> + document.getAnonymousElementByAttribute(this, "anonid", "label"); + </field> + <field name="_creatorLink"> + document.getAnonymousElementByAttribute(this, "anonid", "creator-link"); + </field> + <field name="_creatorName"> + document.getAnonymousElementByAttribute(this, "anonid", "creator-name"); + </field> + + <method name="setCreator"> + <parameter name="aCreator"/> + <parameter name="aHomepageURL"/> + <body><![CDATA[ + if (!aCreator) { + this.collapsed = true; + return; + } + this.collapsed = false; + var url = aCreator.url || aHomepageURL; + var showLink = !!url; + if (showLink) { + this._creatorLink.value = aCreator.name; + this._creatorLink.href = url; + } else { + this._creatorName.value = aCreator.name; + } + this._creatorLink.hidden = !showLink; + this._creatorName.hidden = showLink; + ]]></body> + </method> + </implementation> + </binding> + + + <!-- Install status - Displays the status of an install/upgrade. --> + <binding id="install-status"> + <content> + <xul:label anonid="message"/> + <xul:progressmeter anonid="progress" class="download-progress"/> + <xul:button anonid="purchase-remote-btn" hidden="true" + class="addon-control" + oncommand="document.getBindingParent(this).purchaseRemote();"/> + <xul:button anonid="install-remote-btn" hidden="true" + class="addon-control install" label="&addon.install.label;" + tooltiptext="&addon.install.tooltip;" + oncommand="document.getBindingParent(this).installRemote();"/> + </content> + + <implementation> + <constructor><![CDATA[ + if (this.mInstall) + this.initWithInstall(this.mInstall); + else if (this.mControl.mAddon.install) + this.initWithInstall(this.mControl.mAddon.install); + else + this.refreshState(); + ]]></constructor> + + <destructor><![CDATA[ + if (this.mInstall) + this.mInstall.removeListener(this); + ]]></destructor> + + <field name="_message"> + document.getAnonymousElementByAttribute(this, "anonid", "message"); + </field> + <field name="_progress"> + document.getAnonymousElementByAttribute(this, "anonid", "progress"); + </field> + <field name="_purchaseRemote"> + document.getAnonymousElementByAttribute(this, "anonid", + "purchase-remote-btn"); + </field> + <field name="_installRemote"> + document.getAnonymousElementByAttribute(this, "anonid", + "install-remote-btn"); + </field> + <field name="_restartNeeded"> + document.getAnonymousElementByAttribute(this, "anonid", + "restart-needed"); + </field> + <field name="_undo"> + document.getAnonymousElementByAttribute(this, "anonid", + "undo-btn"); + </field> + + <method name="initWithInstall"> + <parameter name="aInstall"/> + <body><![CDATA[ + if (this.mInstall) { + this.mInstall.removeListener(this); + this.mInstall = null; + } + this.mInstall = aInstall; + this._progress.mInstall = aInstall; + this.refreshState(); + this.mInstall.addListener(this); + ]]></body> + </method> + + <method name="refreshState"> + <body><![CDATA[ + var showInstallRemote = false; + var showPurchase = false; + + if (this.mInstall) { + + switch (this.mInstall.state) { + case AddonManager.STATE_AVAILABLE: + if (this.mControl.getAttribute("remote") != "true") + break; + + this._progress.hidden = true; + showInstallRemote = true; + break; + case AddonManager.STATE_DOWNLOADING: + this.showMessage("installDownloading"); + break; + case AddonManager.STATE_CHECKING: + this.showMessage("installVerifying"); + break; + case AddonManager.STATE_DOWNLOADED: + this.showMessage("installDownloaded"); + break; + case AddonManager.STATE_DOWNLOAD_FAILED: + // XXXunf expose what error occured (bug 553487) + this.showMessage("installDownloadFailed", true); + break; + case AddonManager.STATE_INSTALLING: + this.showMessage("installInstalling"); + break; + case AddonManager.STATE_INSTALL_FAILED: + // XXXunf expose what error occured (bug 553487) + this.showMessage("installFailed", true); + break; + case AddonManager.STATE_CANCELLED: + this.showMessage("installCancelled", true); + break; + } + + } else if (this.mControl.mAddon.purchaseURL) { + this._progress.hidden = true; + showPurchase = true; + this._purchaseRemote.label = + gStrings.ext.formatStringFromName("addon.purchase.label", + [this.mControl.mAddon.purchaseDisplayAmount], 1); + this._purchaseRemote.tooltiptext = + gStrings.ext.GetStringFromName("addon.purchase.tooltip"); + } + + this._purchaseRemote.hidden = !showPurchase; + this._installRemote.hidden = !showInstallRemote; + + if ("refreshInfo" in this.mControl) + this.mControl.refreshInfo(); + ]]></body> + </method> + + <method name="showMessage"> + <parameter name="aMsgId"/> + <parameter name="aHideProgress"/> + <body><![CDATA[ + this._message.setAttribute("hidden", !aHideProgress); + this._progress.setAttribute("hidden", !!aHideProgress); + + var msg = gStrings.ext.GetStringFromName(aMsgId); + if (aHideProgress) + this._message.value = msg; + else + this._progress.status = msg; + ]]></body> + </method> + + <method name="purchaseRemote"> + <body><![CDATA[ + openURL(this.mControl.mAddon.purchaseURL); + ]]></body> + </method> + + <method name="installRemote"> + <body><![CDATA[ + if (this.mControl.getAttribute("remote") != "true") + return; + + if (this.mControl.mAddon.eula) { + var data = { + addon: this.mControl.mAddon, + accepted: false + }; + window.openDialog("chrome://mozapps/content/extensions/eula.xul", "_blank", + "chrome,dialog,modal,centerscreen,resizable=no", data); + if (!data.accepted) + return; + } + + delete this.mControl.mAddon; + this.mControl.mInstall = this.mInstall; + this.mControl.setAttribute("status", "installing"); + this.mInstall.install(); + ]]></body> + </method> + + <method name="undoAction"> + <body><![CDATA[ + if (!this.mAddon) + return; + var pending = this.mAddon.pendingOperations; + if (pending & AddonManager.PENDING_ENABLE) + this.mAddon.userDisabled = true; + else if (pending & AddonManager.PENDING_DISABLE) + this.mAddon.userDisabled = false; + this.refreshState(); + ]]></body> + </method> + + <method name="onDownloadStarted"> + <body><![CDATA[ + this.refreshState(); + ]]></body> + </method> + + <method name="onDownloadEnded"> + <body><![CDATA[ + this.refreshState(); + ]]></body> + </method> + + <method name="onDownloadFailed"> + <body><![CDATA[ + this.refreshState(); + ]]></body> + </method> + + <method name="onDownloadProgress"> + <body><![CDATA[ + this._progress.maxProgress = this.mInstall.maxProgress; + this._progress.progress = this.mInstall.progress; + ]]></body> + </method> + + <method name="onInstallStarted"> + <body><![CDATA[ + this._progress.progress = 0; + this.refreshState(); + ]]></body> + </method> + + <method name="onInstallEnded"> + <body><![CDATA[ + this.refreshState(); + if ("onInstallCompleted" in this.mControl) + this.mControl.onInstallCompleted(); + ]]></body> + </method> + + <method name="onInstallFailed"> + <body><![CDATA[ + this.refreshState(); + ]]></body> + </method> + </implementation> + </binding> + + + <!-- Addon - base - parent binding of any item representing an addon. --> + <binding id="addon-base" + extends="chrome://global/content/bindings/richlistbox.xml#richlistitem"> + <implementation> + <method name="hasPermission"> + <parameter name="aPerm"/> + <body><![CDATA[ + var perm = AddonManager["PERM_CAN_" + aPerm.toUpperCase()]; + return !!(this.mAddon.permissions & perm); + ]]></body> + </method> + + <method name="opRequiresRestart"> + <parameter name="aOperation"/> + <body><![CDATA[ + var operation = AddonManager["OP_NEEDS_RESTART_" + aOperation.toUpperCase()]; + return !!(this.mAddon.operationsRequiringRestart & operation); + ]]></body> + </method> + + <method name="isPending"> + <parameter name="aAction"/> + <body><![CDATA[ + var action = AddonManager["PENDING_" + aAction.toUpperCase()]; + return !!(this.mAddon.pendingOperations & action); + ]]></body> + </method> + + <method name="typeHasFlag"> + <parameter name="aFlag"/> + <body><![CDATA[ + let flag = AddonManager["TYPE_" + aFlag]; + let type = AddonManager.addonTypes[this.mAddon.type]; + + return !!(type.flags & flag); + ]]></body> + </method> + + <method name="onUninstalled"> + <body><![CDATA[ + this.parentNode.removeChild(this); + ]]></body> + </method> + </implementation> + </binding> + + + <!-- Addon - generic - A normal addon item, or an update to one --> + <binding id="addon-generic" + extends="chrome://mozapps/content/extensions/extensions.xml#addon-base"> + <content> + <xul:hbox anonid="warning-container" + class="warning"> + <xul:image class="warning-icon"/> + <xul:label anonid="warning" flex="1"/> + <xul:label anonid="warning-link" class="text-link"/> + <xul:button anonid="warning-btn" class="button-link" hidden="true"/> + <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap --> + </xul:hbox> + <xul:hbox anonid="error-container" + class="error"> + <xul:image class="error-icon"/> + <xul:label anonid="error" flex="1"/> + <xul:label anonid="error-link" class="text-link" hidden="true"/> + <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap --> + </xul:hbox> + <xul:hbox anonid="pending-container" + class="pending"> + <xul:image class="pending-icon"/> + <xul:label anonid="pending" flex="1"/> + <xul:button anonid="restart-btn" class="button-link" + label="&addon.restartNow.label;" + oncommand="document.getBindingParent(this).restart();"/> + <xul:button anonid="undo-btn" class="button-link" + label="&addon.undoAction.label;" + tooltipText="&addon.undoAction.tooltip;" + oncommand="document.getBindingParent(this).undo();"/> + <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap --> + </xul:hbox> + + <xul:hbox class="content-container" align="center"> + <xul:vbox class="icon-container"> + <xul:image anonid="icon" class="icon"/> + </xul:vbox> + <xul:vbox class="content-inner-container" flex="1"> + <xul:hbox class="basicinfo-container"> + <xul:hbox class="name-container"> + <xul:label anonid="name" class="name" crop="end" flex="1" + tooltip="addonitem-tooltip" xbl:inherits="value=name"/> + <xul:label class="disabled-postfix" value="&addon.disabled.postfix;"/> + <xul:label class="update-postfix" value="&addon.update.postfix;"/> + <xul:spacer flex="5000"/> <!-- Necessary to make the name crop --> + </xul:hbox> + <xul:label anonid="date-updated" class="date-updated" + unknown="&addon.unknownDate;"/> + </xul:hbox> + <xul:hbox class="experiment-container"> + <svg width="6" height="6" viewBox="0 0 6 6" version="1.1" + xmlns="http://www.w3.org/2000/svg" + class="experiment-bullet-container"> + <circle cx="3" cy="3" r="3" class="experiment-bullet"/> + </svg> + <xul:label anonid="experiment-state" class="experiment-state"/> + <xul:label anonid="experiment-time" class="experiment-time"/> + </xul:hbox> + + <xul:hbox class="advancedinfo-container" flex="1"> + <xul:vbox class="description-outer-container" flex="1"> + <xul:hbox class="description-container"> + <xul:label anonid="description" class="description" crop="end" flex="1"/> + <xul:button anonid="details-btn" class="details button-link" + label="&addon.details.label;" + tooltiptext="&addon.details.tooltip;" + oncommand="document.getBindingParent(this).showInDetailView();"/> + <xul:spacer flex="5000"/> <!-- Necessary to make the description crop --> + </xul:hbox> + <xul:vbox anonid="relnotes-container" class="relnotes-container"> + <xul:label class="relnotes-header" value="&addon.releaseNotes.label;"/> + <xul:label anonid="relnotes-loading" value="&addon.loadingReleaseNotes.label;"/> + <xul:label anonid="relnotes-error" hidden="true" + value="&addon.errorLoadingReleaseNotes.label;"/> + <xul:vbox anonid="relnotes" class="relnotes"/> + </xul:vbox> + <xul:hbox class="relnotes-toggle-container"> + <xul:button anonid="relnotes-toggle-btn" class="relnotes-toggle" + hidden="true" label="&cmd.showReleaseNotes.label;" + tooltiptext="&cmd.showReleaseNotes.tooltip;" + showlabel="&cmd.showReleaseNotes.label;" + showtooltip="&cmd.showReleaseNotes.tooltip;" + hidelabel="&cmd.hideReleaseNotes.label;" + hidetooltip="&cmd.hideReleaseNotes.tooltip;" + oncommand="document.getBindingParent(this).toggleReleaseNotes();"/> + </xul:hbox> + </xul:vbox> + </xul:hbox> + </xul:vbox> + <xul:vbox class="status-control-wrapper"> + <xul:hbox class="status-container"> + <xul:hbox anonid="checking-update" hidden="true"> + <xul:image class="spinner"/> + <xul:label value="&addon.checkingForUpdates.label;"/> + </xul:hbox> + <xul:vbox anonid="update-available" class="update-available" + hidden="true"> + <xul:checkbox anonid="include-update" class="include-update" + label="&addon.includeUpdate.label;" checked="true" + oncommand="document.getBindingParent(this).onIncludeUpdateChanged();"/> + <xul:hbox class="update-info-container"> + <xul:label class="update-available-notice" + value="&addon.updateAvailable.label;"/> + <xul:button anonid="update-btn" class="addon-control update" + label="&addon.updateNow.label;" + tooltiptext="&addon.updateNow.tooltip;" + oncommand="document.getBindingParent(this).upgrade();"/> + </xul:hbox> + </xul:vbox> + <xul:hbox anonid="install-status" class="install-status" + hidden="true"/> + </xul:hbox> + <xul:hbox anonid="control-container" class="control-container"> + <xul:button anonid="preferences-btn" + class="addon-control preferences" +#ifdef XP_WIN + label="&cmd.showPreferencesWin.label;" + tooltiptext="&cmd.showPreferencesWin.tooltip;" +#else + label="&cmd.showPreferencesUnix.label;" + tooltiptext="&cmd.showPreferencesUnix.tooltip;" +#endif + oncommand="document.getBindingParent(this).showPreferences();"/> + <xul:button anonid="enable-btn" class="addon-control enable" + label="&cmd.enableAddon.label;" + oncommand="document.getBindingParent(this).userDisabled = false;"/> + <xul:button anonid="disable-btn" class="addon-control disable" + label="&cmd.disableAddon.label;" + oncommand="document.getBindingParent(this).userDisabled = true;"/> + <xul:button anonid="remove-btn" class="addon-control remove" + label="&cmd.uninstallAddon.label;" + oncommand="document.getBindingParent(this).uninstall();"/> + <xul:menulist anonid="state-menulist" + class="addon-control state" + tooltiptext="&cmd.stateMenu.tooltip;"> + <xul:menupopup> + <xul:menuitem anonid="ask-to-activate-menuitem" + class="addon-control" + label="&cmd.askToActivate.label;" + tooltiptext="&cmd.askToActivate.tooltip;" + oncommand="document.getBindingParent(this).userDisabled = AddonManager.STATE_ASK_TO_ACTIVATE;"/> + <xul:menuitem anonid="always-activate-menuitem" + class="addon-control" + label="&cmd.alwaysActivate.label;" + tooltiptext="&cmd.alwaysActivate.tooltip;" + oncommand="document.getBindingParent(this).userDisabled = false;"/> + <xul:menuitem anonid="never-activate-menuitem" + class="addon-control" + label="&cmd.neverActivate.label;" + tooltiptext="&cmd.neverActivate.tooltip;" + oncommand="document.getBindingParent(this).userDisabled = true;"/> + </xul:menupopup> + </xul:menulist> + </xul:hbox> + </xul:vbox> + </xul:hbox> + </content> + + <implementation> + <constructor><![CDATA[ + this._installStatus = document.getAnonymousElementByAttribute(this, "anonid", "install-status"); + this._installStatus.mControl = this; + + this.setAttribute("contextmenu", "addonitem-popup"); + + this._showStatus("none"); + + this._initWithAddon(this.mAddon); + + gEventManager.registerAddonListener(this, this.mAddon.id); + ]]></constructor> + + <destructor><![CDATA[ + gEventManager.unregisterAddonListener(this, this.mAddon.id); + ]]></destructor> + + <field name="_warningContainer"> + document.getAnonymousElementByAttribute(this, "anonid", + "warning-container"); + </field> + <field name="_warning"> + document.getAnonymousElementByAttribute(this, "anonid", + "warning"); + </field> + <field name="_warningLink"> + document.getAnonymousElementByAttribute(this, "anonid", + "warning-link"); + </field> + <field name="_warningBtn"> + document.getAnonymousElementByAttribute(this, "anonid", + "warning-btn"); + </field> + <field name="_errorContainer"> + document.getAnonymousElementByAttribute(this, "anonid", + "error-container"); + </field> + <field name="_error"> + document.getAnonymousElementByAttribute(this, "anonid", + "error"); + </field> + <field name="_errorLink"> + document.getAnonymousElementByAttribute(this, "anonid", + "error-link"); + </field> + <field name="_pendingContainer"> + document.getAnonymousElementByAttribute(this, "anonid", + "pending-container"); + </field> + <field name="_pending"> + document.getAnonymousElementByAttribute(this, "anonid", + "pending"); + </field> + <field name="_infoContainer"> + document.getAnonymousElementByAttribute(this, "anonid", + "info-container"); + </field> + <field name="_info"> + document.getAnonymousElementByAttribute(this, "anonid", + "info"); + </field> + <field name="_experimentState"> + document.getAnonymousElementByAttribute(this, "anonid", "experiment-state"); + </field> + <field name="_experimentTime"> + document.getAnonymousElementByAttribute(this, "anonid", "experiment-time"); + </field> + <field name="_icon"> + document.getAnonymousElementByAttribute(this, "anonid", "icon"); + </field> + <field name="_dateUpdated"> + document.getAnonymousElementByAttribute(this, "anonid", + "date-updated"); + </field> + <field name="_description"> + document.getAnonymousElementByAttribute(this, "anonid", + "description"); + </field> + <field name="_stateMenulist"> + document.getAnonymousElementByAttribute(this, "anonid", + "state-menulist"); + </field> + <field name="_askToActivateMenuitem"> + document.getAnonymousElementByAttribute(this, "anonid", + "ask-to-activate-menuitem"); + </field> + <field name="_alwaysActivateMenuitem"> + document.getAnonymousElementByAttribute(this, "anonid", + "always-activate-menuitem"); + </field> + <field name="_neverActivateMenuitem"> + document.getAnonymousElementByAttribute(this, "anonid", + "never-activate-menuitem"); + </field> + <field name="_preferencesBtn"> + document.getAnonymousElementByAttribute(this, "anonid", + "preferences-btn"); + </field> + <field name="_enableBtn"> + document.getAnonymousElementByAttribute(this, "anonid", + "enable-btn"); + </field> + <field name="_disableBtn"> + document.getAnonymousElementByAttribute(this, "anonid", + "disable-btn"); + </field> + <field name="_removeBtn"> + document.getAnonymousElementByAttribute(this, "anonid", + "remove-btn"); + </field> + <field name="_updateBtn"> + document.getAnonymousElementByAttribute(this, "anonid", + "update-btn"); + </field> + <field name="_controlContainer"> + document.getAnonymousElementByAttribute(this, "anonid", + "control-container"); + </field> + <field name="_installStatus"> + document.getAnonymousElementByAttribute(this, "anonid", + "install-status"); + </field> + <field name="_checkingUpdate"> + document.getAnonymousElementByAttribute(this, "anonid", + "checking-update"); + </field> + <field name="_updateAvailable"> + document.getAnonymousElementByAttribute(this, "anonid", + "update-available"); + </field> + <field name="_includeUpdate"> + document.getAnonymousElementByAttribute(this, "anonid", + "include-update"); + </field> + <field name="_relNotesLoaded">false</field> + <field name="_relNotesToggle"> + document.getAnonymousElementByAttribute(this, "anonid", + "relnotes-toggle-btn"); + </field> + <field name="_relNotesLoading"> + document.getAnonymousElementByAttribute(this, "anonid", + "relnotes-loading"); + </field> + <field name="_relNotesError"> + document.getAnonymousElementByAttribute(this, "anonid", + "relnotes-error"); + </field> + <field name="_relNotesContainer"> + document.getAnonymousElementByAttribute(this, "anonid", + "relnotes-container"); + </field> + <field name="_relNotes"> + document.getAnonymousElementByAttribute(this, "anonid", + "relnotes"); + </field> + + <property name="userDisabled"> + <getter><![CDATA[ + return this.mAddon.userDisabled; + ]]></getter> + <setter><![CDATA[ + this.mAddon.userDisabled = val; + ]]></setter> + </property> + + <property name="includeUpdate"> + <getter><![CDATA[ + return this._includeUpdate.checked && !!this.mManualUpdate; + ]]></getter> + <setter><![CDATA[ + // XXXunf Eventually, we'll want to persist this for individual + // updates - see bug 594619. + this._includeUpdate.checked = !!val; + ]]></setter> + </property> + + <method name="_initWithAddon"> + <parameter name="aAddon"/> + <body><![CDATA[ + this.mAddon = aAddon; + + this._installStatus.mAddon = this.mAddon; + this._updateDates(); + this._updateState(); + + this.setAttribute("name", aAddon.name); + + var iconURL = AddonManager.getPreferredIconURL(aAddon, 48, window); + if (iconURL) + this._icon.src = iconURL; + else + this._icon.src = ""; + + if (this.mAddon.description) + this._description.value = this.mAddon.description; + else + this._description.hidden = true; + + if (!("applyBackgroundUpdates" in this.mAddon) || + (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DISABLE || + (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DEFAULT && + !AddonManager.autoUpdateDefault))) { + AddonManager.getAllInstalls(aInstallsList => { + // This can return after the binding has been destroyed, + // so try to detect that and return early + if (!("onNewInstall" in this)) + return; + for (let install of aInstallsList) { + if (install.existingAddon && + install.existingAddon.id == this.mAddon.id && + install.state == AddonManager.STATE_AVAILABLE) { + this.onNewInstall(install); + this.onIncludeUpdateChanged(); + } + } + }); + } + ]]></body> + </method> + + <method name="_showStatus"> + <parameter name="aType"/> + <body><![CDATA[ + this._controlContainer.hidden = aType != "none" && + !(aType == "update-available" && !this.hasAttribute("upgrade")); + + this._installStatus.hidden = aType != "progress"; + if (aType == "progress") + this._installStatus.refreshState(); + this._checkingUpdate.hidden = aType != "checking-update"; + this._updateAvailable.hidden = aType != "update-available"; + this._relNotesToggle.hidden = !(this.mManualUpdate ? + this.mManualUpdate.releaseNotesURI : + this.mAddon.releaseNotesURI); + ]]></body> + </method> + + <method name="_updateDates"> + <body><![CDATA[ + function formatDate(aDate) { + const locale = Components.classes["@mozilla.org/chrome/chrome-registry;1"] + .getService(Components.interfaces.nsIXULChromeRegistry) + .getSelectedLocale("global", true); + const dtOptions = { year: 'numeric', month: 'long', day: 'numeric' }; + return aDate.toLocaleDateString(locale, dtOptions); + } + + if (this.mAddon.updateDate) + this._dateUpdated.value = formatDate(this.mAddon.updateDate); + else + this._dateUpdated.value = this._dateUpdated.getAttribute("unknown"); + ]]></body> + </method> + + <method name="_updateState"> + <body><![CDATA[ + if (this.parentNode.selectedItem == this) + gViewController.updateCommands(); + + var pending = this.mAddon.pendingOperations; + if (pending != AddonManager.PENDING_NONE) { + this.removeAttribute("notification"); + + pending = null; + const PENDING_OPERATIONS = ["enable", "disable", "install", + "uninstall", "upgrade"]; + for (let op of PENDING_OPERATIONS) { + if (this.isPending(op)) + pending = op; + } + + this.setAttribute("pending", pending); + this._pending.textContent = gStrings.ext.formatStringFromName( + "notification." + pending, + [this.mAddon.name, gStrings.brandShortName], 2 + ); + } else { + this.removeAttribute("pending"); + + var isUpgrade = this.hasAttribute("upgrade"); + var install = this._installStatus.mInstall; + + if (install && install.state == AddonManager.STATE_DOWNLOAD_FAILED) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.downloadError", + [this.mAddon.name], 1 + ); + this._warningBtn.label = gStrings.ext.GetStringFromName("notification.downloadError.retry"); + this._warningBtn.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip"); + this._warningBtn.setAttribute("oncommand", "document.getBindingParent(this).retryInstall();"); + this._warningBtn.hidden = false; + this._warningLink.hidden = true; + } else if (install && install.state == AddonManager.STATE_INSTALL_FAILED) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.installError", + [this.mAddon.name], 1 + ); + this._warningBtn.label = gStrings.ext.GetStringFromName("notification.installError.retry"); + this._warningBtn.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip"); + this._warningBtn.setAttribute("oncommand", "document.getBindingParent(this).retryInstall();"); + this._warningBtn.hidden = false; + this._warningLink.hidden = true; + } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) { + this.setAttribute("notification", "error"); + this._error.textContent = gStrings.ext.formatStringFromName( + "notification.blocked", + [this.mAddon.name], 1 + ); + this._errorLink.value = gStrings.ext.GetStringFromName("notification.blocked.link"); + this._errorLink.href = this.mAddon.blocklistURL; + this._errorLink.hidden = false; + } else if (!isUpgrade && !isCorrectlySigned(this.mAddon) && SIGNING_REQUIRED) { + this.setAttribute("notification", "error"); + this._error.textContent = gStrings.ext.formatStringFromName( + "notification.unsignedAndDisabled", [this.mAddon.name, gStrings.brandShortName], 2 + ); + this._errorLink.value = gStrings.ext.GetStringFromName("notification.unsigned.link"); + this._errorLink.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons"; + this._errorLink.hidden = false; + } else if ((!isUpgrade && !this.mAddon.isCompatible) && (AddonManager.checkCompatibility + || (this.mAddon.blocklistState != Ci.nsIBlocklistService.STATE_SOFTBLOCKED))) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.incompatible", + [this.mAddon.name, gStrings.brandShortName, gStrings.appVersion], 3 + ); + this._warningLink.hidden = true; + this._warningBtn.hidden = true; + } else if (!isUpgrade && !isCorrectlySigned(this.mAddon)) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.unsigned", [this.mAddon.name, gStrings.brandShortName], 2 + ); + this._warningLink.value = gStrings.ext.GetStringFromName("notification.unsigned.link"); + this._warningLink.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "unsigned-addons"; + this._warningLink.hidden = false; + } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.softblocked", + [this.mAddon.name], 1 + ); + this._warningLink.value = gStrings.ext.GetStringFromName("notification.softblocked.link"); + this._warningLink.href = this.mAddon.blocklistURL; + this._warningLink.hidden = false; + this._warningBtn.hidden = true; + } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.outdated", + [this.mAddon.name], 1 + ); + this._warningLink.value = gStrings.ext.GetStringFromName("notification.outdated.link"); + this._warningLink.href = this.mAddon.blocklistURL; + this._warningLink.hidden = false; + this._warningBtn.hidden = true; + } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE) { + this.setAttribute("notification", "error"); + this._error.textContent = gStrings.ext.formatStringFromName( + "notification.vulnerableUpdatable", + [this.mAddon.name], 1 + ); + this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableUpdatable.link"); + this._errorLink.href = this.mAddon.blocklistURL; + this._errorLink.hidden = false; + } else if (!isUpgrade && this.mAddon.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) { + this.setAttribute("notification", "error"); + this._error.textContent = gStrings.ext.formatStringFromName( + "notification.vulnerableNoUpdate", + [this.mAddon.name], 1 + ); + this._errorLink.value = gStrings.ext.GetStringFromName("notification.vulnerableNoUpdate.link"); + this._errorLink.href = this.mAddon.blocklistURL; + this._errorLink.hidden = false; + } else if (this.mAddon.isGMPlugin && !this.mAddon.isInstalled && + this.mAddon.isActive) { + this.setAttribute("notification", "warning"); + this._warning.textContent = + gStrings.ext.formatStringFromName("notification.gmpPending", + [this.mAddon.name], 1); + } else { + this.removeAttribute("notification"); + } + } + + this._preferencesBtn.hidden = (!this.mAddon.optionsURL) || + this.mAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE_INFO; + + if (this.typeHasFlag("SUPPORTS_ASK_TO_ACTIVATE")) { + this._enableBtn.disabled = true; + this._disableBtn.disabled = true; + this._askToActivateMenuitem.disabled = !this.hasPermission("ask_to_activate"); + this._alwaysActivateMenuitem.disabled = !this.hasPermission("enable"); + this._neverActivateMenuitem.disabled = !this.hasPermission("disable"); + if (!this.mAddon.isActive) { + this._stateMenulist.selectedItem = this._neverActivateMenuitem; + } else if (this.mAddon.userDisabled == AddonManager.STATE_ASK_TO_ACTIVATE) { + this._stateMenulist.selectedItem = this._askToActivateMenuitem; + } else { + this._stateMenulist.selectedItem = this._alwaysActivateMenuitem; + } + let hasActivatePermission = + ["ask_to_activate", "enable", "disable"].some(perm => this.hasPermission(perm)); + this._stateMenulist.disabled = !hasActivatePermission; + this._stateMenulist.hidden = false; + this._stateMenulist.classList.add('no-auto-hide'); + } else { + this._stateMenulist.hidden = true; + + let enableTooltip = gViewController.commands["cmd_enableItem"] + .getTooltip(this.mAddon); + this._enableBtn.setAttribute("tooltiptext", enableTooltip); + if (this.hasPermission("enable")) { + this._enableBtn.hidden = false; + } else { + this._enableBtn.hidden = true; + } + + let disableTooltip = gViewController.commands["cmd_disableItem"] + .getTooltip(this.mAddon); + this._disableBtn.setAttribute("tooltiptext", disableTooltip); + if (this.hasPermission("disable")) { + this._disableBtn.hidden = false; + } else { + this._disableBtn.hidden = true; + } + } + + let uninstallTooltip = gViewController.commands["cmd_uninstallItem"] + .getTooltip(this.mAddon); + this._removeBtn.setAttribute("tooltiptext", uninstallTooltip); + if (this.hasPermission("uninstall")) { + this._removeBtn.hidden = false; + } else { + this._removeBtn.hidden = true; + } + + this.setAttribute("active", this.mAddon.isActive); + + var showProgress = this.mAddon.purchaseURL || (this.mAddon.install && + this.mAddon.install.state != AddonManager.STATE_INSTALLED); + this._showStatus(showProgress ? "progress" : "none"); + + if (this.mAddon.type == "experiment") { + this.removeAttribute("notification"); + let prefix = "experiment."; + let active = this.mAddon.isActive; + + if (!showProgress) { + let stateKey = prefix + "state." + (active ? "active" : "complete"); + this._experimentState.value = gStrings.ext.GetStringFromName(stateKey); + + let now = Date.now(); + let end = this.endDate; + let days = Math.abs(end - now) / (24 * 60 * 60 * 1000); + + let timeKey = prefix + "time."; + let timeMessage; + + if (days < 1) { + timeKey += (active ? "endsToday" : "endedToday"); + timeMessage = gStrings.ext.GetStringFromName(timeKey); + } else { + timeKey += (active ? "daysRemaining" : "daysPassed"); + days = Math.round(days); + let timeString = gStrings.ext.GetStringFromName(timeKey); + timeMessage = PluralForm.get(days, timeString) + .replace("#1", days); + } + + this._experimentTime.value = timeMessage; + } + } + ]]></body> + </method> + + <method name="_fetchReleaseNotes"> + <parameter name="aURI"/> + <body><![CDATA[ + if (!aURI || this._relNotesLoaded) { + sendToggleEvent(); + return; + } + + var relNotesData = null, transformData = null; + + this._relNotesLoaded = true; + this._relNotesLoading.hidden = false; + this._relNotesError.hidden = true; + + let sendToggleEvent = () => { + var event = document.createEvent("Events"); + event.initEvent("RelNotesToggle", true, true); + this.dispatchEvent(event); + } + + let showRelNotes = () => { + if (!relNotesData || !transformData) + return; + + this._relNotesLoading.hidden = true; + + var processor = Components.classes["@mozilla.org/document-transformer;1?type=xslt"] + .createInstance(Components.interfaces.nsIXSLTProcessor); + processor.flags |= Components.interfaces.nsIXSLTProcessorPrivate.DISABLE_ALL_LOADS; + + processor.importStylesheet(transformData); + var fragment = processor.transformToFragment(relNotesData, document); + this._relNotes.appendChild(fragment); + if (this.hasAttribute("show-relnotes")) { + var container = this._relNotesContainer; + container.style.height = container.scrollHeight + "px"; + } + sendToggleEvent(); + } + + let handleError = () => { + dataReq.abort(); + styleReq.abort(); + this._relNotesLoading.hidden = true; + this._relNotesError.hidden = false; + this._relNotesLoaded = false; // allow loading to be re-tried + sendToggleEvent(); + } + + function handleResponse(aEvent) { + var req = aEvent.target; + var ct = req.getResponseHeader("content-type"); + if ((!ct || ct.indexOf("text/html") < 0) && + req.responseXML && + req.responseXML.documentElement.namespaceURI != XMLURI_PARSE_ERROR) { + if (req == dataReq) + relNotesData = req.responseXML; + else + transformData = req.responseXML; + showRelNotes(); + } else { + handleError(); + } + } + + var dataReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Components.interfaces.nsIXMLHttpRequest); + dataReq.open("GET", aURI.spec, true); + dataReq.responseType = "document"; + dataReq.addEventListener("load", handleResponse, false); + dataReq.addEventListener("error", handleError, false); + dataReq.send(null); + + var styleReq = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(Components.interfaces.nsIXMLHttpRequest); + styleReq.open("GET", UPDATES_RELEASENOTES_TRANSFORMFILE, true); + styleReq.responseType = "document"; + styleReq.addEventListener("load", handleResponse, false); + styleReq.addEventListener("error", handleError, false); + styleReq.send(null); + ]]></body> + </method> + + <method name="toggleReleaseNotes"> + <body><![CDATA[ + if (this.hasAttribute("show-relnotes")) { + this._relNotesContainer.style.height = "0px"; + this.removeAttribute("show-relnotes"); + this._relNotesToggle.setAttribute( + "label", + this._relNotesToggle.getAttribute("showlabel") + ); + this._relNotesToggle.setAttribute( + "tooltiptext", + this._relNotesToggle.getAttribute("showtooltip") + ); + var event = document.createEvent("Events"); + event.initEvent("RelNotesToggle", true, true); + this.dispatchEvent(event); + } else { + this._relNotesContainer.style.height = this._relNotesContainer.scrollHeight + + "px"; + this.setAttribute("show-relnotes", true); + this._relNotesToggle.setAttribute( + "label", + this._relNotesToggle.getAttribute("hidelabel") + ); + this._relNotesToggle.setAttribute( + "tooltiptext", + this._relNotesToggle.getAttribute("hidetooltip") + ); + var uri = this.mManualUpdate ? + this.mManualUpdate.releaseNotesURI : + this.mAddon.releaseNotesURI; + this._fetchReleaseNotes(uri); + } + ]]></body> + </method> + + <method name="restart"> + <body><![CDATA[ + gViewController.commands["cmd_restartApp"].doCommand(); + ]]></body> + </method> + + <method name="undo"> + <body><![CDATA[ + gViewController.commands["cmd_cancelOperation"].doCommand(this.mAddon); + ]]></body> + </method> + + <method name="uninstall"> + <body><![CDATA[ + // If uninstalling does not require a restart and the type doesn't + // support undoing of restartless uninstalls, then we fake it by + // just disabling it it, and doing the real uninstall later. + if (!this.opRequiresRestart("uninstall") && + !this.typeHasFlag("SUPPORTS_UNDO_RESTARTLESS_UNINSTALL")) { + this.setAttribute("wasDisabled", this.mAddon.userDisabled); + + // We must set userDisabled to true first, this will call + // _updateState which will clear any pending attribute set. + this.mAddon.userDisabled = true; + + // This won't update any other add-on manager views (bug 582002) + this.setAttribute("pending", "uninstall"); + } else { + this.mAddon.uninstall(true); + } + ]]></body> + </method> + + <method name="showPreferences"> + <body><![CDATA[ + gViewController.doCommand("cmd_showItemPreferences", this.mAddon); + ]]></body> + </method> + + <method name="upgrade"> + <body><![CDATA[ + var install = this.mManualUpdate; + delete this.mManualUpdate; + install.install(); + ]]></body> + </method> + + <method name="retryInstall"> + <body><![CDATA[ + var install = this._installStatus.mInstall; + if (!install) + return; + if (install.state != AddonManager.STATE_DOWNLOAD_FAILED && + install.state != AddonManager.STATE_INSTALL_FAILED) + return; + install.install(); + ]]></body> + </method> + + <method name="showInDetailView"> + <body><![CDATA[ + gViewController.loadView("addons://detail/" + + encodeURIComponent(this.mAddon.id)); + ]]></body> + </method> + + <method name="onIncludeUpdateChanged"> + <body><![CDATA[ + var event = document.createEvent("Events"); + event.initEvent("IncludeUpdateChanged", true, true); + this.dispatchEvent(event); + ]]></body> + </method> + + <method name="onEnabling"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onEnabled"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onDisabling"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onDisabled"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onUninstalling"> + <parameter name="aRestartRequired"/> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onOperationCancelled"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onPropertyChanged"> + <parameter name="aProperties"/> + <body><![CDATA[ + if (aProperties.indexOf("appDisabled") != -1 || + aProperties.indexOf("signedState") != -1 || + aProperties.indexOf("userDisabled") != -1) + this._updateState(); + ]]></body> + </method> + + <method name="onNoUpdateAvailable"> + <body><![CDATA[ + this._showStatus("none"); + ]]></body> + </method> + + <method name="onCheckingUpdate"> + <body><![CDATA[ + this._showStatus("checking-update"); + ]]></body> + </method> + + <method name="onCompatibilityUpdateAvailable"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onExternalInstall"> + <parameter name="aAddon"/> + <parameter name="aExistingAddon"/> + <parameter name="aNeedsRestart"/> + <body><![CDATA[ + if (aExistingAddon.id != this.mAddon.id) + return; + + // If the install completed without needing a restart then switch to + // using the new Addon + if (!aNeedsRestart) + this._initWithAddon(aAddon); + else + this._updateState(); + ]]></body> + </method> + + <method name="onNewInstall"> + <parameter name="aInstall"/> + <body><![CDATA[ + if (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_ENABLE) + return; + if (this.mAddon.applyBackgroundUpdates == AddonManager.AUTOUPDATE_DEFAULT && + AddonManager.autoUpdateDefault) + return; + + this.mManualUpdate = aInstall; + this._showStatus("update-available"); + ]]></body> + </method> + + <method name="onDownloadStarted"> + <parameter name="aInstall"/> + <body><![CDATA[ + this._updateState(); + this._showStatus("progress"); + this._installStatus.initWithInstall(aInstall); + ]]></body> + </method> + + <method name="onInstallStarted"> + <parameter name="aInstall"/> + <body><![CDATA[ + this._updateState(); + this._showStatus("progress"); + this._installStatus.initWithInstall(aInstall); + ]]></body> + </method> + + <method name="onInstallEnded"> + <parameter name="aInstall"/> + <parameter name="aAddon"/> + <body><![CDATA[ + // If the install completed without needing a restart then switch to + // using the new Addon + if (!(aAddon.pendingOperations & AddonManager.PENDING_INSTALL)) + this._initWithAddon(aAddon); + else + this._updateState(); + ]]></body> + </method> + + <method name="onDownloadFailed"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onInstallFailed"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + + <method name="onInstallCancelled"> + <body><![CDATA[ + this._updateState(); + ]]></body> + </method> + </implementation> + + <handlers> + <handler event="click" button="0"><![CDATA[ + switch (event.detail) { + case 1: + // Prevent double-click where the UI changes on the first click + this._lastClickTarget = event.originalTarget; + break; + case 2: + if (event.originalTarget.localName != 'button' && + !event.originalTarget.classList.contains('text-link') && + event.originalTarget == this._lastClickTarget) { + this.showInDetailView(); + } + break; + } + ]]></handler> + </handlers> + </binding> + + + <!-- Addon - uninstalled - An uninstalled addon that can be re-installed. --> + <binding id="addon-uninstalled" + extends="chrome://mozapps/content/extensions/extensions.xml#addon-base"> + <content> + <xul:hbox class="pending"> + <xul:image class="pending-icon"/> + <xul:label anonid="notice" flex="1"/> + <xul:button anonid="restart-btn" class="button-link" + label="&addon.restartNow.label;" + command="cmd_restartApp"/> + <xul:button anonid="undo-btn" class="button-link" + label="&addon.undoRemove.label;" + tooltiptext="&addon.undoRemove.tooltip;" + oncommand="document.getBindingParent(this).cancelUninstall();"/> + <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap --> + </xul:hbox> + </content> + + <implementation> + <constructor><![CDATA[ + this._notice.textContent = gStrings.ext.formatStringFromName("uninstallNotice", + [this.mAddon.name], + 1); + + if (!this.opRequiresRestart("uninstall")) + this._restartBtn.setAttribute("hidden", true); + + gEventManager.registerAddonListener(this, this.mAddon.id); + ]]></constructor> + + <destructor><![CDATA[ + gEventManager.unregisterAddonListener(this, this.mAddon.id); + ]]></destructor> + + <field name="_notice" readonly="true"> + document.getAnonymousElementByAttribute(this, "anonid", "notice"); + </field> + <field name="_restartBtn" readonly="true"> + document.getAnonymousElementByAttribute(this, "anonid", "restart-btn"); + </field> + + <method name="cancelUninstall"> + <body><![CDATA[ + // This assumes that disabling does not require a restart when + // uninstalling doesn't. Things will still work if not, the add-on + // will just still be active until finally getting uninstalled. + + if (this.isPending("uninstall")) + this.mAddon.cancelUninstall(); + else if (this.getAttribute("wasDisabled") != "true") + this.mAddon.userDisabled = false; + + this.removeAttribute("pending"); + ]]></body> + </method> + + <method name="onOperationCancelled"> + <body><![CDATA[ + if (!this.isPending("uninstall")) + this.removeAttribute("pending"); + ]]></body> + </method> + + <method name="onExternalInstall"> + <parameter name="aAddon"/> + <parameter name="aExistingAddon"/> + <parameter name="aNeedsRestart"/> + <body><![CDATA[ + if (aExistingAddon.id != this.mAddon.id) + return; + + // Make sure any newly installed add-on has the correct disabled state + if (this.hasAttribute("wasDisabled")) + aAddon.userDisabled = this.getAttribute("wasDisabled") == "true"; + + // If the install completed without needing a restart then switch to + // using the new Addon + if (!aNeedsRestart) + this.mAddon = aAddon; + + this.removeAttribute("pending"); + ]]></body> + </method> + + <method name="onInstallStarted"> + <parameter name="aInstall"/> + <body><![CDATA[ + // Make sure any newly installed add-on has the correct disabled state + if (this.hasAttribute("wasDisabled")) + aInstall.addon.userDisabled = this.getAttribute("wasDisabled") == "true"; + ]]></body> + </method> + + <method name="onInstallEnded"> + <parameter name="aInstall"/> + <parameter name="aAddon"/> + <body><![CDATA[ + // If the install completed without needing a restart then switch to + // using the new Addon + if (!(aAddon.pendingOperations & AddonManager.PENDING_INSTALL)) + this.mAddon = aAddon; + + this.removeAttribute("pending"); + ]]></body> + </method> + </implementation> + </binding> + + + <!-- Addon - installing - an addon item that is currently being installed --> + <binding id="addon-installing" + extends="chrome://mozapps/content/extensions/extensions.xml#addon-base"> + <content> + <xul:hbox anonid="warning-container" class="warning"> + <xul:image class="warning-icon"/> + <xul:label anonid="warning" flex="1"/> + <xul:button anonid="warning-link" class="button-link" + oncommand="document.getBindingParent(this).retryInstall();"/> + <xul:spacer flex="5000"/> <!-- Necessary to allow the message to wrap --> + </xul:hbox> + <xul:hbox class="content-container"> + <xul:vbox class="icon-outer-container"> + <xul:vbox class="icon-container"> + <xul:image anonid="icon" class="icon"/> + </xul:vbox> + </xul:vbox> + <xul:vbox class="fade name-outer-container" flex="1"> + <xul:hbox class="name-container"> + <xul:label anonid="name" class="name" crop="end" tooltip="addonitem-tooltip"/> + </xul:hbox> + </xul:vbox> + <xul:vbox class="install-status-container"> + <xul:hbox anonid="install-status" class="install-status"/> + </xul:vbox> + </xul:hbox> + </content> + + <implementation> + <constructor><![CDATA[ + this._installStatus.mControl = this; + this._installStatus.mInstall = this.mInstall; + this.refreshInfo(); + ]]></constructor> + + <field name="_icon"> + document.getAnonymousElementByAttribute(this, "anonid", "icon"); + </field> + <field name="_name"> + document.getAnonymousElementByAttribute(this, "anonid", "name"); + </field> + <field name="_warning"> + document.getAnonymousElementByAttribute(this, "anonid", "warning"); + </field> + <field name="_warningLink"> + document.getAnonymousElementByAttribute(this, "anonid", "warning-link"); + </field> + <field name="_installStatus"> + document.getAnonymousElementByAttribute(this, "anonid", + "install-status"); + </field> + + <method name="onInstallCompleted"> + <body><![CDATA[ + this.mAddon = this.mInstall.addon; + this.setAttribute("name", this.mAddon.name); + this.setAttribute("value", this.mAddon.id); + this.setAttribute("status", "installed"); + ]]></body> + </method> + + <method name="refreshInfo"> + <body><![CDATA[ + this.mAddon = this.mAddon || this.mInstall.addon; + if (this.mAddon) { + this._icon.src = this.mAddon.iconURL || + (this.mInstall ? this.mInstall.iconURL : ""); + this._name.value = this.mAddon.name; + } else { + this._icon.src = this.mInstall.iconURL; + // AddonInstall.name isn't always available - fallback to filename + if (this.mInstall.name) { + this._name.value = this.mInstall.name; + } else if (this.mInstall.sourceURI) { + var url = Components.classes["@mozilla.org/network/standard-url;1"] + .createInstance(Components.interfaces.nsIStandardURL); + url.init(url.URLTYPE_STANDARD, 80, this.mInstall.sourceURI.spec, + null, null); + url.QueryInterface(Components.interfaces.nsIURL); + this._name.value = url.fileName; + } + } + + if (this.mInstall.state == AddonManager.STATE_DOWNLOAD_FAILED) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.downloadError", + [this._name.value], 1 + ); + this._warningLink.label = gStrings.ext.GetStringFromName("notification.downloadError.retry"); + this._warningLink.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip"); + } else if (this.mInstall.state == AddonManager.STATE_INSTALL_FAILED) { + this.setAttribute("notification", "warning"); + this._warning.textContent = gStrings.ext.formatStringFromName( + "notification.installError", + [this._name.value], 1 + ); + this._warningLink.label = gStrings.ext.GetStringFromName("notification.installError.retry"); + this._warningLink.tooltipText = gStrings.ext.GetStringFromName("notification.downloadError.retry.tooltip"); + } else { + this.removeAttribute("notification"); + } + ]]></body> + </method> + + <method name="retryInstall"> + <body><![CDATA[ + this.mInstall.install(); + ]]></body> + </method> + </implementation> + </binding> + + <binding id="detail-row"> + <content> + <xul:label class="detail-row-label" xbl:inherits="value=label"/> + <xul:label class="detail-row-value" xbl:inherits="value"/> + </content> + + <implementation> + <property name="value"> + <getter><![CDATA[ + return this.getAttribute("value"); + ]]></getter> + <setter><![CDATA[ + if (!val) + this.removeAttribute("value"); + else + this.setAttribute("value", val); + ]]></setter> + </property> + </implementation> + </binding> + +</bindings> |