mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Backed out changeset d61965849528 (bug 1491243) for en-US failures at testing\firefox-ui\tests\puppeteer\test_page_info_window.py
This commit is contained in:
parent
3bc9db418d
commit
9a2b88d99e
@ -9,6 +9,8 @@ const EXPORTED_SYMBOLS = ["LinkHandlerChild"];
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "Feeds",
|
||||
"resource:///modules/Feeds.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "FaviconLoader",
|
||||
"resource:///modules/FaviconLoader.jsm");
|
||||
|
||||
@ -95,6 +97,7 @@ class LinkHandlerChild extends ActorChild {
|
||||
|
||||
// Note: following booleans only work for the current link, not for the
|
||||
// whole content
|
||||
let feedAdded = false;
|
||||
let iconAdded = false;
|
||||
let searchAdded = false;
|
||||
let rels = {};
|
||||
@ -105,6 +108,22 @@ class LinkHandlerChild extends ActorChild {
|
||||
let isRichIcon = false;
|
||||
|
||||
switch (relVal) {
|
||||
case "feed":
|
||||
case "alternate":
|
||||
if (!feedAdded && event.type == "DOMLinkAdded") {
|
||||
if (!rels.feed && rels.alternate && rels.stylesheet)
|
||||
break;
|
||||
|
||||
if (Feeds.isValidFeed(link, link.ownerDocument.nodePrincipal, "feed" in rels)) {
|
||||
this.mm.sendAsyncMessage("Link:AddFeed", {
|
||||
type: link.type,
|
||||
href: link.href,
|
||||
title: link.title,
|
||||
});
|
||||
feedAdded = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "apple-touch-icon":
|
||||
case "apple-touch-icon-precomposed":
|
||||
case "fluid-icon":
|
||||
|
@ -10,6 +10,7 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ActorChild.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
Feeds: "resource:///modules/Feeds.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
setTimeout: "resource://gre/modules/Timer.jsm",
|
||||
});
|
||||
@ -33,6 +34,7 @@ class PageInfoChild extends ActorChild {
|
||||
|
||||
let pageInfoData = {metaViewRows: this.getMetaInfo(document),
|
||||
docInfo: this.getDocumentInfo(document),
|
||||
feeds: this.getFeedsInfo(document, strings),
|
||||
windowInfo: this.getWindowInfo(window)};
|
||||
|
||||
message.target.sendAsyncMessage("PageInfo:data", pageInfoData);
|
||||
@ -96,6 +98,36 @@ class PageInfoChild extends ActorChild {
|
||||
return docInfo;
|
||||
}
|
||||
|
||||
getFeedsInfo(document, strings) {
|
||||
let feeds = [];
|
||||
// Get the feeds from the page.
|
||||
let linkNodes = document.getElementsByTagName("link");
|
||||
let length = linkNodes.length;
|
||||
for (let i = 0; i < length; i++) {
|
||||
let link = linkNodes[i];
|
||||
if (!link.href) {
|
||||
continue;
|
||||
}
|
||||
let rel = link.rel && link.rel.toLowerCase();
|
||||
let rels = {};
|
||||
|
||||
if (rel) {
|
||||
for (let relVal of rel.split(/\s+/)) {
|
||||
rels[relVal] = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
|
||||
let type = Feeds.isValidFeed(link, document.nodePrincipal, "feed" in rels);
|
||||
if (type) {
|
||||
type = strings[type] || strings["application/rss+xml"];
|
||||
feeds.push([link.title, type, link.href]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return feeds;
|
||||
}
|
||||
|
||||
// Only called once to get the media tab's media elements from the content page.
|
||||
getMediaInfo(document, window, strings, mm) {
|
||||
let frameList = this.goThroughFrames(document, window);
|
||||
|
@ -114,6 +114,170 @@ function getMimeTypeForFeedType(aFeedType) {
|
||||
var FeedHandler = {
|
||||
_prefChangeCallback: null,
|
||||
|
||||
/** Called when the user clicks on the Subscribe to This Page... menu item,
|
||||
* or when the user clicks the feed button when the page contains multiple
|
||||
* feeds.
|
||||
* Builds a menu of unique feeds associated with the page, and if there
|
||||
* is only one, shows the feed inline in the browser window.
|
||||
* @param container
|
||||
* The feed list container (menupopup or subview) to be populated.
|
||||
* @param isSubview
|
||||
* Whether we're creating a subview (true) or menu (false/undefined)
|
||||
* @return true if the menu/subview should be shown, false if there was only
|
||||
* one feed and the feed should be shown inline in the browser
|
||||
* window (do not show the menupopup/subview).
|
||||
*/
|
||||
buildFeedList(container, isSubview) {
|
||||
let feeds = gBrowser.selectedBrowser.feeds;
|
||||
if (!isSubview && feeds == null) {
|
||||
// XXX hack -- menu opening depends on setting of an "open"
|
||||
// attribute, and the menu refuses to open if that attribute is
|
||||
// set (because it thinks it's already open). onpopupshowing gets
|
||||
// called after the attribute is unset, and it doesn't get unset
|
||||
// if we return false. so we unset it here; otherwise, the menu
|
||||
// refuses to work past this point.
|
||||
container.parentNode.removeAttribute("open");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let i = container.childNodes.length - 1; i >= 0; --i) {
|
||||
let node = container.childNodes[i];
|
||||
if (isSubview && node.localName == "label")
|
||||
continue;
|
||||
container.removeChild(node);
|
||||
}
|
||||
|
||||
if (!feeds || feeds.length <= 1)
|
||||
return false;
|
||||
|
||||
// Build the menu showing the available feed choices for viewing.
|
||||
let itemNodeType = isSubview ? "toolbarbutton" : "menuitem";
|
||||
for (let feedInfo of feeds) {
|
||||
let item = document.createElement(itemNodeType);
|
||||
let baseTitle = feedInfo.title || feedInfo.href;
|
||||
item.setAttribute("label", baseTitle);
|
||||
item.setAttribute("feed", feedInfo.href);
|
||||
item.setAttribute("tooltiptext", feedInfo.href);
|
||||
item.setAttribute("crop", "center");
|
||||
let className = "feed-" + itemNodeType;
|
||||
if (isSubview) {
|
||||
className += " subviewbutton";
|
||||
}
|
||||
item.setAttribute("class", className);
|
||||
container.appendChild(item);
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Subscribe to a given feed. Called when
|
||||
* 1. Page has a single feed and user clicks feed icon in location bar
|
||||
* 2. Page has a single feed and user selects Subscribe menu item
|
||||
* 3. Page has multiple feeds and user selects from feed icon popup (or subview)
|
||||
* 4. Page has multiple feeds and user selects from Subscribe submenu
|
||||
* @param href
|
||||
* The feed to subscribe to. May be null, in which case the
|
||||
* event target's feed attribute is examined.
|
||||
* @param event
|
||||
* The event this method is handling. Used to decide where
|
||||
* to open the preview UI. (Optional, unless href is null)
|
||||
*/
|
||||
subscribeToFeed(href, event) {
|
||||
// Just load the feed in the content area to either subscribe or show the
|
||||
// preview UI
|
||||
if (!href)
|
||||
href = event.target.getAttribute("feed");
|
||||
urlSecurityCheck(href, gBrowser.contentPrincipal,
|
||||
Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
|
||||
this.loadFeed(href, event);
|
||||
},
|
||||
|
||||
loadFeed(href, event) {
|
||||
let feeds = gBrowser.selectedBrowser.feeds;
|
||||
try {
|
||||
openUILink(href, event, {
|
||||
ignoreAlt: true,
|
||||
triggeringPrincipal: gBrowser.contentPrincipal,
|
||||
});
|
||||
} finally {
|
||||
// We might default to a livebookmarks modal dialog,
|
||||
// so reset that if the user happens to click it again
|
||||
gBrowser.selectedBrowser.feeds = feeds;
|
||||
}
|
||||
},
|
||||
|
||||
get _feedMenuitem() {
|
||||
delete this._feedMenuitem;
|
||||
return this._feedMenuitem = document.getElementById("subscribeToPageMenuitem");
|
||||
},
|
||||
|
||||
get _feedMenupopup() {
|
||||
delete this._feedMenupopup;
|
||||
return this._feedMenupopup = document.getElementById("subscribeToPageMenupopup");
|
||||
},
|
||||
|
||||
/**
|
||||
* Update the browser UI to show whether or not feeds are available when
|
||||
* a page is loaded or the user switches tabs to a page that has feeds.
|
||||
*/
|
||||
updateFeeds() {
|
||||
if (this._updateFeedTimeout)
|
||||
clearTimeout(this._updateFeedTimeout);
|
||||
|
||||
let feeds = gBrowser.selectedBrowser.feeds;
|
||||
let haveFeeds = feeds && feeds.length > 0;
|
||||
|
||||
let feedButton = document.getElementById("feed-button");
|
||||
if (feedButton) {
|
||||
if (haveFeeds) {
|
||||
feedButton.removeAttribute("disabled");
|
||||
} else {
|
||||
feedButton.setAttribute("disabled", "true");
|
||||
}
|
||||
}
|
||||
|
||||
if (!haveFeeds) {
|
||||
this._feedMenuitem.setAttribute("disabled", "true");
|
||||
this._feedMenuitem.removeAttribute("hidden");
|
||||
this._feedMenupopup.setAttribute("hidden", "true");
|
||||
return;
|
||||
}
|
||||
|
||||
if (feeds.length > 1) {
|
||||
this._feedMenuitem.setAttribute("hidden", "true");
|
||||
this._feedMenupopup.removeAttribute("hidden");
|
||||
} else {
|
||||
this._feedMenuitem.setAttribute("feed", feeds[0].href);
|
||||
this._feedMenuitem.removeAttribute("disabled");
|
||||
this._feedMenuitem.removeAttribute("hidden");
|
||||
this._feedMenupopup.setAttribute("hidden", "true");
|
||||
}
|
||||
},
|
||||
|
||||
addFeed(link, browserForLink) {
|
||||
if (!browserForLink.feeds)
|
||||
browserForLink.feeds = [];
|
||||
|
||||
urlSecurityCheck(link.href, gBrowser.contentPrincipal,
|
||||
Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
|
||||
|
||||
let feedURI = makeURI(link.href, document.characterSet);
|
||||
if (!/^https?$/.test(feedURI.scheme))
|
||||
return;
|
||||
|
||||
browserForLink.feeds.push({ href: link.href, title: link.title });
|
||||
|
||||
// If this addition was for the current browser, update the UI. For
|
||||
// background browsers, we'll update on tab switch.
|
||||
if (browserForLink == gBrowser.selectedBrowser) {
|
||||
// Batch updates to avoid updating the UI for multiple onLinkAdded events
|
||||
// fired within 100ms of each other.
|
||||
if (this._updateFeedTimeout)
|
||||
clearTimeout(this._updateFeedTimeout);
|
||||
this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the human-readable display name of a file. This could be the
|
||||
* application name.
|
||||
|
@ -400,6 +400,26 @@
|
||||
<menuitem id="menu_bookmarkThisPage"
|
||||
command="Browser:AddBookmarkAs"
|
||||
key="addBookmarkAsKb"/>
|
||||
<menuitem id="subscribeToPageMenuitem"
|
||||
disabled="true"
|
||||
#ifndef XP_MACOSX
|
||||
class="menuitem-iconic"
|
||||
#endif
|
||||
label="&subscribeToPageMenuitem.label;"
|
||||
oncommand="return FeedHandler.subscribeToFeed(null, event);"
|
||||
onclick="checkForMiddleClick(this, event);"
|
||||
/>
|
||||
<menu id="subscribeToPageMenupopup"
|
||||
hidden="true"
|
||||
#ifndef XP_MACOSX
|
||||
class="menu-iconic"
|
||||
#endif
|
||||
label="&subscribeToPageMenupopup.label;">
|
||||
<menupopup id="subscribeToPageSubmenuMenupopup"
|
||||
onpopupshowing="return FeedHandler.buildFeedList(event.target);"
|
||||
oncommand="return FeedHandler.subscribeToFeed(null, event);"
|
||||
onclick="checkForMiddleClick(this, event);"/>
|
||||
</menu>
|
||||
<menuitem id="menu_bookmarkAllTabs"
|
||||
label="&addCurPagesCmd.label;"
|
||||
class="show-only-for-keyboard"
|
||||
|
@ -3661,6 +3661,7 @@ var newWindowButtonObserver = {
|
||||
const DOMEventHandler = {
|
||||
init() {
|
||||
let mm = window.messageManager;
|
||||
mm.addMessageListener("Link:AddFeed", this);
|
||||
mm.addMessageListener("Link:LoadingIcon", this);
|
||||
mm.addMessageListener("Link:SetIcon", this);
|
||||
mm.addMessageListener("Link:SetFailedIcon", this);
|
||||
@ -3670,6 +3671,11 @@ const DOMEventHandler = {
|
||||
|
||||
receiveMessage(aMsg) {
|
||||
switch (aMsg.name) {
|
||||
case "Link:AddFeed":
|
||||
let link = {type: aMsg.data.type, href: aMsg.data.href, title: aMsg.data.title};
|
||||
FeedHandler.addFeed(link, aMsg.target);
|
||||
break;
|
||||
|
||||
case "Link:LoadingIcon":
|
||||
if (aMsg.data.canUseForTab) {
|
||||
this.setPendingIcon(aMsg.target);
|
||||
@ -4702,6 +4708,9 @@ var XULBrowserWindow = {
|
||||
aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
|
||||
|
||||
if (aRequest && aWebProgress.isTopLevel) {
|
||||
// clear out feed data
|
||||
browser.feeds = null;
|
||||
|
||||
// clear out search-engine data
|
||||
browser.engines = null;
|
||||
}
|
||||
@ -4931,6 +4940,7 @@ var XULBrowserWindow = {
|
||||
},
|
||||
|
||||
asyncUpdateUI() {
|
||||
FeedHandler.updateFeeds();
|
||||
BrowserSearch.updateOpenSearchBadge();
|
||||
},
|
||||
|
||||
|
60
browser/base/content/pageinfo/feeds.js
Normal file
60
browser/base/content/pageinfo/feeds.js
Normal file
@ -0,0 +1,60 @@
|
||||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
// Via pageInfo.xul -> utilityOverlay.js
|
||||
/* import-globals-from ../utilityOverlay.js */
|
||||
/* import-globals-from ./pageInfo.js */
|
||||
|
||||
function initFeedTab(feeds) {
|
||||
for (const [name, type, url] of feeds) {
|
||||
addRow(name, type, url);
|
||||
}
|
||||
|
||||
const feedListbox = document.getElementById("feedListbox");
|
||||
document.getElementById("feedTab").hidden = feedListbox.getRowCount() == 0;
|
||||
}
|
||||
|
||||
function addRow(name, type, url) {
|
||||
const item = document.createXULElement("richlistitem");
|
||||
|
||||
const top = document.createXULElement("hbox");
|
||||
top.setAttribute("flex", "1");
|
||||
item.appendChild(top);
|
||||
|
||||
const bottom = document.createXULElement("hbox");
|
||||
bottom.setAttribute("flex", "1");
|
||||
item.appendChild(bottom);
|
||||
|
||||
const nameLabel = document.createXULElement("label");
|
||||
nameLabel.className = "feedTitle";
|
||||
nameLabel.textContent = name;
|
||||
nameLabel.setAttribute("flex", "1");
|
||||
top.appendChild(nameLabel);
|
||||
|
||||
const typeLabel = document.createXULElement("label");
|
||||
typeLabel.textContent = type;
|
||||
top.appendChild(typeLabel);
|
||||
|
||||
const urlContainer = document.createXULElement("hbox");
|
||||
urlContainer.setAttribute("flex", "1");
|
||||
bottom.appendChild(urlContainer);
|
||||
|
||||
const urlLabel = document.createXULElement("label");
|
||||
urlLabel.className = "text-link";
|
||||
urlLabel.textContent = url;
|
||||
urlLabel.setAttribute("tooltiptext", url);
|
||||
urlLabel.addEventListener("click", ev => openUILink(this.value, ev, {triggeringPrincipal: Services.scriptSecurityManager.createNullPrincipal({})}));
|
||||
urlContainer.appendChild(urlLabel);
|
||||
|
||||
const subscribeButton = document.createXULElement("button");
|
||||
subscribeButton.className = "feed-subscribe";
|
||||
subscribeButton.addEventListener("click",
|
||||
() => openWebLinkIn(url, "current", { ignoreAlt: true }));
|
||||
subscribeButton.setAttribute("label", gBundle.getString("feedSubscribe"));
|
||||
subscribeButton.setAttribute("accesskey", gBundle.getString("feedSubscribe.accesskey"));
|
||||
bottom.appendChild(subscribeButton);
|
||||
|
||||
document.getElementById("feedListbox").appendChild(item);
|
||||
}
|
@ -12,6 +12,14 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
#feedListbox richlistitem {
|
||||
-moz-box-orient: vertical;
|
||||
}
|
||||
|
||||
#feedListbox richlistitem:not([selected="true"]) .feed-subscribe {
|
||||
display: none;
|
||||
}
|
||||
|
||||
groupbox[closed="true"] > .groupbox-body {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
/* import-globals-from ../../../../toolkit/content/globalOverlay.js */
|
||||
/* import-globals-from ../../../../toolkit/content/contentAreaUtils.js */
|
||||
/* import-globals-from ../../../../toolkit/content/treeUtils.js */
|
||||
/* import-globals-from ../utilityOverlay.js */
|
||||
/* import-globals-from feeds.js */
|
||||
/* import-globals-from permissions.js */
|
||||
/* import-globals-from security.js */
|
||||
|
||||
@ -333,6 +333,12 @@ function loadPageInfo(frameOuterWindowID, imageElement, browser) {
|
||||
browser = browser || window.opener.gBrowser.selectedBrowser;
|
||||
let mm = browser.messageManager;
|
||||
|
||||
gStrings["application/rss+xml"] = gBundle.getString("feedRss");
|
||||
gStrings["application/atom+xml"] = gBundle.getString("feedAtom");
|
||||
gStrings["text/xml"] = gBundle.getString("feedXML");
|
||||
gStrings["application/xml"] = gBundle.getString("feedXML");
|
||||
gStrings["application/rdf+xml"] = gBundle.getString("feedXML");
|
||||
|
||||
let imageInfo = imageElement;
|
||||
|
||||
// Look for pageInfoListener in content.js. Sends message to listener with arguments.
|
||||
@ -340,7 +346,7 @@ function loadPageInfo(frameOuterWindowID, imageElement, browser) {
|
||||
|
||||
let pageInfoData;
|
||||
|
||||
// Get initial pageInfoData needed to display the general, permission and security tabs.
|
||||
// Get initial pageInfoData needed to display the general, feeds, permission and security tabs.
|
||||
mm.addMessageListener("PageInfo:data", function onmessage(message) {
|
||||
mm.removeMessageListener("PageInfo:data", onmessage);
|
||||
pageInfoData = message.data;
|
||||
@ -359,6 +365,7 @@ function loadPageInfo(frameOuterWindowID, imageElement, browser) {
|
||||
document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);
|
||||
|
||||
makeGeneralTab(pageInfoData.metaViewRows, docInfo);
|
||||
initFeedTab(pageInfoData.feeds);
|
||||
onLoadPermission(uri, principal);
|
||||
securityOnLoad(uri, windowInfo);
|
||||
});
|
||||
@ -402,6 +409,11 @@ function resetPageInfo(args) {
|
||||
gImageView.clear();
|
||||
gImageHash = {};
|
||||
|
||||
/* Reset Feeds Tab */
|
||||
var feedListbox = document.getElementById("feedListbox");
|
||||
while (feedListbox.firstChild)
|
||||
feedListbox.firstChild.remove();
|
||||
|
||||
/* Call registered overlay reset functions */
|
||||
onResetRegistry.forEach(function(func) { func(); });
|
||||
|
||||
@ -423,6 +435,7 @@ function doHelpButton() {
|
||||
const helpTopics = {
|
||||
"generalPanel": "pageinfo_general",
|
||||
"mediaPanel": "pageinfo_media",
|
||||
"feedPanel": "pageinfo_feed",
|
||||
"permPanel": "pageinfo_permissions",
|
||||
"securityPanel": "pageinfo_security",
|
||||
};
|
||||
|
@ -32,6 +32,7 @@
|
||||
<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
|
||||
<script type="application/javascript" src="chrome://global/content/treeUtils.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/pageinfo/pageInfo.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/pageinfo/feeds.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/pageinfo/permissions.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/pageinfo/security.js"/>
|
||||
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
|
||||
@ -73,6 +74,8 @@
|
||||
oncommand="showTab('general');"/>
|
||||
<radio id="mediaTab" label="&mediaTab;" accesskey="&mediaTab.accesskey;"
|
||||
oncommand="showTab('media');" hidden="true"/>
|
||||
<radio id="feedTab" label="&feedTab;" accesskey="&feedTab.accesskey;"
|
||||
oncommand="showTab('feed');" hidden="true"/>
|
||||
<radio id="permTab" label="&permTab;" accesskey="&permTab.accesskey;"
|
||||
oncommand="showTab('perm');"/>
|
||||
<radio id="securityTab" label="&securityTab;" accesskey="&securityTab.accesskey;"
|
||||
@ -261,6 +264,11 @@
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
<!-- Feeds -->
|
||||
<vbox id="feedPanel">
|
||||
<richlistbox id="feedListbox" flex="1"/>
|
||||
</vbox>
|
||||
|
||||
<!-- Permissions -->
|
||||
<vbox id="permPanel">
|
||||
<hbox id="permHostBox">
|
||||
|
@ -33,6 +33,7 @@ support-files =
|
||||
download_page_1.txt
|
||||
download_page_2.txt
|
||||
dummy_page.html
|
||||
feed_tab.html
|
||||
file_documentnavigation_frameset.html
|
||||
file_double_close_tab.html
|
||||
file_fullscreen-window-open.html
|
||||
@ -95,6 +96,8 @@ skip-if = (verify && !debug && (os == 'win'))
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug406216.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug413915.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug416661.js]
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_bug417483.js]
|
||||
@ -335,6 +338,9 @@ subsuite = clipboard
|
||||
support-files = offlineQuotaNotification.cacheManifest offlineQuotaNotification.html
|
||||
skip-if = os == "linux" && !debug # bug 1304273
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_feed_discovery.js]
|
||||
support-files = feed_discovery.html
|
||||
# DO NOT ADD MORE TESTS HERE. USE A TOPICAL DIRECTORY INSTEAD.
|
||||
[browser_gZipOfflineChild.js]
|
||||
skip-if = verify
|
||||
support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^
|
||||
|
68
browser/base/content/test/general/browser_bug413915.js
Normal file
68
browser/base/content/test/general/browser_bug413915.js
Normal file
@ -0,0 +1,68 @@
|
||||
ChromeUtils.defineModuleGetter(this, "Feeds",
|
||||
"resource:///modules/Feeds.jsm");
|
||||
|
||||
function test() {
|
||||
var exampleUri = makeURI("http://example.com/");
|
||||
var principal = Services.scriptSecurityManager.createCodebasePrincipal(exampleUri, {});
|
||||
|
||||
function testIsFeed(aTitle, aHref, aType, aKnown) {
|
||||
var link = {
|
||||
title: aTitle,
|
||||
href: aHref,
|
||||
type: aType,
|
||||
ownerDocument: {
|
||||
characterSet: "UTF-8",
|
||||
},
|
||||
};
|
||||
return Feeds.isValidFeed(link, principal, aKnown);
|
||||
}
|
||||
|
||||
var href = "http://example.com/feed/";
|
||||
var atomType = "application/atom+xml";
|
||||
var funkyAtomType = " aPPLICAtion/Atom+XML ";
|
||||
var rssType = "application/rss+xml";
|
||||
var funkyRssType = " Application/RSS+XML ";
|
||||
var rdfType = "application/rdf+xml";
|
||||
var texmlType = "text/xml";
|
||||
var appxmlType = "application/xml";
|
||||
var noRss = "Foo";
|
||||
var rss = "RSS";
|
||||
|
||||
// things that should be valid
|
||||
ok(testIsFeed(noRss, href, atomType, false) == atomType,
|
||||
"detect Atom feed");
|
||||
ok(testIsFeed(noRss, href, funkyAtomType, false) == atomType,
|
||||
"clean up and detect Atom feed");
|
||||
ok(testIsFeed(noRss, href, rssType, false) == rssType,
|
||||
"detect RSS feed");
|
||||
ok(testIsFeed(noRss, href, funkyRssType, false) == rssType,
|
||||
"clean up and detect RSS feed");
|
||||
|
||||
// things that should not be feeds
|
||||
ok(testIsFeed(noRss, href, rdfType, false) == null,
|
||||
"should not detect RDF non-feed");
|
||||
ok(testIsFeed(rss, href, rdfType, false) == null,
|
||||
"should not detect RDF feed from type and title");
|
||||
ok(testIsFeed(noRss, href, texmlType, false) == null,
|
||||
"should not detect text/xml non-feed");
|
||||
ok(testIsFeed(rss, href, texmlType, false) == null,
|
||||
"should not detect text/xml feed from type and title");
|
||||
ok(testIsFeed(noRss, href, appxmlType, false) == null,
|
||||
"should not detect application/xml non-feed");
|
||||
ok(testIsFeed(rss, href, appxmlType, false) == null,
|
||||
"should not detect application/xml feed from type and title");
|
||||
|
||||
// security check only, returns cleaned up type or "application/rss+xml"
|
||||
ok(testIsFeed(noRss, href, atomType, true) == atomType,
|
||||
"feed security check should return Atom type");
|
||||
ok(testIsFeed(noRss, href, funkyAtomType, true) == atomType,
|
||||
"feed security check should return cleaned up Atom type");
|
||||
ok(testIsFeed(noRss, href, rssType, true) == rssType,
|
||||
"feed security check should return RSS type");
|
||||
ok(testIsFeed(noRss, href, funkyRssType, true) == rssType,
|
||||
"feed security check should return cleaned up RSS type");
|
||||
ok(testIsFeed(noRss, href, "", true) == rssType,
|
||||
"feed security check without type should return RSS type");
|
||||
ok(testIsFeed(noRss, href, "garbage", true) == "garbage",
|
||||
"feed security check with garbage type should return garbage");
|
||||
}
|
33
browser/base/content/test/general/browser_feed_discovery.js
Normal file
33
browser/base/content/test/general/browser_feed_discovery.js
Normal file
@ -0,0 +1,33 @@
|
||||
const URL = "http://mochi.test:8888/browser/browser/base/content/test/general/feed_discovery.html";
|
||||
|
||||
/** Test for Bug 377611 **/
|
||||
|
||||
add_task(async function() {
|
||||
// Open a new tab.
|
||||
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, URL);
|
||||
registerCleanupFunction(() => gBrowser.removeCurrentTab());
|
||||
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
await BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
let discovered = browser.feeds;
|
||||
ok(discovered.length > 0, "some feeds should be discovered");
|
||||
|
||||
let feeds = {};
|
||||
for (let aFeed of discovered) {
|
||||
feeds[aFeed.href] = true;
|
||||
}
|
||||
|
||||
await ContentTask.spawn(browser, feeds, async function(contentFeeds) {
|
||||
for (let aLink of content.document.getElementsByTagName("link")) {
|
||||
// ignore real stylesheets, and anything without an href property
|
||||
if (aLink.type != "text/css" && aLink.href) {
|
||||
if (/bogus/i.test(aLink.title)) {
|
||||
ok(!contentFeeds[aLink.href], "don't discover " + aLink.href);
|
||||
} else {
|
||||
ok(contentFeeds[aLink.href], "should discover " + aLink.href);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
78
browser/base/content/test/general/feed_discovery.html
Normal file
78
browser/base/content/test/general/feed_discovery.html
Normal file
@ -0,0 +1,78 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=377611
|
||||
-->
|
||||
<head>
|
||||
<title>Test for feed discovery</title>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!-- Straight up standard -->
|
||||
<link rel="alternate" type="application/atom+xml" title="1" href="/1.atom" />
|
||||
<link rel="alternate" type="application/rss+xml" title="2" href="/2.rss" />
|
||||
<link rel="feed" title="3" href="/3.xml" />
|
||||
|
||||
<!-- invalid protocol -->
|
||||
<link rel="alternate" type="application/atom+xml" title="Bogus non file protocol" href="file://path/1.rss" />
|
||||
<link rel="alternate" type="application/atom+xml" title="Bogus non feed:http protocol" href="feed:http://path/1.rss" />
|
||||
<link rel="alternate" type="application/atom+xml" title="Bogus non pcast protocol" href="pcast://path/1.rss" />
|
||||
|
||||
<!-- rel is a space-separated list -->
|
||||
<link rel=" alternate " type="application/atom+xml" title="4" href="/4.atom" />
|
||||
<link rel="foo alternate" type="application/atom+xml" title="5" href="/5.atom" />
|
||||
<link rel="alternate foo" type="application/atom+xml" title="6" href="/6.atom" />
|
||||
<link rel="foo alternate foo" type="application/atom+xml" title="7" href="/7.atom" />
|
||||
<link rel="meat feed cake" title="8" href="/8.atom" />
|
||||
|
||||
<!-- rel is case-insensitive -->
|
||||
<link rel="ALTERNate" type="application/atom+xml" title="9" href="/9.atom" />
|
||||
<link rel="fEEd" title="10" href="/10.atom" />
|
||||
|
||||
<!-- type can have leading and trailing whitespace -->
|
||||
<link rel="alternate" type=" application/atom+xml " title="11" href="/11.atom" />
|
||||
|
||||
<!-- type is case-insensitive -->
|
||||
<link rel="alternate" type="aPPliCAtion/ATom+xML" title="12" href="/12.atom" />
|
||||
|
||||
<!-- "feed stylesheet" is a feed, though "alternate stylesheet" isn't -->
|
||||
<link rel="feed stylesheet" title="13" href="/13.atom" />
|
||||
|
||||
<!-- hyphens or letters around rel not allowed -->
|
||||
<link rel="disabled-alternate" type="application/atom+xml" title="Bogus1" href="/Bogus1" />
|
||||
<link rel="alternates" type="application/atom+xml" title="Bogus2" href="/Bogus2" />
|
||||
<link rel=" alternate-like" type="application/atom+xml" title="Bogus3" href="/Bogus3" />
|
||||
|
||||
<!-- don't tolerate text/xml if title includes 'rss' not as a word -->
|
||||
<link rel="alternate" type="text/xml" title="Bogus4 scissorsshaped" href="/Bogus4" />
|
||||
|
||||
<!-- don't tolerate application/xml if title includes 'rss' not as a word -->
|
||||
<link rel="alternate" type="application/xml" title="Bogus5 scissorsshaped" href="/Bogus5" />
|
||||
|
||||
<!-- don't tolerate application/rdf+xml if title includes 'rss' not as a word -->
|
||||
<link rel="alternate" type="application/rdf+xml" title="Bogus6 scissorsshaped" href="/Bogus6" />
|
||||
|
||||
<!-- don't tolerate random types -->
|
||||
<link rel="alternate" type="text/plain" title="Bogus7 rss" href="/Bogus7" />
|
||||
|
||||
<!-- don't find Atom by title -->
|
||||
<link rel="foopy" type="application/atom+xml" title="Bogus8 Atom and RSS" href="/Bogus8" />
|
||||
|
||||
<!-- don't find application/rss+xml by title -->
|
||||
<link rel="goats" type="application/rss+xml" title="Bogus9 RSS and Atom" href="/Bogus9" />
|
||||
|
||||
<!-- don't find application/rdf+xml by title -->
|
||||
<link rel="alternate" type="application/rdf+xml" title="Bogus10 RSS and Atom" href="/Bogus10" />
|
||||
|
||||
<!-- don't find application/xml by title -->
|
||||
<link rel="alternate" type="application/xml" title="Bogus11 RSS and Atom" href="/Bogus11" />
|
||||
|
||||
<!-- don't find text/xml by title -->
|
||||
<link rel="alternate" type="text/xml" title="Bogus12 RSS and Atom" href="/Bogus12" />
|
||||
|
||||
<!-- alternate and stylesheet isn't a feed -->
|
||||
<link rel="alternate stylesheet" type="application/rss+xml" title="Bogus13 RSS" href="/Bogus13" />
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,5 +1,8 @@
|
||||
[DEFAULT]
|
||||
|
||||
[browser_pageInfo.js]
|
||||
support-files =
|
||||
../general/feed_tab.html
|
||||
[browser_pageinfo_firstPartyIsolation.js]
|
||||
support-files =
|
||||
image.html
|
||||
|
39
browser/base/content/test/pageinfo/browser_pageInfo.js
Normal file
39
browser/base/content/test/pageinfo/browser_pageInfo.js
Normal file
@ -0,0 +1,39 @@
|
||||
const URI = "https://example.com/browser/browser/base/content/test/pageinfo/feed_tab.html";
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
var pageInfo;
|
||||
|
||||
gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser);
|
||||
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser, false,
|
||||
URI).then(() => {
|
||||
Services.obs.addObserver(observer, "page-info-dialog-loaded");
|
||||
pageInfo = BrowserPageInfo();
|
||||
});
|
||||
gBrowser.selectedBrowser.loadURI(URI);
|
||||
|
||||
function observer(win, topic, data) {
|
||||
Services.obs.removeObserver(observer, "page-info-dialog-loaded");
|
||||
pageInfo.onFinished.push(handlePageInfo);
|
||||
}
|
||||
|
||||
function handlePageInfo() {
|
||||
ok(pageInfo.document.getElementById("feedTab"), "Feed tab");
|
||||
let feedListbox = pageInfo.document.getElementById("feedListbox");
|
||||
ok(feedListbox, "Feed list should exist.");
|
||||
|
||||
var feedRowsNum = feedListbox.getRowCount();
|
||||
is(feedRowsNum, 3, "Number of feeds listed should be correct.");
|
||||
|
||||
for (var i = 0; i < feedRowsNum; i++) {
|
||||
let feedItem = feedListbox.getItemAtIndex(i);
|
||||
let feedTitle = feedItem.querySelector(".feedTitle");
|
||||
is(feedTitle.textContent, i + 1, "Feed name should be correct.");
|
||||
}
|
||||
|
||||
pageInfo.close();
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
@ -105,6 +105,10 @@
|
||||
list-style-image: var(--sidebars-icon) !important;
|
||||
}
|
||||
|
||||
:root[lwthemeicons~="--subscribe-icon"] #feed-button:-moz-lwtheme {
|
||||
list-style-image: var(--subscribe-icon) !important;
|
||||
}
|
||||
|
||||
:root[lwthemeicons~="--text_encoding-icon"] #characterencoding-button:-moz-lwtheme {
|
||||
list-style-image: var(--text_encoding-icon) !important;
|
||||
}
|
||||
@ -143,6 +147,7 @@
|
||||
:root[lwthemeicons~="--synced_tabs-icon"] #sync-button:-moz-lwtheme,
|
||||
:root[lwthemeicons~="--open_file-icon"] #open-file-button:-moz-lwtheme,
|
||||
:root[lwthemeicons~="--sidebars-icon"] #sidebar-button:-moz-lwtheme,
|
||||
:root[lwthemeicons~="--subscribe-icon"] #feed-button:-moz-lwtheme,
|
||||
:root[lwthemeicons~="--text_encoding-icon"] #characterencoding-button:-moz-lwtheme,
|
||||
:root[lwthemeicons~="--email_link-icon"] #email-link-button:-moz-lwtheme,
|
||||
:root[lwthemeicons~="--forget-icon"] #panic-button:-moz-lwtheme {
|
||||
|
@ -82,6 +82,7 @@ browser.jar:
|
||||
* content/browser/pageinfo/pageInfo.xul (content/pageinfo/pageInfo.xul)
|
||||
content/browser/pageinfo/pageInfo.js (content/pageinfo/pageInfo.js)
|
||||
content/browser/pageinfo/pageInfo.css (content/pageinfo/pageInfo.css)
|
||||
content/browser/pageinfo/feeds.js (content/pageinfo/feeds.js)
|
||||
content/browser/pageinfo/permissions.js (content/pageinfo/permissions.js)
|
||||
content/browser/pageinfo/security.js (content/pageinfo/security.js)
|
||||
content/browser/content-refreshblocker.js (content/content-refreshblocker.js)
|
||||
|
@ -55,7 +55,7 @@ const kSubviewEvents = [
|
||||
* The current version. We can use this to auto-add new default widgets as necessary.
|
||||
* (would be const but isn't because of testing purposes)
|
||||
*/
|
||||
var kVersion = 15;
|
||||
var kVersion = 14;
|
||||
|
||||
/**
|
||||
* Buttons removed from built-ins by version they were removed. kVersion must be
|
||||
@ -63,7 +63,6 @@ var kVersion = 15;
|
||||
* version the button is removed in as the value. e.g. "pocket-button": 5
|
||||
*/
|
||||
var ObsoleteBuiltinButtons = {
|
||||
"feed-button": 15,
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -329,6 +329,44 @@ const CustomizableWidgets = [
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "feed-button",
|
||||
type: "view",
|
||||
viewId: "PanelUI-feeds",
|
||||
tooltiptext: "feed-button.tooltiptext2",
|
||||
onClick(aEvent) {
|
||||
let win = aEvent.target.ownerGlobal;
|
||||
let feeds = win.gBrowser.selectedBrowser.feeds;
|
||||
|
||||
// Here, we only care about the case where we have exactly 1 feed and the
|
||||
// user clicked...
|
||||
let isClick = (aEvent.button == 0 || aEvent.button == 1);
|
||||
if (feeds && feeds.length == 1 && isClick) {
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
win.FeedHandler.subscribeToFeed(feeds[0].href, aEvent);
|
||||
CustomizableUI.hidePanelForNode(aEvent.target);
|
||||
}
|
||||
},
|
||||
onViewShowing(aEvent) {
|
||||
let doc = aEvent.target.ownerDocument;
|
||||
let container = doc.getElementById("PanelUI-feeds");
|
||||
let gotView = doc.defaultView.FeedHandler.buildFeedList(container, true);
|
||||
|
||||
// For no feeds or only a single one, don't show the panel.
|
||||
if (!gotView) {
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
},
|
||||
onCreated(node) {
|
||||
let win = node.ownerGlobal;
|
||||
let selectedBrowser = win.gBrowser.selectedBrowser;
|
||||
let feeds = selectedBrowser && selectedBrowser.feeds;
|
||||
if (!feeds || !feeds.length) {
|
||||
node.setAttribute("disabled", "true");
|
||||
}
|
||||
},
|
||||
}, {
|
||||
id: "characterencoding-button",
|
||||
label: "characterencoding-button2.label",
|
||||
type: "view",
|
||||
|
@ -2,6 +2,8 @@
|
||||
support-files =
|
||||
head.js
|
||||
support/test_967000_charEncoding_page.html
|
||||
support/feeds_test_page.html
|
||||
support/test-feed.xml
|
||||
|
||||
[browser_694291_searchbar_preference.js]
|
||||
[browser_873501_handle_specials.js]
|
||||
@ -93,6 +95,8 @@ skip-if = verify
|
||||
skip-if = verify
|
||||
[browser_963639_customizing_attribute_non_customizable_toolbar.js]
|
||||
[browser_967000_button_charEncoding.js]
|
||||
[browser_967000_button_feeds.js]
|
||||
skip-if = (verify && debug && (os == 'linux'))
|
||||
[browser_968565_insert_before_hidden_items.js]
|
||||
[browser_969427_recreate_destroyed_widget_after_reset.js]
|
||||
[browser_969661_character_encoding_navbar_disabled.js]
|
||||
|
@ -7,7 +7,7 @@
|
||||
// don't try this at home, kids.
|
||||
function test() {
|
||||
// Customize something to make sure stuff changed:
|
||||
CustomizableUI.addWidgetToArea("save-page-button", CustomizableUI.AREA_NAVBAR);
|
||||
CustomizableUI.addWidgetToArea("feed-button", CustomizableUI.AREA_NAVBAR);
|
||||
|
||||
// Check what version we're on:
|
||||
let CustomizableUIBSPass = ChromeUtils.import("resource:///modules/CustomizableUI.jsm", {});
|
||||
|
@ -4,7 +4,7 @@
|
||||
// don't try this at home, kids.
|
||||
function test() {
|
||||
// Customize something to make sure stuff changed:
|
||||
CustomizableUI.addWidgetToArea("save-page-button", CustomizableUI.AREA_NAVBAR);
|
||||
CustomizableUI.addWidgetToArea("feed-button", CustomizableUI.AREA_NAVBAR);
|
||||
|
||||
let CustomizableUIBSPass = ChromeUtils.import("resource:///modules/CustomizableUI.jsm", {});
|
||||
|
||||
|
@ -5,7 +5,7 @@
|
||||
"use strict";
|
||||
|
||||
const kXULWidgetId = "a-test-button"; // we'll create a button with this ID.
|
||||
const kAPIWidgetId = "save-page-button";
|
||||
const kAPIWidgetId = "feed-button";
|
||||
const kPanel = CustomizableUI.AREA_FIXED_OVERFLOW_PANEL;
|
||||
const kToolbar = CustomizableUI.AREA_NAVBAR;
|
||||
const kVisiblePalette = "customization-palette";
|
||||
|
@ -8,12 +8,12 @@ CustomizableUI.createWidget({id: "cui-panel-item-to-drag-to", defaultArea: Custo
|
||||
// Dragging an item from the palette to another button in the panel should work.
|
||||
add_task(async function() {
|
||||
await startCustomizing();
|
||||
let btn = document.getElementById("new-window-button");
|
||||
let btn = document.getElementById("feed-button");
|
||||
let placements = getAreaWidgetIds(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
|
||||
let lastButtonIndex = placements.length - 1;
|
||||
let lastButton = placements[lastButtonIndex];
|
||||
let placementsAfterInsert = placements.slice(0, lastButtonIndex).concat(["new-window-button", lastButton]);
|
||||
let placementsAfterInsert = placements.slice(0, lastButtonIndex).concat(["feed-button", lastButton]);
|
||||
let lastButtonNode = document.getElementById(lastButton);
|
||||
simulateItemDrag(btn, lastButtonNode, "start");
|
||||
assertAreaPlacements(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, placementsAfterInsert);
|
||||
@ -28,11 +28,11 @@ add_task(async function() {
|
||||
add_task(async function() {
|
||||
CustomizableUI.addWidgetToArea("cui-panel-item-to-drag-to", CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
await startCustomizing();
|
||||
let btn = document.getElementById("new-window-button");
|
||||
let btn = document.getElementById("feed-button");
|
||||
let panel = document.getElementById(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
let placements = getAreaWidgetIds(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
|
||||
let placementsAfterAppend = placements.concat(["new-window-button"]);
|
||||
let placementsAfterAppend = placements.concat(["feed-button"]);
|
||||
simulateItemDrag(btn, panel);
|
||||
assertAreaPlacements(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, placementsAfterAppend);
|
||||
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||
@ -49,12 +49,12 @@ add_task(async function() {
|
||||
CustomizableUI.removeWidgetFromArea(widgetIds.shift());
|
||||
}
|
||||
await startCustomizing();
|
||||
let btn = document.getElementById("new-window-button");
|
||||
let btn = document.getElementById("feed-button");
|
||||
let panel = document.getElementById(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
|
||||
assertAreaPlacements(panel.id, []);
|
||||
|
||||
let placementsAfterAppend = ["new-window-button"];
|
||||
let placementsAfterAppend = ["feed-button"];
|
||||
simulateItemDrag(btn, panel);
|
||||
assertAreaPlacements(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL, placementsAfterAppend);
|
||||
ok(!CustomizableUI.inDefaultState, "Should no longer be in default state.");
|
||||
|
@ -29,10 +29,10 @@ add_task(function() {
|
||||
// Insert, then remove items
|
||||
add_task(function() {
|
||||
let currentSet = navbar.currentSet;
|
||||
let newCurrentSet = currentSet.replace("home-button", "new-window-button,sync-button,home-button");
|
||||
let newCurrentSet = currentSet.replace("home-button", "feed-button,sync-button,home-button");
|
||||
navbar.currentSet = newCurrentSet;
|
||||
is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
|
||||
let feedBtn = document.getElementById("new-window-button");
|
||||
let feedBtn = document.getElementById("feed-button");
|
||||
let syncBtn = document.getElementById("sync-button");
|
||||
ok(feedBtn, "Feed button should have been added.");
|
||||
ok(syncBtn, "Sync button should have been added.");
|
||||
@ -54,10 +54,10 @@ add_task(function() {
|
||||
// Simultaneous insert/remove:
|
||||
add_task(function() {
|
||||
let currentSet = navbar.currentSet;
|
||||
let newCurrentSet = currentSet.replace("home-button", "new-window-button");
|
||||
let newCurrentSet = currentSet.replace("home-button", "feed-button");
|
||||
navbar.currentSet = newCurrentSet;
|
||||
is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
|
||||
let feedBtn = document.getElementById("new-window-button");
|
||||
let feedBtn = document.getElementById("feed-button");
|
||||
ok(feedBtn, "Feed button should have been added.");
|
||||
let homeBtn = document.getElementById("home-button");
|
||||
ok(!homeBtn, "Home button should have been removed.");
|
||||
|
@ -0,0 +1,62 @@
|
||||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const TEST_PAGE = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/feeds_test_page.html";
|
||||
const TEST_FEED = "http://mochi.test:8888/browser/browser/components/customizableui/test/support/test-feed.xml";
|
||||
|
||||
var newTab = null;
|
||||
var initialLocation = gBrowser.currentURI.spec;
|
||||
|
||||
add_task(async function() {
|
||||
info("Check Subscribe button functionality");
|
||||
|
||||
// add the Subscribe button to the panel
|
||||
CustomizableUI.addWidgetToArea("feed-button",
|
||||
CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
|
||||
|
||||
await waitForOverflowButtonShown();
|
||||
|
||||
// check the button's functionality
|
||||
await document.getElementById("nav-bar").overflowable.show();
|
||||
|
||||
let feedButton = document.getElementById("feed-button");
|
||||
ok(feedButton, "The Subscribe button was added to the Panel Menu");
|
||||
is(feedButton.getAttribute("disabled"), "true", "The Subscribe button is initially disabled");
|
||||
|
||||
let panelHidePromise = promiseOverflowHidden(window);
|
||||
await document.getElementById("nav-bar").overflowable._panel.hidePopup();
|
||||
await panelHidePromise;
|
||||
|
||||
newTab = gBrowser.selectedTab;
|
||||
await promiseTabLoadEvent(newTab, TEST_PAGE);
|
||||
|
||||
await gCUITestUtils.openMainMenu();
|
||||
|
||||
await waitForCondition(() => !feedButton.hasAttribute("disabled"));
|
||||
ok(!feedButton.hasAttribute("disabled"), "The Subscribe button gets enabled");
|
||||
|
||||
feedButton.click();
|
||||
await promiseTabLoadEvent(newTab, TEST_FEED);
|
||||
|
||||
is(gBrowser.currentURI.spec, TEST_FEED, "Subscribe page opened");
|
||||
ok(!isOverflowOpen(), "Panel is closed");
|
||||
|
||||
if (isOverflowOpen()) {
|
||||
panelHidePromise = promiseOverflowHidden(window);
|
||||
await document.getElementById("nav-bar").overflowable._panel.hidePopup();
|
||||
await panelHidePromise;
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function asyncCleanup() {
|
||||
// reset the panel UI to the default state
|
||||
await resetCustomization();
|
||||
ok(CustomizableUI.inDefaultState, "The UI is in default state again.");
|
||||
|
||||
// restore the initial location
|
||||
BrowserTestUtils.addTab(gBrowser, initialLocation);
|
||||
gBrowser.removeTab(newTab);
|
||||
});
|
@ -79,7 +79,7 @@ add_task(function() {
|
||||
defaultPlacements: [] });
|
||||
CustomizableUI.registerArea("area-996899-2", { anchor: "PanelUI-menu-button",
|
||||
type: CustomizableUI.TYPE_MENU_PANEL,
|
||||
defaultPlacements: ["new-window-button"] });
|
||||
defaultPlacements: ["feed-button"] });
|
||||
} catch (ex) {
|
||||
exceptionThrown = ex;
|
||||
}
|
||||
|
@ -0,0 +1,10 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Feeds test page</title>
|
||||
<link rel="alternate" type="application/rss+xml" href="test-feed.xml" title="Test feed">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
This is a test page for feeds
|
||||
</body>
|
||||
</html>
|
23
browser/components/customizableui/test/support/test-feed.xml
Normal file
23
browser/components/customizableui/test/support/test-feed.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
|
||||
<title>Example Feed</title>
|
||||
<link href="http://example.org/"/>
|
||||
<updated>2010-08-22T18:30:02Z</updated>
|
||||
|
||||
<author>
|
||||
<name>John Doe</name>
|
||||
</author>
|
||||
<id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id>
|
||||
|
||||
<entry>
|
||||
|
||||
<title>Item</title>
|
||||
<link href="http://example.org/first"/>
|
||||
<id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id>
|
||||
<updated>2010-08-22T18:30:02Z</updated>
|
||||
|
||||
<summary>Some text.</summary>
|
||||
</entry>
|
||||
|
||||
</feed>
|
@ -120,6 +120,7 @@ async function runTestWithIcons(icons) {
|
||||
["synced_tabs", "#sync-button", "sync-button"],
|
||||
["open_file", "#open-file-button", "open-file-button"],
|
||||
["sidebars", "#sidebar-button", "sidebar-button"],
|
||||
["subscribe", "#feed-button", "feed-button"],
|
||||
["text_encoding", "#characterencoding-button", "characterencoding-button"],
|
||||
["email_link", "#email-link-button", "email-link-button"],
|
||||
["forget", "#panic-button", "panic-button"],
|
||||
@ -192,6 +193,7 @@ add_task(async function test_all_icons() {
|
||||
["synced_tabs", "fox.svg"],
|
||||
["open_file", "fox.svg"],
|
||||
["sidebars", "fox.svg"],
|
||||
["subscribe", "fox.svg"],
|
||||
["text_encoding", "fox.svg"],
|
||||
["email_link", "fox.svg"],
|
||||
["forget", "fox.svg"],
|
||||
@ -231,6 +233,7 @@ add_task(async function test_some_icons() {
|
||||
["synced_tabs", ""],
|
||||
["open_file", ""],
|
||||
["sidebars", ""],
|
||||
["subscribe", ""],
|
||||
["text_encoding", ""],
|
||||
["email_link", ""],
|
||||
["forget", ""],
|
||||
|
@ -341,6 +341,12 @@ FeedResultService.prototype = {
|
||||
subtitle,
|
||||
feedHandler: "default" });
|
||||
break;
|
||||
default:
|
||||
// fall through
|
||||
case "bookmarks":
|
||||
Services.cpmm.sendAsyncMessage("FeedConverter:addLiveBookmark",
|
||||
{ spec, title });
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -68,7 +68,15 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "gCanFrameFeeds",
|
||||
"browser.feeds.unsafelyFrameFeeds", false);
|
||||
|
||||
function FeedWriter() {
|
||||
this._selectedApp = undefined;
|
||||
this._selectedAppMenuItem = null;
|
||||
this._subscribeCallback = null;
|
||||
this._defaultHandlerMenuItem = null;
|
||||
|
||||
Services.telemetry.scalarAdd("browser.feeds.preview_loaded", 1);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_mm",
|
||||
() => this._window.docShell.messageManager);
|
||||
}
|
||||
|
||||
FeedWriter.prototype = {
|
||||
@ -138,6 +146,24 @@ FeedWriter.prototype = {
|
||||
return this._bundle.GetStringFromName(key);
|
||||
},
|
||||
|
||||
_setCheckboxCheckedState(aValue) {
|
||||
let checkbox = this._document.getElementById("alwaysUse");
|
||||
if (checkbox) {
|
||||
// see checkbox.xml, xbl bindings are not applied within the sandbox! TODO
|
||||
let change = (aValue != (checkbox.getAttribute("checked") == "true"));
|
||||
if (aValue)
|
||||
checkbox.setAttribute("checked", "true");
|
||||
else
|
||||
checkbox.removeAttribute("checked");
|
||||
|
||||
if (change) {
|
||||
let event = this._document.createEvent("Events");
|
||||
event.initEvent("CheckboxStateChange", true, true);
|
||||
checkbox.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns a date suitable for displaying in the feed preview.
|
||||
* If the date cannot be parsed, the return value is "false".
|
||||
@ -167,6 +193,25 @@ FeedWriter.prototype = {
|
||||
return this.__dateFormatter;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the feed type.
|
||||
*/
|
||||
__feedType: null,
|
||||
_getFeedType() {
|
||||
if (this.__feedType != null)
|
||||
return this.__feedType;
|
||||
|
||||
try {
|
||||
// grab the feed because it's got the feed.type in it.
|
||||
let container = this._getContainer();
|
||||
let feed = container.QueryInterface(Ci.nsIFeed);
|
||||
this.__feedType = feed.type;
|
||||
return feed.type;
|
||||
} catch (ex) { }
|
||||
|
||||
return Ci.nsIFeed.TYPE_FEED;
|
||||
},
|
||||
|
||||
/**
|
||||
* Writes the feed title into the preview document.
|
||||
* @param container
|
||||
@ -427,6 +472,234 @@ FeedWriter.prototype = {
|
||||
return container;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get moz-icon url for a file
|
||||
* @param file
|
||||
* A nsIFile object for which the moz-icon:// is returned
|
||||
* @returns moz-icon url of the given file as a string
|
||||
*/
|
||||
_getFileIconURL(file) {
|
||||
let fph = Services.io.getProtocolHandler("file")
|
||||
.QueryInterface(Ci.nsIFileProtocolHandler);
|
||||
let urlSpec = fph.getURLSpecFromFile(file);
|
||||
return "moz-icon://" + urlSpec + "?size=16";
|
||||
},
|
||||
|
||||
/**
|
||||
* Displays a prompt from which the user may choose a (client) feed reader.
|
||||
* @param aCallback the callback method, passes in true if a feed reader was
|
||||
* selected, false otherwise.
|
||||
*/
|
||||
_chooseClientApp(aCallback) {
|
||||
this._subscribeCallback = aCallback;
|
||||
this._mm.sendAsyncMessage("FeedWriter:ChooseClientApp",
|
||||
{ title: this._getString("chooseApplicationDialogTitle"),
|
||||
feedType: this._getFeedType() });
|
||||
},
|
||||
|
||||
_setSubscribeUsingLabel() {
|
||||
let stringLabel = "subscribeFeedUsing";
|
||||
switch (this._getFeedType()) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
stringLabel = "subscribeVideoPodcastUsing";
|
||||
break;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
stringLabel = "subscribeAudioPodcastUsing";
|
||||
break;
|
||||
}
|
||||
|
||||
let subscribeUsing = this._document.getElementById("subscribeUsingDescription");
|
||||
let textNode = this._document.createTextNode(this._getString(stringLabel));
|
||||
subscribeUsing.insertBefore(textNode, subscribeUsing.firstChild);
|
||||
},
|
||||
|
||||
_setAlwaysUseLabel() {
|
||||
let checkbox = this._document.getElementById("alwaysUse");
|
||||
if (checkbox && this._handlersList) {
|
||||
let handlerName = this._handlersList.selectedOptions[0]
|
||||
.textContent;
|
||||
let stringLabel = "alwaysUseForFeeds";
|
||||
switch (this._getFeedType()) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
stringLabel = "alwaysUseForVideoPodcasts";
|
||||
break;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
stringLabel = "alwaysUseForAudioPodcasts";
|
||||
break;
|
||||
}
|
||||
|
||||
let label = this._getFormattedString(stringLabel, [handlerName]);
|
||||
|
||||
let checkboxText = this._document.getElementById("checkboxText");
|
||||
if (checkboxText.lastChild.nodeType == checkboxText.TEXT_NODE) {
|
||||
checkboxText.lastChild.textContent = label;
|
||||
} else {
|
||||
LOG("FeedWriter._setAlwaysUseLabel: Expected textNode as lastChild of alwaysUse label");
|
||||
let textNode = this._document.createTextNode(label);
|
||||
checkboxText.appendChild(textNode);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// EventListener
|
||||
handleEvent(event) {
|
||||
if (event.target.ownerDocument != this._document) {
|
||||
LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.type) {
|
||||
case "click":
|
||||
if (event.target.id == "subscribeButton") {
|
||||
this.subscribe();
|
||||
}
|
||||
break;
|
||||
case "change":
|
||||
LOG("Change fired");
|
||||
if (event.target.selectedOptions[0].id == "chooseApplicationMenuItem") {
|
||||
this._chooseClientApp(() => {
|
||||
// Select the (per-prefs) selected handler if no application
|
||||
// was selected
|
||||
LOG("Selected handler after callback");
|
||||
this._setAlwaysUseLabel();
|
||||
});
|
||||
} else {
|
||||
this._setAlwaysUseLabel();
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_setSelectedHandlerResponse(handler) {
|
||||
LOG(`Selecting handler response ${handler}`);
|
||||
switch (handler) {
|
||||
case "client":
|
||||
case "default":
|
||||
// do nothing, these are handled by the onchange event
|
||||
break;
|
||||
case "bookmarks":
|
||||
default: {
|
||||
let liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem");
|
||||
if (liveBookmarksMenuItem)
|
||||
liveBookmarksMenuItem.selected = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_initSubscriptionUI(setupMessage) {
|
||||
if (!this._handlersList)
|
||||
return;
|
||||
LOG("UI init");
|
||||
|
||||
let feedType = this._getFeedType();
|
||||
|
||||
// change the background
|
||||
let header = this._document.getElementById("feedHeader");
|
||||
switch (feedType) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
header.className = "videoPodcastBackground";
|
||||
break;
|
||||
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
header.className = "audioPodcastBackground";
|
||||
break;
|
||||
|
||||
default:
|
||||
header.className = "feedBackground";
|
||||
}
|
||||
|
||||
let liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem");
|
||||
|
||||
// Last-selected application
|
||||
let menuItem = liveBookmarksMenuItem.cloneNode(false);
|
||||
menuItem.removeAttribute("selected");
|
||||
menuItem.setAttribute("id", "selectedAppMenuItem");
|
||||
menuItem.setAttribute("handlerType", "client");
|
||||
|
||||
// Hide the menuitem until we select an app
|
||||
menuItem.style.display = "none";
|
||||
this._selectedAppMenuItem = menuItem;
|
||||
|
||||
this._handlersList.appendChild(this._selectedAppMenuItem);
|
||||
|
||||
// Create the menuitem for the default reader, but don't show/populate it until
|
||||
// we get confirmation of what it is from the parent
|
||||
menuItem = liveBookmarksMenuItem.cloneNode(false);
|
||||
menuItem.removeAttribute("selected");
|
||||
menuItem.setAttribute("id", "defaultHandlerMenuItem");
|
||||
menuItem.setAttribute("handlerType", "client");
|
||||
menuItem.style.display = "none";
|
||||
|
||||
this._defaultHandlerMenuItem = menuItem;
|
||||
this._handlersList.appendChild(this._defaultHandlerMenuItem);
|
||||
|
||||
// "Choose Application..." menuitem
|
||||
menuItem = liveBookmarksMenuItem.cloneNode(false);
|
||||
menuItem.removeAttribute("selected");
|
||||
menuItem.setAttribute("id", "chooseApplicationMenuItem");
|
||||
menuItem.textContent = this._getString("chooseApplicationMenuItem");
|
||||
|
||||
this._handlersList.appendChild(menuItem);
|
||||
|
||||
this._setSelectedHandlerResponse(setupMessage.reader.handler);
|
||||
|
||||
if (setupMessage.defaultMenuItem) {
|
||||
LOG(`Setting default menu item ${setupMessage.defaultMenuItem}`);
|
||||
this._setApplicationLauncherMenuItem(this._defaultHandlerMenuItem, setupMessage.defaultMenuItem);
|
||||
}
|
||||
if (setupMessage.selectedMenuItem) {
|
||||
LOG(`Setting selected menu item ${setupMessage.selectedMenuItem}`);
|
||||
this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, setupMessage.selectedMenuItem);
|
||||
}
|
||||
|
||||
// "Subscribe using..."
|
||||
this._setSubscribeUsingLabel();
|
||||
|
||||
// "Always use..." checkbox initial state
|
||||
this._setCheckboxCheckedState(setupMessage.reader.alwaysUse);
|
||||
this._setAlwaysUseLabel();
|
||||
|
||||
// We update the "Always use.." checkbox label whenever the selected item
|
||||
// in the list is changed
|
||||
this._handlersList.addEventListener("change", this);
|
||||
|
||||
// Set up the "Subscribe Now" button
|
||||
this._document.getElementById("subscribeButton")
|
||||
.addEventListener("click", this);
|
||||
|
||||
// first-run ui
|
||||
if (setupMessage.showFirstRunUI) {
|
||||
let textfeedinfo1, textfeedinfo2;
|
||||
switch (feedType) {
|
||||
case Ci.nsIFeed.TYPE_VIDEO:
|
||||
textfeedinfo1 = "feedSubscriptionVideoPodcast1";
|
||||
textfeedinfo2 = "feedSubscriptionVideoPodcast2";
|
||||
break;
|
||||
case Ci.nsIFeed.TYPE_AUDIO:
|
||||
textfeedinfo1 = "feedSubscriptionAudioPodcast1";
|
||||
textfeedinfo2 = "feedSubscriptionAudioPodcast2";
|
||||
break;
|
||||
default:
|
||||
textfeedinfo1 = "feedSubscriptionFeed1";
|
||||
textfeedinfo2 = "feedSubscriptionFeed2";
|
||||
}
|
||||
|
||||
let feedinfo1 = this._document.getElementById("feedSubscriptionInfo1");
|
||||
let feedinfo1Str = this._getString(textfeedinfo1);
|
||||
let feedinfo2 = this._document.getElementById("feedSubscriptionInfo2");
|
||||
let feedinfo2Str = this._getString(textfeedinfo2);
|
||||
|
||||
feedinfo1.textContent = feedinfo1Str;
|
||||
feedinfo2.textContent = feedinfo2Str;
|
||||
|
||||
header.setAttribute("firstrun", "true");
|
||||
|
||||
this._mm.sendAsyncMessage("FeedWriter:ShownFirstRun");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the original URI object of the feed and ensures that this
|
||||
* component is only ever invoked from the preview document.
|
||||
@ -462,6 +735,7 @@ FeedWriter.prototype = {
|
||||
_document: null,
|
||||
_feedURI: null,
|
||||
_feedPrincipal: null,
|
||||
_handlersList: null,
|
||||
|
||||
// BrowserFeedWriter WebIDL methods
|
||||
init(aWindow) {
|
||||
@ -475,10 +749,73 @@ FeedWriter.prototype = {
|
||||
|
||||
this._window = window;
|
||||
this._document = window.document;
|
||||
this._handlersList = this._document.getElementById("handlersMenuList");
|
||||
|
||||
this._feedPrincipal = Services.scriptSecurityManager.createCodebasePrincipal(this._feedURI, {});
|
||||
|
||||
LOG("Subscribe Preview: feed uri = " + this._window.location.href);
|
||||
|
||||
|
||||
this._mm.addMessageListener("FeedWriter:PreferenceUpdated", this);
|
||||
this._mm.addMessageListener("FeedWriter:SetApplicationLauncherMenuItem", this);
|
||||
this._mm.addMessageListener("FeedWriter:GetSubscriptionUIResponse", this);
|
||||
|
||||
const feedType = this._getFeedType();
|
||||
this._mm.sendAsyncMessage("FeedWriter:GetSubscriptionUI",
|
||||
{ feedType });
|
||||
},
|
||||
|
||||
receiveMessage(msg) {
|
||||
if (!this._window) {
|
||||
// this._window is null unless this.init was called with a trusted
|
||||
// window object.
|
||||
return;
|
||||
}
|
||||
LOG(`received message from parent ${msg.name}`);
|
||||
switch (msg.name) {
|
||||
case "FeedWriter:PreferenceUpdated":
|
||||
// This is called when browser-feeds.js spots a pref change
|
||||
// This will happen when
|
||||
// - about:preferences#general changes
|
||||
// - another feed reader page changes the preference
|
||||
// - when this page itself changes the select and there isn't a redirect
|
||||
// bookmarks and launching an external app means the page stays open after subscribe
|
||||
const feedType = this._getFeedType();
|
||||
LOG(`Got prefChange! ${JSON.stringify(msg.data)} current type: ${feedType}`);
|
||||
let feedTypePref = msg.data.default;
|
||||
if (feedType in msg.data) {
|
||||
feedTypePref = msg.data[feedType];
|
||||
}
|
||||
LOG(`Got pref ${JSON.stringify(feedTypePref)}`);
|
||||
this._setCheckboxCheckedState(feedTypePref.alwaysUse);
|
||||
this._setSelectedHandlerResponse(feedTypePref.handler);
|
||||
this._setAlwaysUseLabel();
|
||||
break;
|
||||
case "FeedWriter:GetSubscriptionUIResponse":
|
||||
// Set up the subscription UI
|
||||
this._initSubscriptionUI(msg.data);
|
||||
break;
|
||||
case "FeedWriter:SetApplicationLauncherMenuItem":
|
||||
LOG(`FeedWriter:SetApplicationLauncherMenuItem - picked ${msg.data.name}`);
|
||||
this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, msg.data.name);
|
||||
// Potentially a bit racy, but I don't think we can get into a state where this callback is set and
|
||||
// we're not coming back from ChooseClientApp in browser-feeds.js
|
||||
if (this._subscribeCallback) {
|
||||
this._subscribeCallback();
|
||||
this._subscribeCallback = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_setApplicationLauncherMenuItem(menuItem, aName) {
|
||||
/* unselect all handlers */
|
||||
[...this._handlersList.children].forEach((option) => {
|
||||
option.removeAttribute("selected");
|
||||
});
|
||||
menuItem.textContent = aName;
|
||||
menuItem.style.display = "";
|
||||
menuItem.selected = true;
|
||||
},
|
||||
|
||||
writeContent() {
|
||||
@ -503,12 +840,21 @@ FeedWriter.prototype = {
|
||||
if (!this._window) {
|
||||
return;
|
||||
}
|
||||
this._document.getElementById("subscribeButton")
|
||||
.removeEventListener("click", this);
|
||||
this._handlersList
|
||||
.removeEventListener("change", this);
|
||||
this._document = null;
|
||||
this._window = null;
|
||||
this._handlersList = null;
|
||||
|
||||
this._removeFeedFromCache();
|
||||
this.__bundle = null;
|
||||
this._feedURI = null;
|
||||
|
||||
this._selectedApp = undefined;
|
||||
this._selectedAppMenuItem = null;
|
||||
this._defaultHandlerMenuItem = null;
|
||||
},
|
||||
|
||||
_removeFeedFromCache() {
|
||||
@ -520,6 +866,69 @@ FeedWriter.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
subscribe() {
|
||||
if (!this._window) {
|
||||
return;
|
||||
}
|
||||
let feedType = this._getFeedType();
|
||||
|
||||
// Subscribe to the feed using the selected handler and save prefs
|
||||
let defaultHandler = "reader";
|
||||
let useAsDefault = this._document.getElementById("alwaysUse").getAttribute("checked");
|
||||
|
||||
let selectedItem = this._handlersList.selectedOptions[0];
|
||||
let subscribeCallback = () => {
|
||||
let feedReader = null;
|
||||
let settings = {
|
||||
feedType,
|
||||
useAsDefault,
|
||||
// Pull the title and subtitle out of the document
|
||||
feedTitle: this._document.getElementById(TITLE_ID).textContent,
|
||||
feedSubtitle: this._document.getElementById(SUBTITLE_ID).textContent,
|
||||
feedLocation: this._window.location.href,
|
||||
};
|
||||
switch (selectedItem.id) {
|
||||
case "selectedAppMenuItem":
|
||||
feedReader = "client";
|
||||
break;
|
||||
case "defaultHandlerMenuItem":
|
||||
feedReader = "default";
|
||||
break;
|
||||
case "liveBookmarksMenuItem":
|
||||
defaultHandler = "bookmarks";
|
||||
feedReader = "bookmarks";
|
||||
break;
|
||||
}
|
||||
settings.reader = feedReader;
|
||||
|
||||
// If "Always use..." is checked, we should set PREF_*SELECTED_ACTION
|
||||
// to either "reader" (If an application is selected),
|
||||
// or to "bookmarks" (if the live bookmarks option is selected).
|
||||
// Otherwise, we should set it to "ask"
|
||||
if (!useAsDefault) {
|
||||
defaultHandler = "ask";
|
||||
}
|
||||
settings.action = defaultHandler;
|
||||
LOG(`FeedWriter:SetFeedPrefsAndSubscribe - ${JSON.stringify(settings)}`);
|
||||
this._mm.sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribe",
|
||||
settings);
|
||||
};
|
||||
|
||||
// Show the file picker before subscribing if the
|
||||
// choose application menuitem was chosen using the keyboard
|
||||
if (selectedItem.id == "chooseApplicationMenuItem") {
|
||||
this._chooseClientApp(aResult => {
|
||||
if (aResult) {
|
||||
selectedItem =
|
||||
this._handlersList.selectedOptions[0];
|
||||
subscribeCallback();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
subscribeCallback();
|
||||
}
|
||||
},
|
||||
|
||||
classID: FEEDWRITER_CID,
|
||||
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver,
|
||||
Ci.nsIDOMGlobalPropertyInitializer]),
|
||||
|
@ -36,6 +36,18 @@
|
||||
<p id="feedSubscriptionInfo1" />
|
||||
<p id="feedSubscriptionInfo2" />
|
||||
</div>
|
||||
<div id="feedSubscribeLine">
|
||||
<label id="subscribeUsingDescription">
|
||||
<select id="handlersMenuList">
|
||||
<option id="liveBookmarksMenuItem" selected="true">&feedLiveBookmarks;</option>
|
||||
<option disabled="true">━━━━━━━</option>
|
||||
</select>
|
||||
</label>
|
||||
<label id="checkboxText">
|
||||
<input type="checkbox" id="alwaysUse" class="alwaysUse" checked="false"/>
|
||||
</label>
|
||||
<button id="subscribeButton">&feedSubscribeNow;</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="feedHeaderContainerSpacer"/>
|
||||
</div>
|
||||
|
@ -1,3 +1,6 @@
|
||||
[browser_registerProtocolHandler_notification.js]
|
||||
support-files =
|
||||
browser_registerProtocolHandler_notification.html
|
||||
[browser_telemetry_checks.js]
|
||||
support-files =
|
||||
valid-feed.xml
|
||||
|
@ -0,0 +1,96 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function() {
|
||||
function getSnapShot() {
|
||||
return Services.telemetry.snapshotScalars(Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT, false);
|
||||
}
|
||||
const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
|
||||
const FEED_URI = TEST_PATH + "valid-feed.xml";
|
||||
|
||||
// Ensure we don't have any pre-existing telemetry that'd mess up counts in here:
|
||||
Services.telemetry.clearScalars();
|
||||
const kScalarPrefix = "browser.feeds.";
|
||||
const kPreviewLoaded = kScalarPrefix + "preview_loaded";
|
||||
const kSubscribed = kScalarPrefix + "feed_subscribed";
|
||||
const kLivemarkCount = kScalarPrefix + "livebookmark_count";
|
||||
const kLivemarkOpened = kScalarPrefix + "livebookmark_opened";
|
||||
const kLivemarkItemOpened = kScalarPrefix + "livebookmark_item_opened";
|
||||
|
||||
let scalarForContent = gMultiProcessBrowser ? "content" : "parent";
|
||||
let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, FEED_URI);
|
||||
// Ensure we get telemetry from the content process:
|
||||
let previewCount = await TestUtils.waitForCondition(() => {
|
||||
let snapshot = getSnapShot()[scalarForContent];
|
||||
return snapshot && snapshot[kPreviewLoaded];
|
||||
});
|
||||
Assert.equal(previewCount, 1, "Should register the preview in telemetry.");
|
||||
|
||||
// Now check subscription. We stub out the actual code for adding the live bookmark,
|
||||
// because the dialog creates an initial copy of the bookmark when it's opened and
|
||||
// that's hard to deal with deterministically in tests.
|
||||
let old = PlacesCommandHook.addLiveBookmark;
|
||||
let createBMPromise = new Promise(resolve => {
|
||||
PlacesCommandHook.addLiveBookmark = function(...args) {
|
||||
resolve(args);
|
||||
// Return the promise because Feeds.jsm expects a promise:
|
||||
return createBMPromise;
|
||||
};
|
||||
});
|
||||
registerCleanupFunction(() => PlacesCommandHook.addLiveBookmark = old);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter("#subscribeButton", {}, tab.linkedBrowser);
|
||||
let bmArgs = await createBMPromise;
|
||||
Assert.deepEqual(bmArgs, [FEED_URI, "Example Feed"], "Should have been trying to subscribe");
|
||||
let snapshot = getSnapShot();
|
||||
Assert.equal(snapshot.parent[kSubscribed], 1, "Should have subscribed once.");
|
||||
|
||||
// Now manually add a livemark in the menu and one in the bookmarks toolbar:
|
||||
let livemarks = await Promise.all([
|
||||
PlacesUtils.livemarks.addLivemark({
|
||||
parentGuid: PlacesUtils.bookmarks.menuGuid,
|
||||
feedURI: Services.io.newURI(FEED_URI),
|
||||
}),
|
||||
PlacesUtils.livemarks.addLivemark({
|
||||
parentGuid: PlacesUtils.bookmarks.toolbarGuid,
|
||||
feedURI: Services.io.newURI(FEED_URI),
|
||||
}),
|
||||
]);
|
||||
registerCleanupFunction(async () => {
|
||||
for (let mark of livemarks) {
|
||||
await PlacesUtils.livemarks.removeLivemark(mark);
|
||||
}
|
||||
});
|
||||
|
||||
if (document.getElementById("PersonalToolbar").getAttribute("collapsed") == "true") {
|
||||
CustomizableUI.setToolbarVisibility("PersonalToolbar", true);
|
||||
registerCleanupFunction(() => CustomizableUI.setToolbarVisibility("PersonalToolbar", false));
|
||||
}
|
||||
|
||||
// Force updating telemetry:
|
||||
let {PlacesDBUtils} = ChromeUtils.import("resource://gre/modules/PlacesDBUtils.jsm", {});
|
||||
await PlacesDBUtils._telemetryForFeeds();
|
||||
Assert.equal(getSnapShot().parent[kLivemarkCount], 2,
|
||||
"Should have created two livemarks and counted them.");
|
||||
|
||||
info("Waiting for livemark");
|
||||
// Check we count opening the livemark popup:
|
||||
let livemarkOnToolbar = await TestUtils.waitForCondition(
|
||||
() => document.querySelector("#PersonalToolbar .bookmark-item[livemark]"));
|
||||
let popup = livemarkOnToolbar.querySelector("menupopup");
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
info("Clicking on livemark");
|
||||
// Using .click() or .doCommand() doesn't seem to work.
|
||||
EventUtils.synthesizeMouseAtCenter(livemarkOnToolbar, {});
|
||||
await popupShownPromise;
|
||||
Assert.equal(getSnapShot().parent[kLivemarkOpened], 1, "Should count livemark opening");
|
||||
|
||||
// And opening an item in the popup:
|
||||
let item = await TestUtils.waitForCondition(
|
||||
() => popup.querySelector("menuitem.bookmark-item"));
|
||||
item.doCommand();
|
||||
Assert.equal(getSnapShot().parent[kLivemarkItemOpened], 1, "Should count livemark item opening");
|
||||
popup.hidePopup();
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
23
browser/components/feeds/test/browser/valid-feed.xml
Normal file
23
browser/components/feeds/test/browser/valid-feed.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
|
||||
<title>Example Feed</title>
|
||||
<link href="http://example.org/"/>
|
||||
<updated>2010-08-22T18:30:02Z</updated>
|
||||
|
||||
<author>
|
||||
<name>John Doe</name>
|
||||
</author>
|
||||
<id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id>
|
||||
|
||||
<entry>
|
||||
|
||||
<title>Item</title>
|
||||
<link href="http://example.org/first"/>
|
||||
<id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id>
|
||||
<updated>2010-08-22T18:30:02Z</updated>
|
||||
|
||||
<summary>Some text.</summary>
|
||||
</entry>
|
||||
|
||||
</feed>
|
23
browser/components/feeds/test/bug589543-data.xml
Normal file
23
browser/components/feeds/test/bug589543-data.xml
Normal file
@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
|
||||
<title>Example Feed</title>
|
||||
<link href="http://example.org/"/>
|
||||
<updated>2010-08-22T18:30:02Z</updated>
|
||||
|
||||
<author>
|
||||
<name>John Doe</name>
|
||||
</author>
|
||||
<id>urn:uuid:e2df8375-99be-4848-b05e-b9d407555267</id>
|
||||
|
||||
<entry>
|
||||
|
||||
<title>Item</title>
|
||||
<link href="http://example.org/first"/>
|
||||
<id>urn:uuid:9e0f4bed-33d3-4a9d-97ab-ecaa31b3f14a</id>
|
||||
<updated>2010-08-22T18:30:02Z</updated>
|
||||
|
||||
<summary>Some text.</summary>
|
||||
</entry>
|
||||
|
||||
</feed>
|
@ -7,6 +7,7 @@ support-files =
|
||||
bug408328-data.xml
|
||||
bug436801-data.xml
|
||||
bug494328-data.xml
|
||||
bug589543-data.xml
|
||||
valid-feed.xml
|
||||
valid-unsniffable-feed.xml
|
||||
|
||||
@ -16,5 +17,6 @@ support-files =
|
||||
bug364677-data.xml^headers^
|
||||
[test_bug436801.html]
|
||||
[test_bug494328.html]
|
||||
[test_bug589543.html]
|
||||
[test_registerHandler.html]
|
||||
[test_registerHandler_disabled.html]
|
||||
|
32
browser/components/feeds/test/test_bug589543.html
Normal file
32
browser/components/feeds/test/test_bug589543.html
Normal file
@ -0,0 +1,32 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=589543
|
||||
-->
|
||||
<head>
|
||||
<title>Test feed preview subscribe UI</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=589543">Mozilla Bug 589543</a>
|
||||
<p id="display"><iframe id="testFrame" src="bug589543-data.xml"></iframe></p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/** Test for Bug 589543 **/
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
addLoadEvent(function() {
|
||||
var doc = SpecialPowers.wrap($("testFrame")).contentDocument;
|
||||
var popup = doc.getElementById("handlersMenuList");
|
||||
isnot(popup, null, "Feed preview should have a handlers popup");
|
||||
});
|
||||
addLoadEvent(SimpleTest.finish);
|
||||
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
@ -194,6 +194,8 @@ These should match what Safari and other Apple applications use on OS X Lion. --
|
||||
<!ENTITY editThisBookmarkCmd.label "Edit This Bookmark">
|
||||
<!ENTITY bookmarkThisPageCmd.commandkey "d">
|
||||
|
||||
<!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
|
||||
<!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
|
||||
<!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
|
||||
<!ENTITY showAllBookmarks2.label "Show All Bookmarks">
|
||||
<!ENTITY recentBookmarks.label "Recently Bookmarked">
|
||||
|
@ -73,6 +73,9 @@ paste-button.label = Paste
|
||||
# LOCALIZATION NOTE(paste-button.tooltiptext2): %S is the keyboard shortcut.
|
||||
paste-button.tooltiptext2 = Paste (%S)
|
||||
|
||||
feed-button.label = Subscribe
|
||||
feed-button.tooltiptext2 = Subscribe to this page
|
||||
|
||||
# LOCALIZATION NOTE (characterencoding-button2.label): The \u00ad text at the beginning
|
||||
# of the string is used to disable auto hyphenation on the button text when it is displayed
|
||||
# in the menu panel.
|
||||
|
@ -44,6 +44,9 @@
|
||||
<!ENTITY mediaSaveAs2.accesskey "e">
|
||||
<!ENTITY mediaPreview "Media Preview:">
|
||||
|
||||
<!ENTITY feedTab "Feeds">
|
||||
<!ENTITY feedTab.accesskey "F">
|
||||
|
||||
<!ENTITY permTab "Permissions">
|
||||
<!ENTITY permTab.accesskey "P">
|
||||
<!ENTITY permissionsFor "Permissions for:">
|
||||
|
@ -38,6 +38,12 @@ generalSize=%S KB (%S bytes)
|
||||
generalMetaTag=Meta (1 tag)
|
||||
generalMetaTags=Meta (%S tags)
|
||||
|
||||
feedRss=RSS
|
||||
feedAtom=Atom
|
||||
feedXML=XML
|
||||
feedSubscribe=Subscribe
|
||||
feedSubscribe.accesskey=u
|
||||
|
||||
securityNoOwner=This website does not supply ownership information.
|
||||
# LOCALIZATION NOTE (securityVisitsNumber):
|
||||
# Semi-colon list of plural forms.
|
||||
|
@ -6,6 +6,13 @@
|
||||
|
||||
var EXPORTED_SYMBOLS = [ "Feeds" ];
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserUtils",
|
||||
"resource://gre/modules/BrowserUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
|
||||
"resource:///modules/BrowserWindowTracker.jsm");
|
||||
|
||||
var Feeds = {
|
||||
// Listeners are added in nsBrowserGlue.js
|
||||
receiveMessage(aMessage) {
|
||||
@ -18,6 +25,48 @@ var Feeds = {
|
||||
aMessage.target);
|
||||
break;
|
||||
}
|
||||
|
||||
case "FeedConverter:addLiveBookmark": {
|
||||
let topWindow = BrowserWindowTracker.getTopWindow();
|
||||
topWindow.PlacesCommandHook.addLiveBookmark(data.spec, data.title)
|
||||
.catch(Cu.reportError);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* isValidFeed: checks whether the given data represents a valid feed.
|
||||
*
|
||||
* @param aLink
|
||||
* An object representing a feed with title, href and type.
|
||||
* @param aPrincipal
|
||||
* The principal of the document, used for security check.
|
||||
* @param aIsFeed
|
||||
* Whether this is already a known feed or not, if true only a security
|
||||
* check will be performed.
|
||||
*/
|
||||
isValidFeed(aLink, aPrincipal, aIsFeed) {
|
||||
if (!aLink || !aPrincipal)
|
||||
return false;
|
||||
|
||||
var type = aLink.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
|
||||
if (!aIsFeed) {
|
||||
aIsFeed = (type == "application/rss+xml" ||
|
||||
type == "application/atom+xml");
|
||||
}
|
||||
|
||||
if (aIsFeed) {
|
||||
try {
|
||||
let href = Services.io.newURI(aLink.href, aLink.ownerDocument.characterSet);
|
||||
BrowserUtils.urlSecurityCheck(href, aPrincipal,
|
||||
Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
|
||||
return type || "application/rss+xml";
|
||||
} catch (ex) {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
};
|
||||
|
6
browser/themes/shared/icons/feed.svg
Normal file
6
browser/themes/shared/icons/feed.svg
Normal file
@ -0,0 +1,6 @@
|
||||
<!-- 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/. -->
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<path fill="context-fill" fill-opacity="context-fill-opacity" d="M3.5 10A2.5 2.5 0 1 0 6 12.5 2.5 2.5 0 0 0 3.5 10zM2 1a1 1 0 0 0 0 2 10.883 10.883 0 0 1 11 11 1 1 0 0 0 2 0A12.862 12.862 0 0 0 2 1zm0 4a1 1 0 0 0 0 2 6.926 6.926 0 0 1 7 7 1 1 0 0 0 2 0 8.9 8.9 0 0 0-9-9z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 580 B |
@ -148,6 +148,7 @@
|
||||
skin/classic/browser/edit-copy.svg (../shared/icons/edit-copy.svg)
|
||||
skin/classic/browser/edit-cut.svg (../shared/icons/edit-cut.svg)
|
||||
skin/classic/browser/edit-paste.svg (../shared/icons/edit-paste.svg)
|
||||
skin/classic/browser/feed.svg (../shared/icons/feed.svg)
|
||||
skin/classic/browser/folder.svg (../shared/icons/folder.svg)
|
||||
skin/classic/browser/forget.svg (../shared/icons/forget.svg)
|
||||
skin/classic/browser/forward.svg (../shared/icons/forward.svg)
|
||||
|
@ -212,6 +212,10 @@ toolbar[brighttext] {
|
||||
list-style-image: url("chrome://browser/skin/tab.svg");
|
||||
}
|
||||
|
||||
#feed-button {
|
||||
list-style-image: url("chrome://browser/skin/feed.svg");
|
||||
}
|
||||
|
||||
#characterencoding-button {
|
||||
list-style-image: url("chrome://browser/skin/characterEncoding.svg");
|
||||
}
|
||||
|
@ -516,6 +516,10 @@
|
||||
<parameter name="aEvent"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
// Delete the feeds cache if we're hiding the topmost page
|
||||
// (as opposed to one of its iframes).
|
||||
if (this.feeds && aEvent.target == this.contentDocument)
|
||||
this.feeds = null;
|
||||
if (!this.docShell || !this.fastFind)
|
||||
return;
|
||||
var tabBrowser = this.getTabBrowser();
|
||||
@ -897,6 +901,9 @@
|
||||
this._fastFind = null;
|
||||
this._webBrowserFind = null;
|
||||
|
||||
// The feeds cache can keep the document inside this browser alive.
|
||||
this.feeds = null;
|
||||
|
||||
this.lastURI = null;
|
||||
|
||||
if (!this.isRemoteBrowser) {
|
||||
|
Loading…
Reference in New Issue
Block a user