Merge mozilla-central to inbound. a=merge CLOSED TREE

This commit is contained in:
Margareta Eliza Balazs 2018-09-18 17:38:17 +03:00
Родитель f3ecaf9a73 fdb780335d
Коммит 0a37599745
219 изменённых файлов: 12231 добавлений и 16265 удалений

Просмотреть файл

@ -11,3 +11,4 @@ skip-if = e10s
[browser_test_textcaret.js]
[browser_test_focus_browserui.js]
[browser_test_focus_dialog.js]
[browser_test_focus_urlbar.js]

Просмотреть файл

@ -0,0 +1,145 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* import-globals-from ../../mochitest/states.js */
/* import-globals-from ../../mochitest/role.js */
loadScripts({ name: "states.js", dir: MOCHITESTS_DIR },
{ name: "role.js", dir: MOCHITESTS_DIR });
ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
"resource://testing-common/PlacesTestUtils.jsm");
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
function isEventForAutocompleteItem(event) {
return event.accessible.role == ROLE_COMBOBOX_OPTION;
}
/**
* Wait for an autocomplete search to finish.
* This is necessary to ensure predictable results, as these searches are
* async. Pressing down arrow will use results from the previous input if the
* search isn't finished yet.
*/
function waitForSearchFinish() {
return BrowserTestUtils.waitForCondition(() =>
(gURLBar.popupOpen && gURLBar.controller.searchStatus >=
Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH),
"Waiting for search to complete");
}
// Check that the URL bar manages accessibility focus appropriately.
async function runTests() {
registerCleanupFunction(async function() {
await PlacesUtils.history.clear();
});
await PlacesTestUtils.addVisits([
{uri: makeURI("http://example1.com/blah")},
{uri: makeURI("http://example2.com/blah")},
{uri: makeURI("http://example1.com/")},
{uri: makeURI("http://example2.com/")}
]);
let focused = waitForEvent(EVENT_FOCUS,
event => event.accessible.role == ROLE_ENTRY);
gURLBar.focus();
let event = await focused;
let textBox = event.accessible;
// Ensure the URL bar is ready for a new URL to be typed.
// Sometimes, when this test runs, the existing text isn't selected when the
// URL bar is focused. Pressing escape twice ensures that the popup is
// closed and that the existing text is selected.
EventUtils.synthesizeKey("KEY_Escape");
EventUtils.synthesizeKey("KEY_Escape");
info("Ensuring no focus change when first text is typed");
EventUtils.sendString("example");
await waitForSearchFinish();
// Wait a tick for a11y events to fire.
await TestUtils.waitForTick();
testStates(textBox, STATE_FOCUSED);
info("Ensuring no focus change on backspace");
EventUtils.synthesizeKey("KEY_Backspace");
await waitForSearchFinish();
// Wait a tick for a11y events to fire.
await TestUtils.waitForTick();
testStates(textBox, STATE_FOCUSED);
info("Ensuring no focus change on text selection and delete");
EventUtils.synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
EventUtils.synthesizeKey("KEY_Delete");
await waitForSearchFinish();
// Wait a tick for a11y events to fire.
await TestUtils.waitForTick();
testStates(textBox, STATE_FOCUSED);
info("Ensuring autocomplete focus on down arrow");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowDown");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring focus of another autocomplete item on down arrow");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowDown");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring focus of another autocomplete item on up arrow");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowUp");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring text box focus on left arrow");
focused = waitForEvent(EVENT_FOCUS, textBox);
EventUtils.synthesizeKey("KEY_ArrowLeft");
await focused;
testStates(textBox, STATE_FOCUSED);
// On Mac, down arrow when not at the end of the field moves to the end.
// Move back to the end so the next press of down arrow opens the popup.
EventUtils.synthesizeKey("KEY_ArrowRight");
info("Ensuring autocomplete focus on down arrow");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowDown");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring text box focus when text is typed");
focused = waitForEvent(EVENT_FOCUS, textBox);
EventUtils.sendString("z");
await focused;
testStates(textBox, STATE_FOCUSED);
EventUtils.synthesizeKey("KEY_Backspace");
await waitForSearchFinish();
info("Ensuring autocomplete focus on down arrow");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowDown");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring text box focus on backspace");
focused = waitForEvent(EVENT_FOCUS, textBox);
EventUtils.synthesizeKey("KEY_Backspace");
await focused;
testStates(textBox, STATE_FOCUSED);
info("Ensuring autocomplete focus on down arrow");
focused = waitForEvent(EVENT_FOCUS, isEventForAutocompleteItem);
EventUtils.synthesizeKey("KEY_ArrowDown");
event = await focused;
testStates(event.accessible, STATE_FOCUSED);
info("Ensuring text box focus on text selection");
focused = waitForEvent(EVENT_FOCUS, textBox);
EventUtils.synthesizeKey("KEY_ArrowLeft", {shiftKey: true});
await focused;
testStates(textBox, STATE_FOCUSED);
}
addAccessibleTask(``, runTests);

Просмотреть файл

@ -9,8 +9,6 @@ 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");
@ -97,7 +95,6 @@ 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 = {};
@ -108,22 +105,6 @@ 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,7 +10,6 @@ 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",
});
@ -34,7 +33,6 @@ 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);
@ -98,36 +96,6 @@ 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,170 +114,6 @@ 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,26 +400,6 @@
<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,7 +3661,6 @@ 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);
@ -3671,11 +3670,6 @@ 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);
@ -4708,9 +4702,6 @@ 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;
}
@ -4940,7 +4931,6 @@ var XULBrowserWindow = {
},
asyncUpdateUI() {
FeedHandler.updateFeeds();
BrowserSearch.updateOpenSearchBadge();
},

Просмотреть файл

@ -1,60 +0,0 @@
/* -*- 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,14 +12,6 @@
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 feeds.js */
/* import-globals-from ../utilityOverlay.js */
/* import-globals-from permissions.js */
/* import-globals-from security.js */
@ -333,12 +333,6 @@ 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.
@ -346,7 +340,7 @@ function loadPageInfo(frameOuterWindowID, imageElement, browser) {
let pageInfoData;
// Get initial pageInfoData needed to display the general, feeds, permission and security tabs.
// Get initial pageInfoData needed to display the general, permission and security tabs.
mm.addMessageListener("PageInfo:data", function onmessage(message) {
mm.removeMessageListener("PageInfo:data", onmessage);
pageInfoData = message.data;
@ -365,7 +359,6 @@ 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);
});
@ -409,11 +402,6 @@ 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(); });
@ -435,7 +423,6 @@ function doHelpButton() {
const helpTopics = {
"generalPanel": "pageinfo_general",
"mediaPanel": "pageinfo_media",
"feedPanel": "pageinfo_feed",
"permPanel": "pageinfo_permissions",
"securityPanel": "pageinfo_security",
};

Просмотреть файл

@ -32,7 +32,6 @@
<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"/>
@ -74,8 +73,6 @@
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;"
@ -264,11 +261,6 @@
</hbox>
</vbox>
<!-- Feeds -->
<vbox id="feedPanel">
<richlistbox id="feedListbox" flex="1"/>
</vbox>
<!-- Permissions -->
<vbox id="permPanel">
<hbox id="permHostBox">

Просмотреть файл

@ -33,7 +33,6 @@ 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
@ -96,8 +95,6 @@ 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]
@ -338,9 +335,6 @@ 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^

Просмотреть файл

@ -1,68 +0,0 @@
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");
}

Просмотреть файл

@ -1,33 +0,0 @@
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);
}
}
}
});
});

Просмотреть файл

@ -1,78 +0,0 @@
<!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,8 +1,5 @@
[DEFAULT]
[browser_pageInfo.js]
support-files =
../general/feed_tab.html
[browser_pageinfo_firstPartyIsolation.js]
support-files =
image.html

Просмотреть файл

@ -1,39 +0,0 @@
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,10 +105,6 @@
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;
}
@ -147,7 +143,6 @@
: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 {

Просмотреть файл

@ -311,6 +311,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
break;
case KeyEvent.DOM_VK_TAB:
this.userSelectionBehavior = "tab";
// The user is explicitly making a selection, so the popup
// should get accessibility focus.
this.popup.richlistbox.suppressMenuItemEvent = false;
break;
case KeyEvent.DOM_VK_UP:
case KeyEvent.DOM_VK_DOWN:
@ -318,6 +321,9 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
case KeyEvent.DOM_VK_PAGE_DOWN:
if (this.userSelectionBehavior != "tab")
this.userSelectionBehavior = "arrow";
// The user is explicitly making a selection, so the popup
// should get accessibility focus.
this.popup.richlistbox.suppressMenuItemEvent = false;
break;
}
if (!this.popup.disableKeyNavigation) {
@ -1756,6 +1762,15 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
} else {
this.removeAttribute("usertyping");
}
// If the popup already had accessibility focus, bring it back to
// the input, since the user is editing.
if (!this.popup.richlistbox.suppressMenuItemEvent &&
this.popup.richlistbox.currentItem) {
this.popup.richlistbox.currentItem._fireEvent("DOMMenuItemInactive");
}
// The user is typing, so don't give accessibility focus to the
// popup, even if an item gets automatically selected.
this.popup.richlistbox.suppressMenuItemEvent = true;
// Only wait for a result when we are sure to get one. In some
// cases, like when pasting the same exact text, we may not fire
// a new search and we won't get a result.
@ -2434,6 +2449,15 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
window.windowUtils.getBoundsWithoutFlushing(document.getElementById("nav-bar")).bottom -
window.windowUtils.getBoundsWithoutFlushing(aInput).bottom);
if (!this.richlistbox.suppressMenuItemEvent && this.richlistbox.currentItem) {
// The richlistbox fired a DOMMenuItemActive for accessibility,
// but because the popup isn't open yet, accessibility will ignore
// it. Therefore, fire it again once the popup opens.
this.addEventListener("popupshown", () => {
this.richlistbox.currentItem._fireEvent("DOMMenuItemActive");
}, {once: true});
}
this.openPopup(aElement, "after_start", 0, yOffset, false, false);
// Do this immediately after we've requested the popup to open. This
@ -2726,11 +2750,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
// If nothing is selected yet, select the first result if it is a
// pre-selected "heuristic" result. (See UnifiedComplete.js.)
if (this.selectedIndex == -1 && this._isFirstResultHeuristic) {
// Don't fire DOMMenuItemActive so that screen readers still see
// the input as being focused.
this.richlistbox.suppressMenuItemEvent = true;
this.input.controller.setInitiallySelectedIndex(0);
this.richlistbox.suppressMenuItemEvent = false;
}
// If this is the first time we get the result from the current
// search and we are not in the private context, we can speculatively

Просмотреть файл

@ -82,7 +82,6 @@ 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 = 14;
var kVersion = 15;
/**
* Buttons removed from built-ins by version they were removed. kVersion must be
@ -63,6 +63,7 @@ var kVersion = 14;
* version the button is removed in as the value. e.g. "pocket-button": 5
*/
var ObsoleteBuiltinButtons = {
"feed-button": 15,
};
/**

Просмотреть файл

@ -329,44 +329,6 @@ 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,8 +2,6 @@
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]
@ -95,8 +93,6 @@ 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("feed-button", CustomizableUI.AREA_NAVBAR);
CustomizableUI.addWidgetToArea("save-page-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("feed-button", CustomizableUI.AREA_NAVBAR);
CustomizableUI.addWidgetToArea("save-page-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 = "feed-button";
const kAPIWidgetId = "save-page-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("feed-button");
let btn = document.getElementById("new-window-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(["feed-button", lastButton]);
let placementsAfterInsert = placements.slice(0, lastButtonIndex).concat(["new-window-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("feed-button");
let btn = document.getElementById("new-window-button");
let panel = document.getElementById(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
let placements = getAreaWidgetIds(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
let placementsAfterAppend = placements.concat(["feed-button"]);
let placementsAfterAppend = placements.concat(["new-window-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("feed-button");
let btn = document.getElementById("new-window-button");
let panel = document.getElementById(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
assertAreaPlacements(panel.id, []);
let placementsAfterAppend = ["feed-button"];
let placementsAfterAppend = ["new-window-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", "feed-button,sync-button,home-button");
let newCurrentSet = currentSet.replace("home-button", "new-window-button,sync-button,home-button");
navbar.currentSet = newCurrentSet;
is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
let feedBtn = document.getElementById("feed-button");
let feedBtn = document.getElementById("new-window-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", "feed-button");
let newCurrentSet = currentSet.replace("home-button", "new-window-button");
navbar.currentSet = newCurrentSet;
is(newCurrentSet, navbar.currentSet, "Current set should match expected current set.");
let feedBtn = document.getElementById("feed-button");
let feedBtn = document.getElementById("new-window-button");
ok(feedBtn, "Feed button should have been added.");
let homeBtn = document.getElementById("home-button");
ok(!homeBtn, "Home button should have been removed.");

Просмотреть файл

@ -1,62 +0,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/. */
"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: ["feed-button"] });
defaultPlacements: ["new-window-button"] });
} catch (ex) {
exceptionThrown = ex;
}

Просмотреть файл

@ -1,10 +0,0 @@
<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>

Просмотреть файл

@ -1,23 +0,0 @@
<?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,7 +120,6 @@ 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"],
@ -193,7 +192,6 @@ 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"],
@ -233,7 +231,6 @@ add_task(async function test_some_icons() {
["synced_tabs", ""],
["open_file", ""],
["sidebars", ""],
["subscribe", ""],
["text_encoding", ""],
["email_link", ""],
["forget", ""],

Просмотреть файл

@ -341,12 +341,6 @@ FeedResultService.prototype = {
subtitle,
feedHandler: "default" });
break;
default:
// fall through
case "bookmarks":
Services.cpmm.sendAsyncMessage("FeedConverter:addLiveBookmark",
{ spec, title });
break;
}
},

Просмотреть файл

@ -68,15 +68,7 @@ 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 = {
@ -146,24 +138,6 @@ 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".
@ -193,25 +167,6 @@ 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
@ -472,234 +427,6 @@ 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.
@ -735,7 +462,6 @@ FeedWriter.prototype = {
_document: null,
_feedURI: null,
_feedPrincipal: null,
_handlersList: null,
// BrowserFeedWriter WebIDL methods
init(aWindow) {
@ -749,73 +475,10 @@ 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() {
@ -840,21 +503,12 @@ 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() {
@ -866,69 +520,6 @@ 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,18 +36,6 @@
<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">&#x2501;&#x2501;&#x2501;&#x2501;&#x2501;&#x2501;&#x2501;</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,6 +1,3 @@
[browser_registerProtocolHandler_notification.js]
support-files =
browser_registerProtocolHandler_notification.html
[browser_telemetry_checks.js]
support-files =
valid-feed.xml

Просмотреть файл

@ -1,96 +0,0 @@
/* 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);
});

Просмотреть файл

@ -1,23 +0,0 @@
<?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>

Просмотреть файл

@ -1,23 +0,0 @@
<?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,7 +7,6 @@ support-files =
bug408328-data.xml
bug436801-data.xml
bug494328-data.xml
bug589543-data.xml
valid-feed.xml
valid-unsniffable-feed.xml
@ -17,6 +16,5 @@ support-files =
bug364677-data.xml^headers^
[test_bug436801.html]
[test_bug494328.html]
[test_bug589543.html]
[test_registerHandler.html]
[test_registerHandler_disabled.html]

Просмотреть файл

@ -1,32 +0,0 @@
<!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,8 +194,6 @@ 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,9 +73,6 @@ 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,9 +44,6 @@
<!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,12 +38,6 @@ 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,13 +6,6 @@
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) {
@ -25,48 +18,6 @@ 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;
},
};

Просмотреть файл

@ -1,6 +0,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/. -->
<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>

До

Ширина:  |  Высота:  |  Размер: 580 B

Просмотреть файл

@ -148,7 +148,6 @@
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,10 +212,6 @@ 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");
}

Просмотреть файл

@ -75,7 +75,7 @@ class ConnectPage extends PureComponent {
render() {
return dom.article(
{
className: "page connect-page",
className: "page connect-page js-connect-page",
},
dom.h1(
{

Просмотреть файл

@ -39,7 +39,7 @@ class NetworkLocationsForm extends PureComponent {
},
dom.span({}, "Host:port"),
dom.input({
className: "connect-page__network-form__input",
className: "connect-page__network-form__input js-network-form-input",
placeholder: "localhost:6080",
type: "text",
value: this.state.value,
@ -49,7 +49,7 @@ class NetworkLocationsForm extends PureComponent {
}
}),
dom.button({
className: "aboutdebugging-button"
className: "aboutdebugging-button js-network-form-submit-button"
}, "Add")
);
}

Просмотреть файл

@ -24,17 +24,17 @@ class NetworkLocationsList extends PureComponent {
this.props.networkLocations.map(location =>
dom.li(
{
className: "connect-page__network-location"
className: "connect-page__network-location js-network-location"
},
dom.span(
{
className: "ellipsis-text"
className: "ellipsis-text js-network-location-value"
},
location
),
dom.button(
{
className: "aboutdebugging-button",
className: "aboutdebugging-button js-network-location-remove-button",
onClick: () => {
this.props.dispatch(Actions.removeNetworkLocation(location));
}

Просмотреть файл

@ -38,7 +38,7 @@ class SidebarItem extends PureComponent {
return dom.li(
{
className: "sidebar-item" +
className: "sidebar-item js-sidebar-item" +
(isSelected ?
" sidebar-item--selected js-sidebar-item-selected" :
""

Просмотреть файл

@ -7,3 +7,4 @@ support-files =
!/devtools/client/shared/test/telemetry-test-helpers.js
[browser_aboutdebugging_thisfirefox.js]
[browser_aboutdebugging_connect_networklocations.js]

Просмотреть файл

@ -0,0 +1,79 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/**
* Test the network locations form of the Connect page.
* Check that a network location can be added and removed.
*/
const TEST_NETWORK_LOCATION = "localhost:1111";
add_task(async function() {
const { document, tab } = await openAboutDebugging();
const sidebarItems = document.querySelectorAll(".js-sidebar-item");
const connectSidebarItem = [...sidebarItems].find(element => {
return element.textContent === "Connect";
});
ok(connectSidebarItem, "Sidebar contains a Connect item");
info("Click on the Connect item in the sidebar");
connectSidebarItem.click();
info("Wait until Connect page is displayed");
await waitUntil(() => document.querySelector(".js-connect-page"));
let networkLocations = document.querySelectorAll(".js-network-location");
is(networkLocations.length, 0, "By default, no network locations are displayed");
addNetworkLocation(TEST_NETWORK_LOCATION, document);
info("Wait until the new network location is visible in the list");
await waitUntil(() => document.querySelectorAll(".js-network-location").length === 1);
networkLocations = document.querySelectorAll(".js-network-location");
const networkLocationValue =
networkLocations[0].querySelector(".js-network-location-value");
is(networkLocationValue.textContent, TEST_NETWORK_LOCATION,
"Added network location has the expected value");
removeNetworkLocation(TEST_NETWORK_LOCATION, document);
info("Wait until the new network location is removed from the list");
await waitUntil(() => document.querySelectorAll(".js-network-location").length === 0);
await removeTab(tab);
});
function addNetworkLocation(location, document) {
info("Setting a value in the network form input");
const networkLocationInput =
document.querySelector(".js-network-form-input");
networkLocationInput.focus();
EventUtils.sendString(location, networkLocationInput.ownerGlobal);
info("Click on network form submit button");
const networkLocationSubmitButton =
document.querySelector(".js-network-form-submit-button");
networkLocationSubmitButton.click();
}
function removeNetworkLocation(location, document) {
const networkLocation = getNetworkLocation(location, document);
ok(networkLocation, "Network location container found.");
info("Click on the remove button for the provided network location");
const removeButton =
networkLocation.querySelector(".js-network-location-remove-button");
removeButton.click();
}
function getNetworkLocation(location, document) {
info("Find the container for network location: " + location);
const networkLocations = document.querySelectorAll(".js-network-location");
return [...networkLocations].find(element => {
return element.querySelector(".js-network-location-value").textContent === location;
});
}

Просмотреть файл

@ -20,9 +20,6 @@ const EXPECTED_TARGET_PANES = [
add_task(async function() {
const { document, tab } = await openAboutDebugging();
// Wait until the client connection was established.
await waitUntil(() => document.querySelector(".js-runtime-page"));
// Check that the selected sidebar item is "This Firefox"
const selectedSidebarItem = document.querySelector(".js-sidebar-item-selected");
ok(selectedSidebarItem, "An item is selected in the sidebar");

Просмотреть файл

@ -31,5 +31,8 @@ async function openAboutDebugging(page, win) {
info("Wait until the main about debugging container is available");
await waitUntil(() => document.querySelector(".app"));
info("Wait until the client connection was established");
await waitUntil(() => document.querySelector(".js-runtime-page"));
return { tab, document, window };
}

Просмотреть файл

@ -0,0 +1,27 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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 {
trackChange,
} = require("./actions/changes");
class ChangesManager {
constructor(inspector) {
this.store = inspector.store;
}
track(change) {
this.store.dispatch(trackChange(change));
}
destroy() {
this.store = null;
}
}
module.exports = ChangesManager;

Просмотреть файл

@ -0,0 +1,53 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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 { createFactory, createElement } = require("devtools/client/shared/vendor/react");
const { Provider } = require("devtools/client/shared/vendor/react-redux");
const ChangesApp = createFactory(require("./components/ChangesApp"));
const {
resetChanges,
} = require("./actions/changes");
class ChangesView {
constructor(inspector) {
this.inspector = inspector;
this.store = this.inspector.store;
this.destroy = this.destroy.bind(this);
this.init();
}
init() {
const changesApp = ChangesApp({});
// Expose the provider to let inspector.js use it in setupSidebar.
this.provider = createElement(Provider, {
id: "changesview",
key: "changesview",
store: this.store,
}, changesApp);
// TODO: save store and restore/replay on refresh.
// Bug 1478439 - https://bugzilla.mozilla.org/show_bug.cgi?id=1478439
this.inspector.target.once("will-navigate", this.destroy);
}
/**
* Destruction function called when the inspector is destroyed.
*/
destroy() {
this.store.dispatch(resetChanges());
this.inspector = null;
this.store = null;
}
}
module.exports = ChangesView;

Просмотреть файл

@ -0,0 +1,27 @@
/* 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 {
RESET_CHANGES,
TRACK_CHANGE,
} = require("./index");
module.exports = {
resetChanges() {
return {
type: RESET_CHANGES,
};
},
trackChange(data) {
return {
type: TRACK_CHANGE,
data,
};
},
};

Просмотреть файл

@ -0,0 +1,17 @@
/* 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 { createEnum } = require("devtools/client/shared/enum");
createEnum([
// Remove all changes
"RESET_CHANGES",
// Track a style change
"TRACK_CHANGE",
], module.exports);

Просмотреть файл

@ -0,0 +1,10 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
'changes.js',
'index.js',
)

Просмотреть файл

@ -0,0 +1,78 @@
/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { connect } = require("devtools/client/shared/vendor/react-redux");
class ChangesApp extends PureComponent {
static get propTypes() {
return {
changes: PropTypes.object.isRequired
};
}
renderMutations(remove = {}, add = {}) {
const removals = Object.entries(remove).map(([prop, value]) => {
return dom.div(
{ className: "line diff-remove"},
`${prop}: ${value};`
);
});
const additions = Object.entries(add).map(([prop, value]) => {
return dom.div(
{ className: "line diff-add"},
`${prop}: ${value};`
);
});
return [removals, additions];
}
renderSelectors(selectors = {}) {
return Object.keys(selectors).map(sel => {
return dom.details(
{ className: "selector", open: true },
dom.summary(
{
title: sel,
},
sel),
this.renderMutations(selectors[sel].remove, selectors[sel].add)
);
});
}
renderDiff(diff = {}) {
// Render groups of style sources: stylesheets, embedded styles and inline styles
return Object.keys(diff).map(href => {
return dom.details(
{ className: "source", open: true },
dom.summary(
{
title: href,
},
href),
// Render groups of selectors
this.renderSelectors(diff[href])
);
});
}
render() {
return dom.div(
{
className: "theme-sidebar inspector-tabpanel",
id: "sidebar-panel-changes"
},
this.renderDiff(this.props.changes.diff)
);
}
}
module.exports = connect(state => state)(ChangesApp);

Просмотреть файл

@ -0,0 +1,9 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
'ChangesApp.js',
)

Просмотреть файл

@ -0,0 +1,16 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DIRS += [
'actions',
'components',
'reducers',
]
DevToolsModules(
'ChangesManager.js',
'ChangesView.js',
)

Просмотреть файл

@ -0,0 +1,128 @@
/* 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 {
RESET_CHANGES,
TRACK_CHANGE,
} = require("../actions/index");
const INITIAL_STATE = {
/**
* Diff of changes grouped by stylesheet href, then by selector, then into add/remove
* objects with CSS property names and values for corresponding changes.
*
* Structure:
*
* diff = {
* "href": {
* "selector": {
* add: {
* "property": value
* ... // more properties
* },
* remove: {
* "property": value
* ...
* }
* },
* ... // more selectors
* }
* ... // more stylesheet hrefs
* }
*/
diff: {},
};
/**
* Mutate the given diff object with data about a new change.
*
* @param {Object} diff
* Diff object from the store.
* @param {Object} change
* Data about the change: which property was added or removed, on which selector,
* in which stylesheet or whether the source is an element's inline style.
* @return {Object}
* Mutated diff object.
*/
function updateDiff(diff = {}, change = {}) {
// Ensure expected diff structure exists
diff[change.href] = diff[change.href] || {};
diff[change.href][change.selector] = diff[change.href][change.selector] || {};
diff[change.href][change.selector].add = diff[change.href][change.selector].add || {};
diff[change.href][change.selector].remove = diff[change.href][change.selector].remove
|| {};
// Reference to the diff data for this stylesheet/selector pair.
const ref = diff[change.href][change.selector];
// Track the remove operation only if the property WAS NOT previously introduced by an
// add operation. This ensures that repeated changes of the same property show up as a
// single remove operation of the original value.
if (change.remove && change.remove.property && !ref.add[change.remove.property]) {
ref.remove[change.remove.property] = change.remove.value;
}
if (change.add && change.add.property) {
ref.add[change.add.property] = change.add.value;
}
const propertyName = change.add && change.add.property ||
change.remove && change.remove.property;
// Remove information about operations on the property if they cancel each other out.
if (ref.add[propertyName] === ref.remove[propertyName]) {
delete ref.add[propertyName];
delete ref.remove[propertyName];
// Remove information about the selector if there are no changes to its declarations.
if (Object.keys(ref.add).length === 0 && Object.keys(ref.remove).length === 0) {
delete diff[change.href][change.selector];
}
// Remove information about the stylesheet if there are no changes to its rules.
if (Object.keys(diff[change.href]).length === 0) {
delete diff[change.href];
}
}
ref.tag = change.tag;
return diff;
}
const reducers = {
[TRACK_CHANGE](state, { data }) {
const defaults = {
href: "",
selector: "",
tag: null,
add: null,
remove: null
};
data = { ...defaults, ...data };
// Update the state in-place with data about a style change (no deep clone of state).
// TODO: redefine state as a shallow object structure after figuring how to track
// both CSS Declarations and CSS Rules and At-Rules (@media, @keyframes, etc).
// @See https://bugzilla.mozilla.org/show_bug.cgi?id=1491263
return Object.assign({}, { diff: updateDiff(state.diff, data) });
},
[RESET_CHANGES](state) {
return INITIAL_STATE;
},
};
module.exports = function(state = INITIAL_STATE, action) {
const reducer = reducers[action.type];
if (!reducer) {
return state;
}
return reducer(state, action);
};

Просмотреть файл

@ -0,0 +1,9 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
'changes.js',
)

Просмотреть файл

@ -12,6 +12,7 @@
<link rel="stylesheet" href="chrome://devtools/skin/inspector.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/rules.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/computed.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/changes.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/fonts.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/boxmodel.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/layout.css"/>

Просмотреть файл

@ -37,6 +37,8 @@ loader.lazyRequireGetter(this, "clipboardHelper", "devtools/shared/platform/clip
loader.lazyRequireGetter(this, "openContentLink", "devtools/client/shared/link", true);
loader.lazyRequireGetter(this, "getScreenshotFront", "devtools/shared/fronts/screenshot", true);
loader.lazyRequireGetter(this, "saveScreenshot", "devtools/shared/screenshot/save");
loader.lazyRequireGetter(this, "ChangesManager",
"devtools/client/inspector/changes/ChangesManager");
loader.lazyImporter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm");
@ -67,6 +69,7 @@ const THREE_PANE_ENABLED_PREF = "devtools.inspector.three-pane-enabled";
const THREE_PANE_ENABLED_SCALAR = "devtools.inspector.three_pane_enabled";
const THREE_PANE_CHROME_ENABLED_PREF = "devtools.inspector.chrome.three-pane-enabled";
const TELEMETRY_EYEDROPPER_OPENED = "devtools.toolbar.eyedropper.opened";
const TRACK_CHANGES_ENABLED = "devtools.inspector.changes.enabled";
/**
* Represents an open instance of the Inspector for a tab.
@ -122,6 +125,9 @@ function Inspector(toolbox) {
this.reflowTracker = new ReflowTracker(this._target);
this.styleChangeTracker = new InspectorStyleChangeTracker(this);
if (Services.prefs.getBoolPref(TRACK_CHANGES_ENABLED)) {
this.changesManager = new ChangesManager(this);
}
// Store the URL of the target page prior to navigation in order to ensure
// telemetry counts in the Grid Inspector are not double counted on reload.
@ -979,6 +985,32 @@ Inspector.prototype = {
},
defaultTab == fontId);
if (Services.prefs.getBoolPref(TRACK_CHANGES_ENABLED)) {
// Inject a lazy loaded react tab by exposing a fake React object
// with a lazy defined Tab thanks to `panel` being a function
const changesId = "changesview";
const changesTitle = INSPECTOR_L10N.getStr("inspector.sidebar.changesViewTitle");
this.sidebar.queueTab(
changesId,
changesTitle,
{
props: {
id: changesId,
title: changesTitle
},
panel: () => {
if (!this.changesView) {
const ChangesView =
this.browserRequire("devtools/client/inspector/changes/ChangesView");
this.changesView = new ChangesView(this, this.panelWin);
}
return this.changesView.provider;
}
},
defaultTab == changesId);
}
this.sidebar.addAllQueuedTabs();
// Persist splitter state in preferences.
@ -1417,6 +1449,10 @@ Inspector.prototype = {
this.layoutview.destroy();
}
if (this.changesView) {
this.changesView.destroy();
}
if (this.fontinspector) {
this.fontinspector.destroy();
}
@ -1452,6 +1488,10 @@ Inspector.prototype = {
this.reflowTracker.destroy();
this.styleChangeTracker.destroy();
if (this.changesManager) {
this.changesManager.destroy();
}
this._is3PaneModeChromeEnabled = null;
this._is3PaneModeEnabled = null;
this._notificationBox = null;

Просмотреть файл

@ -5,6 +5,7 @@
DIRS += [
'animation',
'boxmodel',
'changes',
'components',
'computed',
'extensions',

Просмотреть файл

@ -9,6 +9,7 @@
exports.animations = require("devtools/client/inspector/animation/reducers/animations");
exports.boxModel = require("devtools/client/inspector/boxmodel/reducers/box-model");
exports.changes = require("devtools/client/inspector/changes/reducers/changes");
exports.extensionsSidebar = require("devtools/client/inspector/extensions/reducers/sidebar");
exports.flexbox = require("devtools/client/inspector/flexbox/reducers/flexbox");
exports.fontOptions = require("devtools/client/inspector/fonts/reducers/font-options");

Просмотреть файл

@ -1,351 +0,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/. */
"use strict";
const EventEmitter = require("devtools/shared/event-emitter");
const {createNode} = require("devtools/client/inspector/animation-old/utils");
const { LocalizationHelper } = require("devtools/shared/l10n");
const STRINGS_URI = "devtools/client/locales/inspector.properties";
const L10N = new LocalizationHelper(STRINGS_URI);
/**
* UI component responsible for displaying a preview of a dom node.
* @param {InspectorPanel} inspector Requires a reference to the inspector-panel
* to highlight and select the node, as well as refresh it when there are
* mutations.
* @param {Object} options Supported properties are:
* - compact {Boolean} Defaults to false.
* By default, nodes are previewed like <tag id="id" class="class">
* If true, nodes will be previewed like tag#id.class instead.
*/
function DomNodePreview(inspector, options = {}) {
this.inspector = inspector;
this.options = options;
this.onPreviewMouseOver = this.onPreviewMouseOver.bind(this);
this.onPreviewMouseOut = this.onPreviewMouseOut.bind(this);
this.onSelectElClick = this.onSelectElClick.bind(this);
this.onMarkupMutations = this.onMarkupMutations.bind(this);
this.onHighlightElClick = this.onHighlightElClick.bind(this);
this.onHighlighterLocked = this.onHighlighterLocked.bind(this);
EventEmitter.decorate(this);
}
exports.DomNodePreview = DomNodePreview;
DomNodePreview.prototype = {
init: function(containerEl) {
const document = containerEl.ownerDocument;
// Init the markup for displaying the target node.
this.el = createNode({
parent: containerEl,
attributes: {
"class": "animation-target"
}
});
// Icon to select the node in the inspector.
this.highlightNodeEl = createNode({
parent: this.el,
nodeType: "span",
attributes: {
"class": "node-highlighter",
"title": L10N.getStr("inspector.nodePreview.highlightNodeLabel")
}
});
// Wrapper used for mouseover/out event handling.
this.previewEl = createNode({
parent: this.el,
nodeType: "span",
attributes: {
"title": L10N.getStr("inspector.nodePreview.selectNodeLabel")
}
});
if (!this.options.compact) {
this.previewEl.appendChild(document.createTextNode("<"));
}
// Only used for ::before and ::after pseudo-elements.
this.pseudoEl = createNode({
parent: this.previewEl,
nodeType: "span",
attributes: {
"class": "pseudo-element theme-fg-color5"
}
});
// Tag name.
this.tagNameEl = createNode({
parent: this.previewEl,
nodeType: "span",
attributes: {
"class": "tag-name theme-fg-color3"
}
});
// Id attribute container.
this.idEl = createNode({
parent: this.previewEl,
nodeType: "span"
});
if (!this.options.compact) {
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "attribute-name theme-fg-color2"
},
textContent: "id"
});
this.idEl.appendChild(document.createTextNode("=\""));
} else {
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "theme-fg-color4"
},
textContent: "#"
});
}
createNode({
parent: this.idEl,
nodeType: "span",
attributes: {
"class": "attribute-value theme-fg-color4"
}
});
if (!this.options.compact) {
this.idEl.appendChild(document.createTextNode("\""));
}
// Class attribute container.
this.classEl = createNode({
parent: this.previewEl,
nodeType: "span"
});
if (!this.options.compact) {
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "attribute-name theme-fg-color2"
},
textContent: "class"
});
this.classEl.appendChild(document.createTextNode("=\""));
} else {
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "theme-fg-color4"
},
textContent: "."
});
}
createNode({
parent: this.classEl,
nodeType: "span",
attributes: {
"class": "attribute-value theme-fg-color4"
}
});
if (!this.options.compact) {
this.classEl.appendChild(document.createTextNode("\""));
this.previewEl.appendChild(document.createTextNode(">"));
}
this.startListeners();
},
startListeners: function() {
// Init events for highlighting and selecting the node.
this.previewEl.addEventListener("mouseover", this.onPreviewMouseOver);
this.previewEl.addEventListener("mouseout", this.onPreviewMouseOut);
this.previewEl.addEventListener("click", this.onSelectElClick);
this.highlightNodeEl.addEventListener("click", this.onHighlightElClick);
// Start to listen for markupmutation events.
this.inspector.on("markupmutation", this.onMarkupMutations);
// Listen to the target node highlighter.
HighlighterLock.on("highlighted", this.onHighlighterLocked);
},
stopListeners: function() {
HighlighterLock.off("highlighted", this.onHighlighterLocked);
this.inspector.off("markupmutation", this.onMarkupMutations);
this.previewEl.removeEventListener("mouseover", this.onPreviewMouseOver);
this.previewEl.removeEventListener("mouseout", this.onPreviewMouseOut);
this.previewEl.removeEventListener("click", this.onSelectElClick);
this.highlightNodeEl.removeEventListener("click", this.onHighlightElClick);
},
destroy: function() {
HighlighterLock.unhighlight().catch(console.error);
this.stopListeners();
this.el.remove();
this.el = this.tagNameEl = this.idEl = this.classEl = this.pseudoEl = null;
this.highlightNodeEl = this.previewEl = null;
this.nodeFront = this.inspector = null;
},
get highlighterUtils() {
if (this.inspector && this.inspector.toolbox) {
return this.inspector.toolbox.highlighterUtils;
}
return null;
},
onPreviewMouseOver: function() {
if (!this.nodeFront || !this.highlighterUtils) {
return;
}
this.highlighterUtils.highlightNodeFront(this.nodeFront)
.catch(console.error);
},
onPreviewMouseOut: function() {
if (!this.nodeFront || !this.highlighterUtils) {
return;
}
this.highlighterUtils.unhighlight()
.catch(console.error);
},
onSelectElClick: function() {
if (!this.nodeFront) {
return;
}
this.inspector.selection.setNodeFront(this.nodeFront, { reason: "dom-node-preview" });
},
onHighlightElClick: function(e) {
e.stopPropagation();
const classList = this.highlightNodeEl.classList;
const isHighlighted = classList.contains("selected");
if (isHighlighted) {
classList.remove("selected");
HighlighterLock.unhighlight().then(() => {
this.emit("target-highlighter-unlocked");
}, console.error);
} else {
classList.add("selected");
HighlighterLock.highlight(this).then(() => {
this.emit("target-highlighter-locked");
}, console.error);
}
},
onHighlighterLocked: function(domNodePreview) {
if (domNodePreview !== this) {
this.highlightNodeEl.classList.remove("selected");
}
},
onMarkupMutations: function(mutations) {
if (!this.nodeFront) {
return;
}
for (const {target} of mutations) {
if (target === this.nodeFront) {
// Re-render with the same nodeFront to update the output.
this.render(this.nodeFront);
break;
}
}
},
render: function(nodeFront) {
this.nodeFront = nodeFront;
const {displayName, attributes} = nodeFront;
if (nodeFront.isPseudoElement) {
this.pseudoEl.textContent = nodeFront.isBeforePseudoElement
? "::before"
: "::after";
this.pseudoEl.style.display = "inline";
this.tagNameEl.style.display = "none";
} else {
this.tagNameEl.textContent = displayName;
this.pseudoEl.style.display = "none";
this.tagNameEl.style.display = "inline";
}
const idIndex = attributes.findIndex(({name}) => name === "id");
if (idIndex > -1 && attributes[idIndex].value) {
this.idEl.querySelector(".attribute-value").textContent =
attributes[idIndex].value;
this.idEl.style.display = "inline";
} else {
this.idEl.style.display = "none";
}
const classIndex = attributes.findIndex(({name}) => name === "class");
if (classIndex > -1 && attributes[classIndex].value) {
let value = attributes[classIndex].value;
if (this.options.compact) {
value = value.split(" ").join(".");
}
this.classEl.querySelector(".attribute-value").textContent = value;
this.classEl.style.display = "inline";
} else {
this.classEl.style.display = "none";
}
}
};
/**
* HighlighterLock is a helper used to lock the highlighter on DOM nodes in the
* page.
* It instantiates a new highlighter that is then shared amongst all instances
* of DomNodePreview. This is useful because that means showing the highlighter
* on one node will unhighlight the previously highlighted one, but will not
* interfere with the default inspector highlighter.
*/
var HighlighterLock = {
highlighter: null,
isShown: false,
async highlight(animationTargetNode) {
if (!this.highlighter) {
const util = animationTargetNode.inspector.toolbox.highlighterUtils;
this.highlighter = await util.getHighlighterByType("BoxModelHighlighter");
}
await this.highlighter.show(animationTargetNode.nodeFront);
this.isShown = true;
this.emit("highlighted", animationTargetNode);
},
async unhighlight() {
if (!this.highlighter || !this.isShown) {
return;
}
await this.highlighter.hide();
this.isShown = false;
this.emit("unhighlighted");
}
};
EventEmitter.decorate(HighlighterLock);

Просмотреть файл

@ -5,7 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'dom-node-preview.js',
'highlighters-overlay.js',
'node-types.js',
'reflow-tracker.js',

Просмотреть файл

@ -243,6 +243,7 @@ devtools.jar:
skin/images/cubic-bezier-swatch.png (themes/images/cubic-bezier-swatch.png)
skin/images/cubic-bezier-swatch@2x.png (themes/images/cubic-bezier-swatch@2x.png)
skin/fonts.css (themes/fonts.css)
skin/changes.css (themes/changes.css)
skin/computed.css (themes/computed.css)
skin/layout.css (themes/layout.css)
skin/images/arrow-e.png (themes/images/arrow-e.png)

Просмотреть файл

@ -400,6 +400,11 @@ markupView.scrollInto.key=s
# that corresponds to the tool displaying the list of fonts used in the page.
inspector.sidebar.fontInspectorTitle=Fonts
# LOCALIZATION NOTE (inspector.sidebar.changesViewTitle):
# Title of the Changes sidebar tab shown in the Inspector panel. The Changes panel shows
# style changes made using DevTools.
inspector.sidebar.changesViewTitle=Changes
# LOCALIZATION NOTE (inspector.sidebar.ruleViewTitle):
# This is the title shown in a tab in the side panel of the Inspector panel
# that corresponds to the tool displaying the list of CSS rules used

Просмотреть файл

@ -74,7 +74,6 @@
<!ENTITY runtimePanel_usb "USB Devices">
<!ENTITY runtimePanel_wifi "Wi-Fi Devices">
<!ENTITY runtimePanel_other "Other">
<!ENTITY runtimePanel_noadbextension "Install ADB Extension">
<!ENTITY runtimePanel_nousbdevice "Cant see your device?">
<!ENTITY runtimePanel_refreshDevices_label "Refresh Devices">

Просмотреть файл

@ -55,7 +55,6 @@ addons_stable=stable
addons_unstable=unstable
addons_install_button=install
addons_uninstall_button=uninstall
addons_adb_label2=ADB Extension Add-on
addons_adb_warning=USB devices wont be detected without this add-on
addons_status_unknown=?
addons_status_installed=Installed
@ -68,6 +67,11 @@ runtimedetails_checkno=no
runtimedetails_checkyes=yes
runtimedetails_notUSBDevice=Not a USB device
# LOCALIZATION NOTE (runtimePanel_noadbextension): Displayed in the WebIDE right sidebar
# when the ADB Extension is not installed, %S will be replaced with the name of extension
# ("ADB Extension").
runtimePanel_noadbextension=Install %S
# Validation status
status_tooltip=Validation status: %1$S
status_valid=VALID

Просмотреть файл

@ -63,6 +63,8 @@ pref("devtools.inspector.shapesHighlighter.enabled", true);
pref("devtools.inspector.fonteditor.enabled", true);
// Enable the font highlight-on-hover feature
pref("devtools.inspector.fonthighlighter.enabled", true);
// Enable tracking of style changes and the Changes panel in the Inspector
pref("devtools.inspector.changes.enabled", false);
// Flexbox preferences
// Enable the Flexbox highlighter and inspector panel in Nightly

Просмотреть файл

@ -0,0 +1,97 @@
/* 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/. */
/* CSS Variables specific to the Changes panel that aren't defined by the themes */
:root {
--diff-add-background-color: #f1feec;
--diff-add-text-color: #54983f;
--diff-remove-background-color: #fbf2f5;
--diff-remove-text-color: #bf7173;
}
#sidebar-panel-changes {
margin: 0;
width: 100%;
height: 100%;
overflow: auto;
}
details {
font-family: var(--monospace-font-family);
font-size: 12px;
}
summary {
outline: none;
padding: 5px 0;
-moz-user-select: none;
cursor: pointer;
}
details.source[open] {
padding-bottom: 10px;
}
details.source > summary {
background: var(--theme-sidebar-background);
border-top: 1px solid var(--theme-splitter-color);
border-bottom: 1px solid var(--theme-splitter-color);
padding-left: 5px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
}
details.selector > summary {
padding-left: 10px;
}
details.selector summary::after{
content: "{…}";
display: inline-block;
padding-left: 5px;
}
details.selector[open]{
margin-bottom: 5px;
}
details.selector[open] summary::after{
content: "{";
}
details.selector[open]::after{
content: "}";
display: block;
padding-left: 10px;
}
.line {
padding: 3px 5px 3px 15px;
position: relative;
}
.diff-add::before,
.diff-remove::before{
position: absolute;
left: 5px;
}
.diff-add {
background-color: var(--diff-add-background-color);
}
.diff-add::before {
content: "+";
color: var(--diff-add-text-color);
}
.diff-remove {
background-color: var(--diff-remove-background-color);
}
.diff-remove::before{
content: "-";
color: var(--diff-remove-text-color);
}

Просмотреть файл

@ -69,7 +69,7 @@ function BuildItem(addon, type) {
switch (type) {
case "adb":
li.setAttribute("addon", type);
name.textContent = Strings.GetStringFromName("addons_adb_label2");
name.textContent = "ADB Extension";
break;
}

Просмотреть файл

@ -22,7 +22,7 @@
<button class="runtime-panel-item-refreshdevices refresh-icon" id="refresh-devices" title="&runtimePanel_refreshDevices_label;"></button>
</label>
<button class="panel-item" id="runtime-panel-nousbdevice">&runtimePanel_nousbdevice;</button>
<button class="panel-item" id="runtime-panel-noadbextension">&runtimePanel_noadbextension;</button>
<button class="panel-item" id="runtime-panel-noadbextension"></button>
<div id="runtime-panel-usb"></div>
<label class="panel-header" id="runtime-header-wifi">&runtimePanel_wifi;</label>
<div id="runtime-panel-wifi"></div>

Просмотреть файл

@ -4,10 +4,12 @@
"use strict";
const Services = require("Services");
const {AppManager} = require("devtools/client/webide/modules/app-manager");
const EventEmitter = require("devtools/shared/event-emitter");
const {RuntimeScanners, WiFiScanner} = require("devtools/client/webide/modules/runtimes");
const {Devices} = require("resource://devtools/shared/apps/Devices.jsm");
const Strings = Services.strings.createBundle("chrome://devtools/locale/webide.properties");
var RuntimeList;
@ -124,6 +126,8 @@ RuntimeList.prototype = {
const otherListNode = doc.querySelector("#runtime-panel-other");
const noADBExtensionNode = doc.querySelector("#runtime-panel-noadbextension");
const noUSBNode = doc.querySelector("#runtime-panel-nousbdevice");
noADBExtensionNode.textContent =
Strings.formatStringFromName("runtimePanel_noadbextension", ["ADB Extension"], 1);
if (Devices.adbExtensionInstalled) {
noADBExtensionNode.setAttribute("hidden", "true");

Просмотреть файл

@ -9,9 +9,6 @@ loader.lazyRequireGetter(this, "Services");
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm", true);
loader.lazyGetter(this, "screenManager", () => {
return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
});
loader.lazyGetter(this, "hostname", () => {
try {
// On some platforms (Linux according to try), this service does not exist and fails.
@ -185,57 +182,4 @@ function getProfileLocation() {
}
}
/**
* Function for fetching screen dimensions and returning
* an enum for Telemetry.
*/
function getScreenDimensions() {
const width = {};
const height = {};
screenManager.primaryScreen.GetRect({}, {}, width, height);
const dims = width.value + "x" + height.value;
if (width.value < 800 || height.value < 600) {
return 0;
}
if (dims === "800x600") {
return 1;
}
if (dims === "1024x768") {
return 2;
}
if (dims === "1280x800") {
return 3;
}
if (dims === "1280x1024") {
return 4;
}
if (dims === "1366x768") {
return 5;
}
if (dims === "1440x900") {
return 6;
}
if (dims === "1920x1080") {
return 7;
}
if (dims === "2560×1440") {
return 8;
}
if (dims === "2560×1600") {
return 9;
}
if (dims === "2880x1800") {
return 10;
}
if (width.value > 2880 || height.value > 1800) {
return 12;
}
// Other dimension such as a VM.
return 11;
}
exports.getSystemInfo = getSystemInfo;
exports.getScreenDimensions = getScreenDimensions;

Просмотреть файл

@ -65,7 +65,7 @@
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/EventStates.h"
#include "mozilla/FullscreenRequest.h"
#include "mozilla/FullscreenChange.h"
#include "mozilla/InternalMutationEvent.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/RestyleManager.h"

Просмотреть файл

@ -21,36 +21,18 @@
namespace mozilla {
struct FullscreenRequest : public LinkedListElement<FullscreenRequest>
class FullscreenChange : public LinkedListElement<FullscreenChange>
{
typedef dom::Promise Promise;
public:
FullscreenChange(const FullscreenChange&) = delete;
static UniquePtr<FullscreenRequest> Create(Element* aElement,
dom::CallerType aCallerType,
ErrorResult& aRv)
enum ChangeType
{
RefPtr<Promise> promise = Promise::Create(aElement->GetOwnerGlobal(), aRv);
return WrapUnique(new FullscreenRequest(aElement, promise.forget(),
aCallerType, true));
}
eEnter,
eExit,
};
static UniquePtr<FullscreenRequest> CreateForRemote(Element* aElement)
{
return WrapUnique(new FullscreenRequest(aElement, nullptr,
dom::CallerType::NonSystem,
false));
}
FullscreenRequest(const FullscreenRequest&) = delete;
~FullscreenRequest()
{
MOZ_COUNT_DTOR(FullscreenRequest);
MOZ_ASSERT_IF(mPromise,
mPromise->State() != Promise::PromiseState::Pending);
}
dom::Element* Element() const { return mElement; }
ChangeType Type() const { return mType; }
nsIDocument* Document() const { return mDocument; }
dom::Promise* GetPromise() const { return mPromise; }
@ -70,28 +52,78 @@ struct FullscreenRequest : public LinkedListElement<FullscreenRequest>
}
}
protected:
typedef dom::Promise Promise;
FullscreenChange(ChangeType aType, nsIDocument* aDocument,
already_AddRefed<Promise> aPromise)
: mType(aType)
, mDocument(aDocument)
, mPromise(aPromise)
{
MOZ_ASSERT(aDocument);
}
~FullscreenChange()
{
MOZ_ASSERT_IF(mPromise,
mPromise->State() != Promise::PromiseState::Pending);
}
private:
ChangeType mType;
nsCOMPtr<nsIDocument> mDocument;
RefPtr<Promise> mPromise;
};
class FullscreenRequest : public FullscreenChange
{
public:
static const ChangeType kType = eEnter;
static UniquePtr<FullscreenRequest> Create(Element* aElement,
dom::CallerType aCallerType,
ErrorResult& aRv)
{
RefPtr<Promise> promise = Promise::Create(aElement->GetOwnerGlobal(), aRv);
return WrapUnique(new FullscreenRequest(aElement, promise.forget(),
aCallerType, true));
}
static UniquePtr<FullscreenRequest> CreateForRemote(Element* aElement)
{
return WrapUnique(new FullscreenRequest(aElement, nullptr,
dom::CallerType::NonSystem,
false));
}
~FullscreenRequest()
{
MOZ_COUNT_DTOR(FullscreenRequest);
}
dom::Element* Element() const { return mElement; }
// Reject the fullscreen request with the given reason.
// It will dispatch the fullscreenerror event.
void Reject(const char* aReason) const
{
if (nsPresContext* presContext = mDocument->GetPresContext()) {
if (nsPresContext* presContext = Document()->GetPresContext()) {
auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
FullscreenEventType::Error, mDocument, mElement);
FullscreenEventType::Error, Document(), mElement);
presContext->RefreshDriver()->
ScheduleFullscreenEvent(std::move(pendingEvent));
}
MayRejectPromise();
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
NS_LITERAL_CSTRING("DOM"),
mDocument,
Document(),
nsContentUtils::eDOM_PROPERTIES,
aReason);
}
private:
RefPtr<dom::Element> mElement;
RefPtr<nsIDocument> mDocument;
RefPtr<dom::Promise> mPromise;
public:
// This value should be true if the fullscreen request is
@ -110,9 +142,8 @@ private:
already_AddRefed<dom::Promise> aPromise,
dom::CallerType aCallerType,
bool aShouldNotifyNewOrigin)
: mElement(aElement)
, mDocument(aElement->OwnerDoc())
, mPromise(aPromise)
: FullscreenChange(kType, aElement->OwnerDoc(), std::move(aPromise))
, mElement(aElement)
, mCallerType(aCallerType)
, mShouldNotifyNewOrigin(aShouldNotifyNewOrigin)
{
@ -120,6 +151,35 @@ private:
}
};
class FullscreenExit : public FullscreenChange
{
public:
static const ChangeType kType = eExit;
static UniquePtr<FullscreenExit> Create(nsIDocument* aDoc, ErrorResult& aRv)
{
RefPtr<Promise> promise = Promise::Create(aDoc->GetOwnerGlobal(), aRv);
return WrapUnique(new FullscreenExit(aDoc, promise.forget()));
}
static UniquePtr<FullscreenExit> CreateForRemote(nsIDocument* aDoc)
{
return WrapUnique(new FullscreenExit(aDoc, nullptr));
}
~FullscreenExit()
{
MOZ_COUNT_DTOR(FullscreenExit);
}
private:
FullscreenExit(nsIDocument* aDoc, already_AddRefed<Promise> aPromise)
: FullscreenChange(kType, aDoc, std::move(aPromise))
{
MOZ_COUNT_CTOR(FullscreenExit);
}
};
} // namespace mozilla
#endif // mozilla_FullscreenRequest_h

Просмотреть файл

@ -131,7 +131,7 @@ EXPORTS.mozilla += [
'CORSMode.h',
'FeedWriterEnabled.h',
'FlushType.h',
'FullscreenRequest.h',
'FullscreenChange.h',
'RangeBoundary.h',
'SelectionChangeEventDispatcher.h',
'TextInputProcessor.h',

Просмотреть файл

@ -58,7 +58,7 @@
#include "mozilla/BasicEvents.h"
#include "mozilla/EventListenerManager.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/FullscreenRequest.h"
#include "mozilla/FullscreenChange.h"
#include "mozilla/dom/Attr.h"
#include "mozilla/dom/BindingDeclarations.h"
@ -10618,8 +10618,109 @@ FullscreenRoots::IsEmpty()
return !sInstance;
}
// Any fullscreen change waiting for the widget to finish transition
// is queued here. This is declared static instead of a member of
// nsDocument because in the majority of time, there would be at most
// one document requesting or exiting fullscreen. We shouldn't waste
// the space to hold for it in every document.
class PendingFullscreenChangeList
{
public:
PendingFullscreenChangeList() = delete;
template<typename T>
static void Add(UniquePtr<T> aChange)
{
sList.insertBack(aChange.release());
}
static const FullscreenChange* GetLast()
{
return sList.getLast();
}
enum IteratorOption
{
// When we are committing fullscreen changes or preparing for
// that, we generally want to iterate all requests in the same
// window with eDocumentsWithSameRoot option.
eDocumentsWithSameRoot,
// If we are removing a document from the tree, we would only
// want to remove the requests from the given document and its
// descendants. For that case, use eInclusiveDescendants.
eInclusiveDescendants
};
template<typename T>
class Iterator
{
public:
explicit Iterator(nsIDocument* aDoc, IteratorOption aOption)
: mCurrent(PendingFullscreenChangeList::sList.getFirst())
, mRootShellForIteration(aDoc->GetDocShell())
{
if (mCurrent) {
if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) {
mRootShellForIteration->
GetRootTreeItem(getter_AddRefs(mRootShellForIteration));
}
SkipToNextMatch();
}
}
UniquePtr<T> TakeAndNext()
{
auto thisChange = TakeAndNextInternal();
SkipToNextMatch();
return thisChange;
}
bool AtEnd() const { return mCurrent == nullptr; }
private:
UniquePtr<T> TakeAndNextInternal()
{
FullscreenChange* thisChange = mCurrent;
MOZ_ASSERT(thisChange->Type() == T::kType);
mCurrent = mCurrent->removeAndGetNext();
return WrapUnique(static_cast<T*>(thisChange));
}
void SkipToNextMatch()
{
while (mCurrent) {
if (mCurrent->Type() == T::kType) {
nsCOMPtr<nsIDocShellTreeItem>
docShell = mCurrent->Document()->GetDocShell();
if (!docShell) {
// Always automatically drop fullscreen changes which are
// from a document detached from the doc shell.
UniquePtr<T> change = TakeAndNextInternal();
change->MayRejectPromise();
continue;
}
while (docShell && docShell != mRootShellForIteration) {
docShell->GetParent(getter_AddRefs(docShell));
}
if (docShell) {
break;
}
}
// The current one either don't have matched type, or isn't
// inside the given subtree, so skip this item.
mCurrent = mCurrent->getNext();
}
}
FullscreenChange* mCurrent;
nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration;
};
private:
static LinkedList<FullscreenChange> sList;
};
/* static */ LinkedList<FullscreenChange> PendingFullscreenChangeList::sList;
} // end namespace mozilla.
using mozilla::FullscreenRoots;
nsIDocument*
nsIDocument::GetFullscreenRoot()
@ -10634,10 +10735,13 @@ nsIDocument::SetFullscreenRoot(nsIDocument* aRoot)
mFullscreenRoot = do_GetWeakReference(aRoot);
}
void
nsIDocument::ExitFullscreen()
already_AddRefed<Promise>
nsIDocument::ExitFullscreen(ErrorResult& aRv)
{
RestorePreviousFullscreenState();
UniquePtr<FullscreenExit> exit = FullscreenExit::Create(this, aRv);
RefPtr<Promise> promise = exit->GetPromise();
RestorePreviousFullscreenState(std::move(exit));
return promise.forget();
}
static void
@ -10809,6 +10913,14 @@ nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
// Unlock the pointer
UnlockPointer();
// Resolve all promises which waiting for exit fullscreen.
PendingFullscreenChangeList::Iterator<FullscreenExit> iter(
aMaybeNotARootDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
while (!iter.AtEnd()) {
UniquePtr<FullscreenExit> exit = iter.TakeAndNext();
exit->MayResolvePromise();
}
nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
if (!root || !root->FullscreenStackTop()) {
// If a document was detached before exiting from fullscreen, it is
@ -10852,12 +10964,13 @@ DispatchFullscreenNewOriginEvent(nsIDocument* aDoc)
}
void
nsIDocument::RestorePreviousFullscreenState()
nsIDocument::RestorePreviousFullscreenState(UniquePtr<FullscreenExit> aExit)
{
NS_ASSERTION(!FullscreenStackTop() || !FullscreenRoots::IsEmpty(),
"Should have at least 1 fullscreen root when fullscreen!");
if (!FullscreenStackTop() || !GetWindow() || FullscreenRoots::IsEmpty()) {
aExit->MayRejectPromise();
return;
}
@ -10898,6 +11011,7 @@ nsIDocument::RestorePreviousFullscreenState()
lastDoc->mFullscreenStack.Length() == 1) {
// If we are fully exiting fullscreen, don't touch anything here,
// just wait for the window to get out from fullscreen first.
PendingFullscreenChangeList::Add(std::move(aExit));
AskWindowToExitFullscreen(this);
return;
}
@ -10925,6 +11039,7 @@ nsIDocument::RestorePreviousFullscreenState()
for (Element* e : Reversed(exitElements)) {
DispatchFullscreenChange(e->OwnerDoc(), e);
}
aExit->MayResolvePromise();
MOZ_ASSERT(newFullscreenDoc, "If we were going to exit from fullscreen on "
"all documents in this doctree, we should've asked the window to "
@ -11133,7 +11248,8 @@ nsresult nsIDocument::RemoteFrameFullscreenChanged(Element* aFrameElement)
nsresult nsIDocument::RemoteFrameFullscreenReverted()
{
RestorePreviousFullscreenState();
UniquePtr<FullscreenExit> exit = FullscreenExit::CreateForRemote(this);
RestorePreviousFullscreenState(std::move(exit));
return NS_OK;
}
@ -11244,104 +11360,6 @@ nsIDocument::FullscreenElementReadyCheck(const FullscreenRequest& aRequest)
return true;
}
// Any fullscreen request waiting for the widget to finish being full-
// screen is queued here. This is declared static instead of a member
// of nsDocument because in the majority of time, there would be at most
// one document requesting fullscreen. We shouldn't waste the space to
// hold for it in every document.
class PendingFullscreenRequestList
{
public:
static void Add(UniquePtr<FullscreenRequest> aRequest)
{
sList.insertBack(aRequest.release());
}
static const FullscreenRequest* GetLast()
{
return sList.getLast();
}
enum IteratorOption
{
// When we are committing fullscreen changes or preparing for
// that, we generally want to iterate all requests in the same
// window with eDocumentsWithSameRoot option.
eDocumentsWithSameRoot,
// If we are removing a document from the tree, we would only
// want to remove the requests from the given document and its
// descendants. For that case, use eInclusiveDescendants.
eInclusiveDescendants
};
class Iterator
{
public:
explicit Iterator(nsIDocument* aDoc, IteratorOption aOption)
: mCurrent(PendingFullscreenRequestList::sList.getFirst())
, mRootShellForIteration(aDoc->GetDocShell())
{
if (mCurrent) {
if (mRootShellForIteration && aOption == eDocumentsWithSameRoot) {
mRootShellForIteration->
GetRootTreeItem(getter_AddRefs(mRootShellForIteration));
}
SkipToNextMatch();
}
}
UniquePtr<FullscreenRequest> TakeAndNext()
{
auto thisRequest = TakeAndNextInternal();
SkipToNextMatch();
return thisRequest;
}
bool AtEnd() const { return mCurrent == nullptr; }
private:
UniquePtr<FullscreenRequest> TakeAndNextInternal()
{
UniquePtr<FullscreenRequest> thisRequest(mCurrent);
mCurrent = mCurrent->removeAndGetNext();
return thisRequest;
}
void SkipToNextMatch()
{
while (mCurrent) {
nsCOMPtr<nsIDocShellTreeItem>
docShell = mCurrent->Document()->GetDocShell();
if (!docShell) {
// Always automatically drop fullscreen requests which is from
// a document detached from the doc shell.
UniquePtr<FullscreenRequest> request = TakeAndNextInternal();
request->MayRejectPromise();
} else {
while (docShell && docShell != mRootShellForIteration) {
docShell->GetParent(getter_AddRefs(docShell));
}
if (!docShell) {
// We've gone over the root, but haven't find the target
// ancestor, so skip this item.
mCurrent = mCurrent->getNext();
} else {
break;
}
}
}
}
FullscreenRequest* mCurrent;
nsCOMPtr<nsIDocShellTreeItem> mRootShellForIteration;
};
private:
PendingFullscreenRequestList() = delete;
static LinkedList<FullscreenRequest> sList;
};
/* static */ LinkedList<FullscreenRequest> PendingFullscreenRequestList::sList;
static nsCOMPtr<nsPIDOMWindowOuter>
GetRootWindow(nsIDocument* aDoc)
{
@ -11373,8 +11391,8 @@ ShouldApplyFullscreenDirectly(nsIDocument* aDoc,
// pending fullscreen request relates to this document. We have to
// push the request to the pending queue so requests are handled
// in the correct order.
PendingFullscreenRequestList::Iterator
iter(aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
if (!iter.AtEnd()) {
return false;
}
@ -11417,7 +11435,7 @@ nsIDocument::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
return;
}
PendingFullscreenRequestList::Add(std::move(aRequest));
PendingFullscreenChangeList::Add(std::move(aRequest));
if (XRE_GetProcessType() == GeckoProcessType_Content) {
// If we are not the top level process, dispatch an event to make
// our parent process go fullscreen first.
@ -11434,8 +11452,8 @@ nsIDocument::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
{
bool handled = false;
PendingFullscreenRequestList::Iterator iter(
aDoc, PendingFullscreenRequestList::eDocumentsWithSameRoot);
PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
aDoc, PendingFullscreenChangeList::eDocumentsWithSameRoot);
while (!iter.AtEnd()) {
UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
nsIDocument* doc = request->Document();
@ -11449,8 +11467,8 @@ nsIDocument::HandlePendingFullscreenRequests(nsIDocument* aDoc)
static void
ClearPendingFullscreenRequests(nsIDocument* aDoc)
{
PendingFullscreenRequestList::Iterator iter(
aDoc, PendingFullscreenRequestList::eInclusiveDescendants);
PendingFullscreenChangeList::Iterator<FullscreenRequest> iter(
aDoc, PendingFullscreenChangeList::eInclusiveDescendants);
while (!iter.AtEnd()) {
UniquePtr<FullscreenRequest> request = iter.TakeAndNext();
request->MayRejectPromise();

Просмотреть файл

@ -126,7 +126,8 @@ class Encoding;
class ErrorResult;
class EventStates;
class EventListenerManager;
struct FullscreenRequest;
class FullscreenExit;
class FullscreenRequest;
class PendingAnimationTracker;
class ServoStyleSet;
template<typename> class OwningNonNull;
@ -1811,7 +1812,8 @@ public:
* is no former fullscreen element, this exits fullscreen, moving the
* top-level browser window out of fullscreen mode.
*/
void RestorePreviousFullscreenState();
void RestorePreviousFullscreenState(
mozilla::UniquePtr<mozilla::FullscreenExit>);
/**
* Returns true if this document is a fullscreen leaf document, i.e. it
@ -3293,7 +3295,7 @@ public:
{
return !!GetFullscreenElement();
}
void ExitFullscreen();
already_AddRefed<mozilla::dom::Promise> ExitFullscreen(ErrorResult&);
void ExitPointerLock()
{
UnlockPointer(this);

Просмотреть файл

@ -44,6 +44,7 @@
class nsGenericHTMLElement;
class nsIJSID;
class nsIDocument;
namespace mozilla {
@ -53,6 +54,7 @@ namespace dom {
class CustomElementReactionsStack;
class MessageManagerGlobal;
template<typename KeyType, typename ValueType> class Record;
class Location;
nsresult
UnwrapArgImpl(JSContext* cx, JS::Handle<JSObject*> src, const nsIID& iid,
@ -1060,8 +1062,30 @@ struct CheckWrapperCacheTracing<T, true>
void
AssertReflectorHasGivenProto(JSContext* aCx, JSObject* aReflector,
JS::Handle<JSObject*> aGivenProto);
#endif // DEBUG
template <class T>
MOZ_ALWAYS_INLINE void
CrashIfDocumentOrLocationWrapFailed()
{
// Do nothing.
}
template<>
MOZ_ALWAYS_INLINE void
CrashIfDocumentOrLocationWrapFailed<nsIDocument>()
{
MOZ_CRASH("Looks like bug 1488480/1405521, with WrapObject() on nsIDocument throwing");
}
template<>
MOZ_ALWAYS_INLINE void
CrashIfDocumentOrLocationWrapFailed<Location>()
{
MOZ_CRASH("Looks like bug 1488480/1405521, with WrapObject() on Location throwing");
}
template <class T, GetOrCreateReflectorWrapBehavior wrapBehavior>
MOZ_ALWAYS_INLINE bool
DoGetOrCreateDOMReflector(JSContext* cx, T* value,
@ -1084,6 +1108,7 @@ DoGetOrCreateDOMReflector(JSContext* cx, T* value,
// At this point, obj is null, so just return false.
// Callers seem to be testing JS_IsExceptionPending(cx) to
// figure out whether WrapObject() threw.
CrashIfDocumentOrLocationWrapFailed<T>();
return false;
}
@ -1113,19 +1138,35 @@ DoGetOrCreateDOMReflector(JSContext* cx, T* value,
rval.set(JS::ObjectValue(*obj));
if (js::GetObjectCompartment(obj) == js::GetContextCompartment(cx)) {
return TypeNeedsOuterization<T>::value ? TryToOuterize(rval) : true;
if (!TypeNeedsOuterization<T>::value) {
return true;
}
if (TryToOuterize(rval)) {
return true;
}
return false;
}
if (wrapBehavior == eDontWrapIntoContextCompartment) {
if (TypeNeedsOuterization<T>::value) {
JSAutoRealm ar(cx, obj);
return TryToOuterize(rval);
if (TryToOuterize(rval)) {
return true;
}
return false;
}
return true;
}
return JS_WrapValue(cx, rval);
if (JS_WrapValue(cx, rval)) {
return true;
}
MOZ_CRASH("Looks like bug 1488480/1405521, with JS_WrapValue failing");
return false;
}
} // namespace binding_detail

Просмотреть файл

@ -4015,6 +4015,14 @@ class CGClearCachedValueMethod(CGAbstractMethod):
saveMember = (
"JS::Rooted<JS::Value> oldValue(aCx, js::GetReservedSlot(obj, %s));\n" %
slotIndex)
if self.descriptor.name == "Document" or self.descriptor.name == "Location":
maybeCrash = dedent(
"""
MOZ_CRASH("Looks like bug 1488480/1405521, with the getter failing");
""")
else:
maybeCrash = ""
regetMember = fill(
"""
JS::Rooted<JS::Value> temp(aCx);
@ -4022,12 +4030,14 @@ class CGClearCachedValueMethod(CGAbstractMethod):
JSAutoRealm ar(aCx, obj);
if (!get_${name}(aCx, obj, aObject, args)) {
js::SetReservedSlot(obj, ${slotIndex}, oldValue);
$*{maybeCrash}
return false;
}
return true;
""",
name=self.member.identifier.name,
slotIndex=slotIndex)
slotIndex=slotIndex,
maybeCrash=maybeCrash)
else:
declObj = "JSObject* obj;\n"
noopRetval = ""
@ -6681,6 +6691,11 @@ def getWrapTemplateForType(type, descriptorProvider, result, successCode,
# threw an exception.
failed = ("MOZ_ASSERT(JS_IsExceptionPending(cx));\n" +
exceptionCode)
if descriptor.name == "Document" or descriptor.name == "Location":
failed = (
'MOZ_CRASH("Looks like bug 1488480/1405521, with getting the reflector failing");\n' +
failed)
else:
if descriptor.notflattened:
getIID = "&NS_GET_IID(%s), " % descriptor.nativeType
@ -7836,6 +7851,14 @@ class CGPerSignatureCall(CGThing):
"args.rval().isObject()")
postConversionSteps += freezeValue.define()
if self.descriptor.name == "Window":
maybeCrash = dedent(
"""
MOZ_CRASH("Looks like bug 1488480/1405521, with the other MaybeWrap failing");
""")
else:
maybeCrash = ""
# slotStorageSteps are steps that run once we have entered the
# slotStorage compartment.
slotStorageSteps= fill(
@ -7843,11 +7866,13 @@ class CGPerSignatureCall(CGThing):
// Make a copy so that we don't do unnecessary wrapping on args.rval().
JS::Rooted<JS::Value> storedVal(cx, args.rval());
if (!${maybeWrap}(cx, &storedVal)) {
$*{maybeCrash}
return false;
}
js::SetReservedSlot(slotStorage, slotIndex, storedVal);
""",
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
maybeCrash=maybeCrash)
checkForXray = mayUseXrayExpandoSlots(self.descriptor, self.idlNode)
@ -7885,6 +7910,14 @@ class CGPerSignatureCall(CGThing):
else:
conversionScope = "slotStorage"
if self.descriptor.name == "Window":
maybeCrash = dedent(
"""
MOZ_CRASH("Looks like bug 1488480/1405521, with the third MaybeWrap failing");
""")
else:
maybeCrash = ""
wrapCode = fill(
"""
{
@ -7900,13 +7933,18 @@ class CGPerSignatureCall(CGThing):
$*{slotStorageSteps}
}
// And now make sure args.rval() is in the caller realm.
return ${maybeWrap}(cx, args.rval());
if (${maybeWrap}(cx, args.rval())) {
return true;
}
$*{maybeCrash}
return false;
""",
conversionScope=conversionScope,
wrapCode=wrapCode,
postConversionSteps=postConversionSteps,
slotStorageSteps=slotStorageSteps,
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type))
maybeWrap=getMaybeWrapValueFuncForType(self.idlNode.type),
maybeCrash=maybeCrash)
return wrapCode
def define(self):
@ -8938,6 +8976,14 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
""",
slotIndex=memberReservedSlot(self.attr, self.descriptor))
if self.descriptor.name == "Window":
maybeCrash = dedent(
"""
MOZ_CRASH("Looks like bug 1488480/1405521, with cached value wrapping failing");
""")
else:
maybeCrash = ""
prefix += fill(
"""
MOZ_ASSERT(JSCLASS_RESERVED_SLOTS(js::GetObjectClass(slotStorage)) > slotIndex);
@ -8948,12 +8994,17 @@ class CGSpecializedGetter(CGAbstractStaticMethod):
args.rval().set(cachedVal);
// The cached value is in the compartment of slotStorage,
// so wrap into the caller compartment as needed.
return ${maybeWrap}(cx, args.rval());
if (${maybeWrap}(cx, args.rval())) {
return true;
}
$*{maybeCrash}
return false;
}
}
""",
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type))
maybeWrap=getMaybeWrapValueFuncForType(self.attr.type),
maybeCrash=maybeCrash)
else:
prefix = ""

Просмотреть файл

@ -194,7 +194,7 @@ nsGenericHTMLElement::GetAccessKeyLabel(nsString& aLabel)
}
static bool
IS_TABLE_CELL(LayoutFrameType frameType)
IsTableCell(LayoutFrameType frameType)
{
return LayoutFrameType::TableCell == frameType ||
LayoutFrameType::BCTableCell == frameType;
@ -205,7 +205,7 @@ IsOffsetParent(nsIFrame* aFrame)
{
LayoutFrameType frameType = aFrame->Type();
if (IS_TABLE_CELL(frameType) || frameType == LayoutFrameType::Table) {
if (IsTableCell(frameType) || frameType == LayoutFrameType::Table) {
// Per the IDL for Element, only td, th, and table are acceptable offsetParents
// apart from body or positioned elements; we need to check the content type as
// well as the frame type so we ignore anonymous tables created by an element

Просмотреть файл

@ -109,7 +109,7 @@ function enter2(event) {
// the full-screen element to the previous full-screen element. This causes
// a fullscreenchange event.
addFullscreenChangeContinuation("exit", exit2);
document.exitFullscreen();
promise = document.exitFullscreen();
}
function exit2(event) {
@ -117,6 +117,7 @@ function exit2(event) {
"Full-screen element should have rolled back.");
is(iframe.contentDocument.fullscreenElement, null,
"Full-screen element in subframe should be null");
assertPromiseResolved(promise, "in exit2");
addFullscreenChangeContinuation("enter", enter3);
promise = FULLSCREEN_ELEMENT.requestFullscreen();

Просмотреть файл

@ -3381,34 +3381,17 @@ ContentParent::KillHard(const char* aReason)
mCrashReporter->AddAnnotation(CrashReporter::Annotation::ipc_channel_error,
reason);
Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
RefPtr<ContentParent> self = this;
std::function<void(bool)> callback = [self](bool aResult) {
self->OnGenerateMinidumpComplete(aResult);
};
// Generate the report and insert into the queue for submittal.
mCrashReporter->GenerateMinidumpAndPair(Process(),
nullptr,
NS_LITERAL_CSTRING("browser"),
std::move(callback),
true);
return;
if (mCrashReporter->GenerateMinidumpAndPair(this,
nullptr,
NS_LITERAL_CSTRING("browser")))
{
mCreatedPairedMinidumps = mCrashReporter->FinalizeCrashReport();
}
Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
}
OnGenerateMinidumpComplete(false);
}
void
ContentParent::OnGenerateMinidumpComplete(bool aDumpResult)
{
if (mCrashReporter && aDumpResult) {
// CrashReporterHost::GenerateMinidumpAndPair() is successful.
mCreatedPairedMinidumps = mCrashReporter->FinalizeCrashReport();
}
Unused << aDumpResult; // Don't care about result if no minidump was requested.
ProcessHandle otherProcessHandle;
if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
NS_ERROR("Failed to open child process when attempting kill.");

Просмотреть файл

@ -862,8 +862,6 @@ private:
// Start the force-kill timer on shutdown.
void StartForceKillTimer();
void OnGenerateMinidumpComplete(bool aDumpResult);
// Ensure that the permissions for the giben Permission key are set in the
// content process.
//

Просмотреть файл

@ -264,8 +264,6 @@ private:
void SendHangNotification(const HangData& aHangData,
const nsString& aBrowserDumpId,
bool aTakeMinidump);
void OnTakeFullMinidumpComplete(const HangData& aHangData,
const nsString& aDumpId);
void ClearHangNotification();
@ -738,48 +736,25 @@ HangMonitorParent::SendHangNotification(const HangData& aHangData,
// chrome process, main thread
MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsString dumpId;
if ((aHangData.type() == HangData::TPluginHangData) && aTakeMinidump) {
// We've been handed a partial minidump; complete it with plugin and
// content process dumps.
const PluginHangData& phd = aHangData.get_PluginHangData();
WeakPtr<HangMonitorParent> self = this;
std::function<void(nsString)> callback =
[self, aHangData](nsString aResult) {
MOZ_RELEASE_ASSERT(NS_IsMainThread());
if (!self) {
// Don't report hang since the process has already shut down.
return;
}
self->UpdateMinidump(aHangData.get_PluginHangData().pluginId(),
aResult);
self->OnTakeFullMinidumpComplete(aHangData, aResult);
};
plugins::TakeFullMinidump(phd.pluginId(),
phd.contentProcessId(),
aBrowserDumpId,
std::move(callback),
true);
plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
aBrowserDumpId, dumpId);
UpdateMinidump(phd.pluginId(), dumpId);
} else {
// We already have a full minidump; go ahead and use it.
OnTakeFullMinidumpComplete(aHangData, aBrowserDumpId);
dumpId = aBrowserDumpId;
}
}
void
HangMonitorParent::OnTakeFullMinidumpComplete(const HangData& aHangData,
const nsString& aDumpId)
{
mProcess->SetHangData(aHangData, aDumpId);
mProcess->SetHangData(aHangData, dumpId);
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
observerService->NotifyObservers(mProcess,
"process-hang-report",
nullptr);
observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
}
void
@ -1117,20 +1092,12 @@ HangMonitoredProcess::TerminatePlugin()
// Use the multi-process crash report generated earlier.
uint32_t id = mHangData.get_PluginHangData().pluginId();
base::ProcessId contentPid = mHangData.get_PluginHangData().contentProcessId();
plugins::TerminatePlugin(id, contentPid, NS_LITERAL_CSTRING("HangMonitor"),
mDumpId);
RefPtr<HangMonitoredProcess> self{this};
std::function<void(bool)> callback =
[self, id](bool aResult) {
if (self->mActor) {
self->mActor->CleanupPluginHang(id, false);
}
};
plugins::TerminatePlugin(id,
contentPid,
NS_LITERAL_CSTRING("HangMonitor"),
mDumpId,
std::move(callback));
if (mActor) {
mActor->CleanupPluginHang(id, false);
}
return NS_OK;
}

Просмотреть файл

@ -7,8 +7,6 @@
#ifndef mozilla_plugins_PluginBridge_h
#define mozilla_plugins_PluginBridge_h
#include <functional>
#include "base/process.h"
namespace mozilla {
@ -41,15 +39,14 @@ void
TakeFullMinidump(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync);
nsString& aDumpId);
void
TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback);
const nsAString& aDumpId);
} // namespace plugins
} // namespace mozilla

Просмотреть файл

@ -355,13 +355,10 @@ PluginHangUIParent::RecvUserResponse(const unsigned int& aResponse)
int responseCode;
if (aResponse & HANGUI_USER_RESPONSE_STOP) {
// User clicked Stop
std::function<void(bool)> callback = [](bool aResult) { };
mModule->TerminateChildProcess(mMainThreadMessageLoop,
mozilla::ipc::kInvalidProcessId,
NS_LITERAL_CSTRING("ModalHangUI"),
EmptyString(),
mModule->DummyCallback<bool>(),
/* aAsync = */ false);
EmptyString());
responseCode = 1;
} else if(aResponse & HANGUI_USER_RESPONSE_CONTINUE) {
mModule->OnHangUIContinue();

Просмотреть файл

@ -357,19 +357,13 @@ void
mozilla::plugins::TakeFullMinidump(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync)
nsString& aDumpId)
{
PluginModuleChromeParent* chromeParent =
PluginModuleChromeParentForId(aPluginId);
if (chromeParent) {
chromeParent->TakeFullMinidump(aContentProcessId,
aBrowserDumpId,
std::move(aCallback),
aAsync);
} else {
aCallback(EmptyString());
chromeParent->TakeFullMinidump(aContentProcessId, aBrowserDumpId, aDumpId);
}
}
@ -377,8 +371,7 @@ void
mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
base::ProcessId aContentProcessId,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback)
const nsAString& aDumpId)
{
PluginModuleChromeParent* chromeParent =
PluginModuleChromeParentForId(aPluginId);
@ -387,11 +380,7 @@ mozilla::plugins::TerminatePlugin(uint32_t aPluginId,
chromeParent->TerminateChildProcess(MessageLoop::current(),
aContentProcessId,
aMonitorDescription,
aDumpId,
std::move(aCallback),
true); // Always runs asynchronously.
} else {
aCallback(true);
aDumpId);
}
}
@ -587,7 +576,7 @@ PluginModuleChromeParent::InitCrashReporter()
}
{
mozilla::RecursiveMutexAutoLock lock(mCrashReporterMutex);
mozilla::MutexAutoLock lock(mCrashReporterMutex);
mCrashReporter = MakeUnique<ipc::CrashReporterHost>(
GeckoProcessType_Plugin,
shmem,
@ -727,7 +716,7 @@ void
PluginModuleChromeParent::WriteExtraDataForMinidump()
{
// mCrashReporterMutex is already held by the caller
mCrashReporterMutex.AssertCurrentThreadIn();
mCrashReporterMutex.AssertCurrentThreadOwns();
typedef nsDependentCString cstring;
@ -1066,14 +1055,10 @@ PluginModuleChromeParent::ShouldContinueFromReplyTimeout()
FinishHangUI();
#endif // XP_WIN
// Terminate the child process synchronously because this function can be
// called in sync IPC.
TerminateChildProcess(MessageLoop::current(),
mozilla::ipc::kInvalidProcessId,
NS_LITERAL_CSTRING("ModalHangUI"),
EmptyString(),
DummyCallback<bool>(),
/* aAsync = */ false);
EmptyString());
GetIPCChannel()->CloseWithTimeout();
return false;
}
@ -1098,141 +1083,55 @@ PluginModuleContentParent::OnExitedSyncSend()
void
PluginModuleChromeParent::TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync)
nsString& aDumpId)
{
mozilla::RecursiveMutexAutoLock lock(mCrashReporterMutex);
mozilla::MutexAutoLock lock(mCrashReporterMutex);
if (!mCrashReporter || !mTakeFullMinidumpCallback.IsEmpty()) {
aCallback(EmptyString());
if (!mCrashReporter) {
return;
}
mTakeFullMinidumpCallback.Init(std::move(aCallback), aAsync);
nsString browserDumpId{aBrowserDumpId};
bool reportsReady = false;
// Check to see if we already have a browser dump id - with e10s plugin
// hangs we take this earlier (see ProcessHangMonitor) from a background
// thread. We do this before we message the main thread about the hang
// since the posted message will trash our browser stack state.
nsCOMPtr<nsIFile> browserDumpFile;
if (CrashReporter::GetMinidumpForID(aBrowserDumpId,
getter_AddRefs(mBrowserDumpFile))) {
// Hold a ref to mPlugin to keep *this* alive until the callback runs.
RetainPluginRef();
std::function<void(bool)> callback =
[this, aContentPid, browserDumpId, aAsync](bool aResult) {
if (aAsync) {
this->mCrashReporterMutex.Lock();
}
this->TakeBrowserAndPluginMinidumps(aResult,
aContentPid,
browserDumpId,
aAsync);
if (aAsync) {
this->mCrashReporterMutex.Unlock();
}
this->ReleasePluginRef();
};
getter_AddRefs(browserDumpFile))) {
// We have a single browser report, generate a new plugin process parent
// report and pair it up with the browser report handed in.
mCrashReporter->GenerateMinidumpAndPair(Process(), mBrowserDumpFile,
NS_LITERAL_CSTRING("browser"),
std::move(callback), aAsync);
} else {
TakeBrowserAndPluginMinidumps(false, aContentPid, browserDumpId, aAsync);
}
}
reportsReady = mCrashReporter->GenerateMinidumpAndPair(
this,
browserDumpFile,
NS_LITERAL_CSTRING("browser"));
void
PluginModuleChromeParent::RetainPluginRef()
{
if (!mPlugin) {
return;
if (!reportsReady) {
browserDumpFile = nullptr;
CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
}
}
if (NS_IsMainThread()) {
mPlugin->AddRef();
} else {
// XXX We can't sync-dispatch to the main thread because doing that
// deadlocks when we are called from
// PluginHangUIParent::RecvUserResponse().
Unused << NS_DispatchToMainThread(
NewNonOwningRunnableMethod("nsNPAPIPlugin::AddRef",
mPlugin, &nsNPAPIPlugin::AddRef));
}
}
void
PluginModuleChromeParent::ReleasePluginRef()
{
if (!mPlugin) {
return;
}
if (NS_IsMainThread()) {
mPlugin->Release();
} else {
// Async release the reference to mPlugin.
Unused << NS_DispatchToMainThread(
NewNonOwningRunnableMethod("nsNPAPIPlugin::Release",
mPlugin, &nsNPAPIPlugin::Release));
}
}
void
PluginModuleChromeParent::TakeBrowserAndPluginMinidumps(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
bool aAsync)
{
mCrashReporterMutex.AssertCurrentThreadIn();
// Generate crash report including plugin and browser process minidumps.
// The plugin process is the parent report with additional dumps including
// the browser process, content process when running under e10s, and
// various flash subprocesses if we're the flash module.
if (!aReportsReady) {
mBrowserDumpFile = nullptr;
CrashReporter::DeleteMinidumpFilesForID(aBrowserDumpId);
nsString browserDumpId{aBrowserDumpId};
RetainPluginRef();
std::function<void(bool)> callback =
[this, aContentPid, browserDumpId](bool aResult) {
this->OnTakeFullMinidumpComplete(aResult,
aContentPid,
browserDumpId);
this->ReleasePluginRef();
};
mCrashReporter->GenerateMinidumpAndPair(Process(),
nullptr, // Pair with a dump of this process and thread.
NS_LITERAL_CSTRING("browser"),
std::move(callback),
aAsync);
} else {
OnTakeFullMinidumpComplete(aReportsReady, aContentPid, aBrowserDumpId);
if (!reportsReady) {
reportsReady = mCrashReporter->GenerateMinidumpAndPair(
this,
nullptr, // Pair with a dump of this process and thread.
NS_LITERAL_CSTRING("browser"));
}
}
void
PluginModuleChromeParent::OnTakeFullMinidumpComplete(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId)
{
mCrashReporterMutex.AssertCurrentThreadIn();
if (aReportsReady) {
nsString dumpId = mCrashReporter->MinidumpID();
if (reportsReady) {
aDumpId = mCrashReporter->MinidumpID();
PLUGIN_LOG_DEBUG(
("generated paired browser/plugin minidumps: %s)",
NS_ConvertUTF16toUTF8(dumpId).get()));
("generated paired browser/plugin minidumps: %s)",
NS_ConvertUTF16toUTF8(aDumpId).get()));
nsAutoCString additionalDumps("browser");
nsCOMPtr<nsIFile> pluginDumpFile;
if (GetMinidumpForID(dumpId, getter_AddRefs(pluginDumpFile))) {
if (GetMinidumpForID(aDumpId, getter_AddRefs(pluginDumpFile))) {
#ifdef MOZ_CRASHREPORTER_INJECTOR
// If we have handles to the flash sandbox processes on Windows,
// include those minidumps as well.
@ -1256,10 +1155,7 @@ PluginModuleChromeParent::OnTakeFullMinidumpComplete(bool aReportsReady,
}
mCrashReporter->AddAnnotation(Annotation::additional_minidumps,
additionalDumps);
mTakeFullMinidumpCallback.Invoke(mCrashReporter->MinidumpID());
} else {
mTakeFullMinidumpCallback.Invoke(EmptyString());
NS_WARNING("failed to capture paired minidumps from hang");
}
}
@ -1268,52 +1164,20 @@ void
PluginModuleChromeParent::TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback,
bool aAsync)
const nsAString& aDumpId)
{
if (!mTerminateChildProcessCallback.IsEmpty()) {
aCallback(false);
return;
}
mTerminateChildProcessCallback.Init(std::move(aCallback), aAsync);
// Start by taking a full minidump if necessary, this is done early
// because it also needs to lock the mCrashReporterMutex and Mutex doesn't
// support recursive locking.
nsAutoString dumpId;
if (aDumpId.IsEmpty()) {
RetainPluginRef();
std::function<void(nsString)> callback =
[this, aMsgLoop, aMonitorDescription, aAsync](nsString aResult) {
if (aAsync) {
this->mCrashReporterMutex.Lock();
}
this->TerminateChildProcessOnDumpComplete(aMsgLoop,
aMonitorDescription);
if (aAsync) {
this->mCrashReporterMutex.Unlock();
}
this->ReleasePluginRef();
};
TakeFullMinidump(aContentPid, EmptyString(), std::move(callback), aAsync);
} else {
TerminateChildProcessOnDumpComplete(aMsgLoop, aMonitorDescription);
TakeFullMinidump(aContentPid, EmptyString(), dumpId);
}
}
void
PluginModuleChromeParent::TerminateChildProcessOnDumpComplete(MessageLoop* aMsgLoop,
const nsCString& aMonitorDescription)
{
mCrashReporterMutex.AssertCurrentThreadIn();
mozilla::MutexAutoLock lock(mCrashReporterMutex);
if (!mCrashReporter) {
// If mCrashReporter is null then the hang has ended, the plugin module
// is shutting down. There's nothing to do here.
mTerminateChildProcessCallback.Invoke(true);
return;
}
mCrashReporter->AddAnnotation(Annotation::PluginHang, true);
@ -1370,8 +1234,6 @@ PluginModuleChromeParent::TerminateChildProcessOnDumpComplete(MessageLoop* aMsgL
if (!childOpened || !KillProcess(geckoChildProcess, 1, false)) {
NS_WARNING("failed to kill subprocess!");
}
mTerminateChildProcessCallback.Invoke(true);
}
bool
@ -1522,7 +1384,7 @@ RemoveMinidump(nsIFile* minidump)
void
PluginModuleChromeParent::ProcessFirstMinidump()
{
mozilla::RecursiveMutexAutoLock lock(mCrashReporterMutex);
mozilla::MutexAutoLock lock(mCrashReporterMutex);
if (!mCrashReporter)
return;

Просмотреть файл

@ -11,13 +11,11 @@
#include "mozilla/FileUtils.h"
#include "mozilla/HangAnnotations.h"
#include "mozilla/PluginLibrary.h"
#include "mozilla/ipc/CrashReporterHost.h"
#include "mozilla/plugins/PluginProcessParent.h"
#include "mozilla/plugins/PPluginModuleParent.h"
#include "mozilla/plugins/PluginMessageUtils.h"
#include "mozilla/plugins/PluginTypes.h"
#include "mozilla/ipc/TaskFactory.h"
#include "mozilla/RecursiveMutex.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/Unused.h"
#include "npapi.h"
@ -34,6 +32,9 @@ class nsPluginTag;
namespace mozilla {
namespace ipc {
class CrashReporterHost;
} // namespace ipc
namespace layers {
class TextureClientRecycleAllocator;
} // namespace layers
@ -318,11 +319,9 @@ protected:
* This mutex protects the crash reporter when the Plugin Hang UI event
* handler is executing off main thread. It is intended to protect both
* the mCrashReporter variable in addition to the CrashReporterHost object
* that mCrashReporter refers to. Sometimes asynchronous crash reporter
* callbacks are dispatched synchronously while the caller is still holding
* the mutex. This requires recursive locking support in the mutex.
* that mCrashReporter refers to.
*/
mozilla::RecursiveMutex mCrashReporterMutex;
mozilla::Mutex mCrashReporterMutex;
UniquePtr<ipc::CrashReporterHost> mCrashReporter;
};
@ -359,10 +358,6 @@ class PluginModuleChromeParent
, public mozilla::BackgroundHangAnnotator
{
friend class mozilla::ipc::CrashReporterHost;
using TerminateChildProcessCallback =
mozilla::ipc::CrashReporterHost::CallbackWrapper<bool>;
using TakeFullMinidumpCallback =
mozilla::ipc::CrashReporterHost::CallbackWrapper<nsString>;
public:
/**
* LoadModule
@ -387,16 +382,12 @@ class PluginModuleChromeParent
* provided TakeFullMinidump will use this dump file instead of
* generating a new one. If not provided a browser dump will be taken at
* the time of this call.
* @param aCallback a callback invoked when the operation completes. The ID
* of the newly generated crash dump is provided in the callback argument.
* An empty string will be provided upon failure.
* @param aAsync whether to perform the dump asynchronously.
* @param aDumpId Returns the ID of the newly generated crash dump. Left
* untouched upon failure.
*/
void
TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
std::function<void(nsString)>&& aCallback,
bool aAsync);
void TakeFullMinidump(base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
nsString& aDumpId);
/*
* Terminates the plugin process associated with this plugin module. Also
@ -414,46 +405,11 @@ class PluginModuleChromeParent
* TerminateChildProcess will use this dump file instead of generating a
* multi-process crash report. If not provided a multi-process dump will
* be taken at the time of this call.
* @param aCallback a callback invoked when the operation completes. The
* argument denotes whether the operation succeeded.
* @param aAsync whether to perform the operation asynchronously.
*/
void
TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId,
std::function<void(bool)>&& aCallback,
bool aAsync);
/**
* Helper for passing a dummy callback in calling the above function if it
* is called synchronously and the caller doesn't care about the callback
* result.
*/
template<typename T>
static std::function<void(T)> DummyCallback()
{
return std::function<void(T)>([](T aResult) { });
}
private:
// The following methods are callbacks invoked after calling
// TakeFullMinidump(). The methods are invoked in the following order:
void TakeBrowserAndPluginMinidumps(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId,
bool aAsync);
void OnTakeFullMinidumpComplete(bool aReportsReady,
base::ProcessId aContentPid,
const nsAString& aBrowserDumpId);
// The following method is the callback invoked after calling
// TerminateChidlProcess().
void TerminateChildProcessOnDumpComplete(MessageLoop* aMsgLoop,
const nsCString& aMonitorDescription);
public:
void TerminateChildProcess(MessageLoop* aMsgLoop,
base::ProcessId aContentPid,
const nsCString& aMonitorDescription,
const nsAString& aDumpId);
#ifdef XP_WIN
/**
@ -486,8 +442,6 @@ private:
void ProcessFirstMinidump();
void WriteExtraDataForMinidump();
void RetainPluginRef();
void ReleasePluginRef();
PluginProcessParent* Process() const { return mSubprocess; }
base::ProcessHandle ChildProcessHandle() { return mSubprocess->GetChildProcessHandle(); }
@ -603,12 +557,6 @@ private:
nsCOMPtr<nsIObserver> mPluginOfflineObserver;
bool mIsBlocklisted;
nsCOMPtr<nsIFile> mBrowserDumpFile;
TakeFullMinidumpCallback mTakeFullMinidumpCallback;
TerminateChildProcessCallback mTerminateChildProcessCallback;
bool mIsCleaningFromTimeout;
};

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше