This commit is contained in:
Ryan VanderMeulen 2015-07-29 13:51:27 -04:00
Родитель 97b9240b34 0cdb86e630
Коммит cbef4f34fc
81 изменённых файлов: 2070 добавлений и 863 удалений

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -19,7 +19,7 @@
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="8bc59310552179f9a8bc6cdd0188e2475df52fb7"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="e862ab9177af664f00b4522e2350f4cb13866d73">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -1,9 +1,9 @@
{
"git": {
"git_revision": "bbf14824939479074049ff4c3888614794c4cec1",
"git_revision": "088f350b39baf8f86c7c1161fd4be178ce822b7b",
"remote": "https://git.mozilla.org/releases/gaia.git",
"branch": ""
},
"revision": "a032af293d40c761c90853a6ffc1e9e3692f3ecd",
"revision": "97666dae0fe5da2a0da4f57f41fcb12e9c2fe709",
"repo_path": "integration/gaia-central"
}

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

@ -17,7 +17,7 @@
</project>
<project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="moztt" path="external/moztt" remote="b2g" revision="657894b4a1dc0a926117f4812e0940229f9f676f"/>
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="5bb657ada461be666c35f419dbe072ed2ce632fc"/>

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

@ -15,7 +15,7 @@
<project name="platform_build" path="build" remote="b2g" revision="07c383a786f188904311a37f6062c2cb84c9b61d">
<copyfile dest="Makefile" src="core/root.mk"/>
</project>
<project name="gaia" path="gaia" remote="mozillaorg" revision="bbf14824939479074049ff4c3888614794c4cec1"/>
<project name="gaia" path="gaia" remote="mozillaorg" revision="088f350b39baf8f86c7c1161fd4be178ce822b7b"/>
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="9f45c1988fe72749f0659409e6e3320fabf7b79a"/>
<project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>

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

@ -126,6 +126,9 @@ let TrackingProtection = {
// Telemetry for disable protection.
this.eventsHistogram.add(1);
// Hide the control center.
document.getElementById("identity-popup").hidePopup();
BrowserReload();
},
@ -146,6 +149,9 @@ let TrackingProtection = {
// Telemetry for enable protection.
this.eventsHistogram.add(2);
// Hide the control center.
document.getElementById("identity-popup").hidePopup();
BrowserReload();
},

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

@ -1139,7 +1139,9 @@ var gBrowserInit = {
gBrowser.addEventListener("AboutTabCrashedLoad", function(event) {
#ifdef MOZ_CRASHREPORTER
TabCrashReporter.onAboutTabCrashedLoad(gBrowser.getBrowserForDocument(event.target));
TabCrashReporter.onAboutTabCrashedLoad(gBrowser.getBrowserForDocument(event.target), {
crashedTabCount: SessionStore.crashedTabCount,
});
#endif
}, false, true);
@ -1171,11 +1173,7 @@ var gBrowserInit = {
SessionStore.reviveCrashedTab(tab);
break;
case "restoreAll":
for (let browserWin of browserWindows()) {
for (let tab of browserWin.gBrowser.tabs) {
SessionStore.reviveCrashedTab(tab);
}
}
SessionStore.reviveAllCrashedTabs();
break;
}
}, false, true);
@ -2411,8 +2409,10 @@ function BrowserViewSource(browser) {
// doc - document to use for source, or null for this window's document
// initialTab - name of the initial tab to display, or null for the first tab
// imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
function BrowserPageInfo(doc, initialTab, imageElement) {
var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
// frameOuterWindowID - the id of the frame that the context menu opened in; can be null/omitted
function BrowserPageInfo(doc, initialTab, imageElement, frameOuterWindowID) {
var args = {doc: doc, initialTab: initialTab, imageElement: imageElement,
frameOuterWindowID: frameOuterWindowID};
var windows = Services.wm.getEnumerator("Browser:page-info");
var documentURL = doc ? doc.location : window.gBrowser.selectedBrowser.contentDocumentAsCPOW.location;
@ -6633,6 +6633,7 @@ var gIdentityHandler = {
IDENTITY_MODE_MIXED_ACTIVE_BLOCKED : "verifiedDomain mixedContent mixedActiveBlocked", // SSL with unauthenticated active content blocked; no unauthenticated display content
IDENTITY_MODE_MIXED_ACTIVE_BLOCKED_IDENTIFIED : "verifiedIdentity mixedContent mixedActiveBlocked", // SSL with unauthenticated active content blocked; no unauthenticated display content
IDENTITY_MODE_CHROMEUI : "chromeUI", // Part of the product's UI
IDENTITY_MODE_FILE_URI : "fileURI", // File path
// Cache the most recent SSLStatus and Location seen in checkIdentity
_lastStatus : null,
@ -6848,7 +6849,20 @@ var gIdentityHandler = {
this.setMode(this.IDENTITY_MODE_USES_WEAK_CIPHER);
}
} else {
this.setMode(this.IDENTITY_MODE_UNKNOWN);
// Create a channel for the sole purpose of getting the resolved URI
// of the request to determine if it's loaded from the file system.
let resolvedURI = NetUtil.newChannel({uri,loadUsingSystemPrincipal:true}).URI;
if (resolvedURI.schemeIs("jar")) {
// Given a URI "jar:<jar-file-uri>!/<jar-entry>"
// create a new URI using <jar-file-uri>!/<jar-entry>
resolvedURI = NetUtil.newURI(resolvedURI.path);
}
if (resolvedURI.schemeIs("file")) {
this.setMode(this.IDENTITY_MODE_FILE_URI);
} else {
this.setMode(this.IDENTITY_MODE_UNKNOWN);
}
}
// Show the doorhanger when:

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

@ -39,6 +39,8 @@ XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() {
return new tmp.PageMenuChild();
});
XPCOMUtils.defineLazyModuleGetter(this, "Feeds", "resource:///modules/Feeds.jsm");
// TabChildGlobal
var global = this;
@ -840,3 +842,396 @@ addMessageListener("ContextMenu:SetAsDesktopBackground", (message) => {
if (disable)
sendAsyncMessage("ContextMenu:SetAsDesktopBackground:Result", { disable });
});
let pageInfoListener = {
init: function(chromeGlobal) {
chromeGlobal.addMessageListener("PageInfo:getData", this, false, true);
},
receiveMessage: function(message) {
this.imageViewRows = [];
this.frameList = [];
this.strings = message.data.strings;
let frameOuterWindowID = message.data.frameOuterWindowID;
// If inside frame then get the frame's window and document.
if (frameOuterWindowID) {
this.window = Services.wm.getOuterWindowWithId(frameOuterWindowID);
this.document = this.window.document;
}
else {
this.document = content.document;
this.window = content.window;
}
let pageInfoData = {metaViewRows: this.getMetaInfo(), docInfo: this.getDocumentInfo(),
feeds: this.getFeedsInfo(), windowInfo: this.getWindowInfo()};
sendAsyncMessage("PageInfo:data", pageInfoData);
// Separate step so page info dialog isn't blank while waiting for this to finish.
this.getMediaInfo();
// Send the message after all the media elements have been walked through.
let pageInfoMediaData = {imageViewRows: this.imageViewRows};
this.imageViewRows = null;
this.frameList = null;
this.strings = null;
this.window = null;
this.document = null;
sendAsyncMessage("PageInfo:mediaData", pageInfoMediaData);
},
getMetaInfo: function() {
let metaViewRows = [];
// Get the meta tags from the page.
let metaNodes = this.document.getElementsByTagName("meta");
for (let metaNode of metaNodes) {
metaViewRows.push([metaNode.name || metaNode.httpEquiv || metaNode.getAttribute("property"),
metaNode.content]);
}
return metaViewRows;
},
getWindowInfo: function() {
let windowInfo = {};
windowInfo.isTopWindow = this.window == this.window.top;
let hostName = null;
try {
hostName = this.window.location.host;
}
catch (exception) { }
windowInfo.hostName = hostName;
return windowInfo;
},
getDocumentInfo: function() {
let docInfo = {};
docInfo.title = this.document.title;
docInfo.location = this.document.location.toString();
docInfo.referrer = this.document.referrer;
docInfo.compatMode = this.document.compatMode;
docInfo.contentType = this.document.contentType;
docInfo.characterSet = this.document.characterSet;
docInfo.lastModified = this.document.lastModified;
let documentURIObject = {};
documentURIObject.spec = this.document.documentURIObject.spec;
documentURIObject.originCharset = this.document.documentURIObject.originCharset;
docInfo.documentURIObject = documentURIObject;
docInfo.isContentWindowPrivate = PrivateBrowsingUtils.isContentWindowPrivate(content);
return docInfo;
},
getFeedsInfo: function() {
let feeds = [];
// Get the feeds from the page.
let linkNodes = this.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 each (let relVal in rel.split(/\s+/)) {
rels[relVal] = true;
}
}
if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
let type = Feeds.isValidFeed(link, this.document.nodePrincipal, "feed" in rels);
if (type) {
type = this.strings[type] || this.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.
// The actual work is done with a TreeWalker that calls doGrab() once for
// each element node in the document.
getMediaInfo: function()
{
this.goThroughFrames(this.document, this.window);
this.processFrames();
},
goThroughFrames: function(aDocument, aWindow)
{
this.frameList.push(aDocument);
if (aWindow && aWindow.frames.length > 0) {
let num = aWindow.frames.length;
for (let i = 0; i < num; i++) {
this.goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]); // recurse through the frames
}
}
},
processFrames: function()
{
if (this.frameList.length) {
let doc = this.frameList[0];
let iterator = doc.createTreeWalker(doc, content.NodeFilter.SHOW_ELEMENT, elem => this.grabAll(elem));
this.frameList.shift();
this.doGrab(iterator);
}
},
/**
* This function's previous purpose in pageInfo.js was to get loop through 500 elements at a time.
* The iterator filter will filter for media elements.
* #TODO Bug 1175794: refactor pageInfo.js to receive a media element at a time
* from messages and continually update UI.
*/
doGrab: function(iterator)
{
while (true)
{
if (!iterator.nextNode()) {
this.processFrames();
return;
}
}
},
grabAll: function(elem)
{
// Check for images defined in CSS (e.g. background, borders), any node may have multiple.
let computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
let addImage = (url, type, alt, elem, isBg) => {
let element = this.serializeElementInfo(url, type, alt, elem, isBg);
this.imageViewRows.push([url, type, alt, element, isBg]);
};
if (computedStyle) {
let addImgFunc = (label, val) => {
if (val.primitiveType == content.CSSPrimitiveValue.CSS_URI) {
addImage(val.getStringValue(), label, this.strings.notSet, elem, true);
}
else if (val.primitiveType == content.CSSPrimitiveValue.CSS_STRING) {
// This is for -moz-image-rect.
// TODO: Reimplement once bug 714757 is fixed.
let strVal = val.getStringValue();
if (strVal.search(/^.*url\(\"?/) > -1) {
let url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
addImage(url, label, this.strings.notSet, elem, true);
}
}
else if (val.cssValueType == content.CSSValue.CSS_VALUE_LIST) {
// Recursively resolve multiple nested CSS value lists.
for (let i = 0; i < val.length; i++) {
addImgFunc(label, val.item(i));
}
}
};
addImgFunc(this.strings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
addImgFunc(this.strings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
addImgFunc(this.strings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
addImgFunc(this.strings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
}
// One swi^H^H^Hif-else to rule them all.
if (elem instanceof content.HTMLImageElement) {
addImage(elem.src, this.strings.mediaImg,
(elem.hasAttribute("alt")) ? elem.alt : this.strings.notSet, elem, false);
}
else if (elem instanceof content.SVGImageElement) {
try {
// Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
// or the URI formed from the baseURI and the URL is not a valid URI.
let href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
addImage(href, this.strings.mediaImg, "", elem, false);
} catch (e) { }
}
else if (elem instanceof content.HTMLVideoElement) {
addImage(elem.currentSrc, this.strings.mediaVideo, "", elem, false);
}
else if (elem instanceof content.HTMLAudioElement) {
addImage(elem.currentSrc, this.strings.mediaAudio, "", elem, false);
}
else if (elem instanceof content.HTMLLinkElement) {
if (elem.rel && /\bicon\b/i.test(elem.rel)) {
addImage(elem.href, this.strings.mediaLink, "", elem, false);
}
}
else if (elem instanceof content.HTMLInputElement || elem instanceof content.HTMLButtonElement) {
if (elem.type.toLowerCase() == "image") {
addImage(elem.src, this.strings.mediaInput,
(elem.hasAttribute("alt")) ? elem.alt : this.strings.notSet, elem, false);
}
}
else if (elem instanceof content.HTMLObjectElement) {
addImage(elem.data, this.strings.mediaObject, this.getValueText(elem), elem, false);
}
else if (elem instanceof content.HTMLEmbedElement) {
addImage(elem.src, this.strings.mediaEmbed, "", elem, false);
}
return content.NodeFilter.FILTER_ACCEPT;
},
/**
* Set up a JSON element object with all the instanceOf and other infomation that
* makePreview in pageInfo.js uses to figure out how to display the preview.
*/
serializeElementInfo: function(url, type, alt, item, isBG)
{
// Interface for image loading content.
const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
let result = {};
let imageText;
if (!isBG &&
!(item instanceof content.SVGImageElement) &&
!(this.document instanceof content.ImageDocument)) {
imageText = item.title || item.alt;
if (!imageText && !(item instanceof content.HTMLImageElement)) {
imageText = this.getValueText(item);
}
}
result.imageText = imageText;
result.longDesc = item.longDesc;
result.numFrames = 1;
if (item instanceof content.HTMLObjectElement ||
item instanceof content.HTMLEmbedElement ||
item instanceof content.HTMLLinkElement) {
result.mimeType = item.type;
}
if (!result.mimeType && !isBG && item instanceof nsIImageLoadingContent) {
// Interface for image loading content.
const nsIImageLoadingContent = Components.interfaces.nsIImageLoadingContent;
let imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
if (imageRequest) {
result.mimeType = imageRequest.mimeType;
let image = !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) && imageRequest.image;
if (image) {
result.numFrames = image.numFrames;
}
}
}
// if we have a data url, get the MIME type from the url
if (!result.mimeType && url.startsWith("data:")) {
let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
if (dataMimeType)
result.mimeType = dataMimeType[1].toLowerCase();
}
result.HTMLLinkElement = item instanceof content.HTMLLinkElement;
result.HTMLInputElement = item instanceof content.HTMLInputElement;
result.HTMLImageElement = item instanceof content.HTMLImageElement;
result.HTMLObjectElement = item instanceof content.HTMLObjectElement;
result.SVGImageElement = item instanceof content.SVGImageElement;
result.HTMLVideoElement = item instanceof content.HTMLVideoElement;
result.HTMLAudioElement = item instanceof content.HTMLAudioElement;
if (!isBG) {
result.width = item.width;
result.height = item.height;
}
if (item instanceof content.SVGImageElement) {
result.SVGImageElementWidth = item.width.baseVal.value;
result.SVGImageElementHeight = item.height.baseVal.value;
}
result.baseURI = item.baseURI;
return result;
},
//******** Other Misc Stuff
// Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
// parse a node to extract the contents of the node
getValueText: function(node)
{
let valueText = "";
// Form input elements don't generally contain information that is useful to our callers, so return nothing.
if (node instanceof content.HTMLInputElement ||
node instanceof content.HTMLSelectElement ||
node instanceof content.HTMLTextAreaElement) {
return valueText;
}
// Otherwise recurse for each child.
let length = node.childNodes.length;
for (let i = 0; i < length; i++) {
let childNode = node.childNodes[i];
let nodeType = childNode.nodeType;
// Text nodes are where the goods are.
if (nodeType == content.Node.TEXT_NODE) {
valueText += " " + childNode.nodeValue;
}
// And elements can have more text inside them.
else if (nodeType == content.Node.ELEMENT_NODE) {
// Images are special, we want to capture the alt text as if the image weren't there.
if (childNode instanceof content.HTMLImageElement) {
valueText += " " + this.getAltText(childNode);
}
else {
valueText += " " + this.getValueText(childNode);
}
}
}
return this.stripWS(valueText);
},
// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
// Traverse the tree in search of an img or area element and grab its alt tag.
getAltText: function(node)
{
let altText = "";
if (node.alt) {
return node.alt;
}
let length = node.childNodes.length;
for (let i = 0; i < length; i++) {
if ((altText = this.getAltText(node.childNodes[i]) != undefined)) { // stupid js warning...
return altText;
}
}
return "";
},
// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
// Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
stripWS: function(text)
{
let middleRE = /\s+/g;
let endRE = /(^\s+)|(\s+$)/g;
text = text.replace(middleRE, " ");
return text.replace(endRE, "");
}
};
pageInfoListener.init(this);

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

@ -96,10 +96,6 @@ ContentSearchUIController.prototype = {
set engines(val) {
this._engines = val;
if (!this._table.hidden) {
this._setUpOneOffButtons();
return;
}
this._pendingOneOffRefresh = true;
},
@ -127,6 +123,9 @@ ContentSearchUIController.prototype = {
let allElts = [...this._suggestionsList.children,
...this._oneOffButtons,
document.getElementById("contentSearchSettingsButton")];
// If we are selecting a suggestion and a one-off is selected, don't deselect it.
let excludeIndex = idx < this.numSuggestions && this.selectedButtonIndex > -1 ?
this.numSuggestions + this.selectedButtonIndex : -1;
for (let i = 0; i < allElts.length; ++i) {
let elt = allElts[i];
let ariaSelectedElt = i < this.numSuggestions ? elt.firstChild : elt;
@ -135,16 +134,43 @@ ContentSearchUIController.prototype = {
ariaSelectedElt.setAttribute("aria-selected", "true");
this.input.setAttribute("aria-activedescendant", ariaSelectedElt.id);
}
else {
else if (i != excludeIndex) {
elt.classList.remove("selected");
ariaSelectedElt.setAttribute("aria-selected", "false");
}
}
},
get selectedButtonIndex() {
let elts = [...this._oneOffButtons,
document.getElementById("contentSearchSettingsButton")];
for (let i = 0; i < elts.length; ++i) {
if (elts[i].classList.contains("selected")) {
return i;
}
}
return -1;
},
set selectedButtonIndex(idx) {
let elts = [...this._oneOffButtons,
document.getElementById("contentSearchSettingsButton")];
for (let i = 0; i < elts.length; ++i) {
let elt = elts[i];
if (i == idx) {
elt.classList.add("selected");
elt.setAttribute("aria-selected", "true");
}
else {
elt.classList.remove("selected");
elt.setAttribute("aria-selected", "false");
}
}
},
get selectedEngineName() {
let selectedElt = this._table.querySelector(".selected");
if (selectedElt && selectedElt.engineName) {
let selectedElt = this._oneOffsTable.querySelector(".selected");
if (selectedElt) {
return selectedElt.engineName;
}
return this.defaultEngine.name;
@ -194,7 +220,7 @@ ContentSearchUIController.prototype = {
},
_onCommand: function(aEvent) {
if (this.selectedIndex == this.numSuggestions + this._oneOffButtons.length) {
if (this.selectedButtonIndex == this._oneOffButtons.length) {
// Settings button was selected.
this._sendMsg("ManageEngines");
return;
@ -264,19 +290,58 @@ ContentSearchUIController.prototype = {
_onKeypress: function (event) {
let selectedIndexDelta = 0;
let selectedSuggestionDelta = 0;
let selectedOneOffDelta = 0;
switch (event.keyCode) {
case event.DOM_VK_UP:
if (!this._table.hidden) {
selectedIndexDelta = -1;
if (this._table.hidden) {
return;
}
if (event.getModifierState("Accel")) {
if (event.shiftKey) {
selectedSuggestionDelta = -1;
break;
}
this._cycleCurrentEngine(true);
break;
}
if (event.altKey) {
selectedOneOffDelta = -1;
break;
}
selectedIndexDelta = -1;
break;
case event.DOM_VK_DOWN:
if (this._table.hidden) {
this._getSuggestions();
return;
}
else {
selectedIndexDelta = 1;
if (event.getModifierState("Accel")) {
if (event.shiftKey) {
selectedSuggestionDelta = 1;
break;
}
this._cycleCurrentEngine(false);
break;
}
if (event.altKey) {
selectedOneOffDelta = 1;
break;
}
selectedIndexDelta = 1;
break;
case event.DOM_VK_TAB:
if (this._table.hidden) {
return;
}
// Shift+tab when either the first or no one-off is selected, as well as
// tab when the settings button is selected, should change focus as normal.
if ((this.selectedButtonIndex <= 0 && event.shiftKey) ||
this.selectedButtonIndex == this._oneOffButtons.length && !event.shiftKey) {
return;
}
selectedOneOffDelta = event.shiftKey ? -1 : 1;
break;
case event.DOM_VK_RIGHT:
// Allow normal caret movement until the caret is at the end of the input.
@ -297,37 +362,97 @@ ContentSearchUIController.prototype = {
}
this._stickyInputValue = this.input.value;
this._hideSuggestions();
break;
return;
case event.DOM_VK_RETURN:
this._onCommand(event);
break;
return;
case event.DOM_VK_DELETE:
if (this.selectedIndex >= 0) {
this.deleteSuggestionAtIndex(this.selectedIndex);
}
break;
return;
case event.DOM_VK_ESCAPE:
if (!this._table.hidden) {
this._hideSuggestions();
}
return;
default:
return;
}
let currentIndex = this.selectedIndex;
if (selectedIndexDelta) {
// Update the selection.
let newSelectedIndex = this.selectedIndex + selectedIndexDelta;
let newSelectedIndex = currentIndex + selectedIndexDelta;
if (newSelectedIndex < -1) {
newSelectedIndex = this.numSuggestions + this._oneOffButtons.length;
}
else if (this.numSuggestions + this._oneOffButtons.length < newSelectedIndex) {
// If are moving up from the first one off, we have to deselect the one off
// manually because the selectedIndex setter tries to exclude the selected
// one-off (which is desirable for accel+shift+up/down).
if (currentIndex == this.numSuggestions && selectedIndexDelta == -1) {
this.selectedButtonIndex = -1;
}
this.selectAndUpdateInput(newSelectedIndex);
}
else if (selectedSuggestionDelta) {
let newSelectedIndex;
if (currentIndex >= this.numSuggestions || currentIndex == -1) {
// No suggestion already selected, select the first/last one appropriately.
newSelectedIndex = selectedSuggestionDelta == 1 ?
0 : this.numSuggestions - 1;
}
else {
newSelectedIndex = currentIndex + selectedSuggestionDelta;
}
if (newSelectedIndex >= this.numSuggestions) {
newSelectedIndex = -1;
}
this.selectAndUpdateInput(newSelectedIndex);
// Prevent the input's caret from moving.
event.preventDefault();
}
else if (selectedOneOffDelta) {
let newSelectedIndex;
let currentButton = this.selectedButtonIndex;
if (currentButton == -1 || currentButton == this._oneOffButtons.length) {
// No one-off already selected, select the first/last one appropriately.
newSelectedIndex = selectedOneOffDelta == 1 ?
0 : this._oneOffButtons.length - 1;
}
else {
newSelectedIndex = currentButton + selectedOneOffDelta;
}
// Allow selection of the settings button via the tab key.
if (newSelectedIndex == this._oneOffButtons.length &&
event.keyCode != event.DOM_VK_TAB) {
newSelectedIndex = -1;
}
this.selectedButtonIndex = newSelectedIndex;
}
// Prevent the input's caret from moving.
event.preventDefault();
},
_currentEngineIndex: -1,
_cycleCurrentEngine: function (aReverse) {
if ((this._currentEngineIndex == this._oneOffButtons.length - 1 && !aReverse) ||
(this._currentEngineIndex < 0 && aReverse)) {
return;
}
this._currentEngineIndex += aReverse ? -1 : 1;
let engine;
if (this._currentEngineIndex == -1) {
engine = this._originalDefaultEngine;
} else {
let button = this._oneOffButtons[this._currentEngineIndex];
engine = {
name: button.engineName,
icon: button.firstChild.getAttribute("src"),
};
}
this._sendMsg("SetCurrentEngine", engine.name);
this.defaultEngine = engine;
},
_onFocus: function () {
@ -356,7 +481,12 @@ ContentSearchUIController.prototype = {
},
_onMousemove: function (event) {
this.selectedIndex = this._indexOfTableItem(event.target);
let idx = this._indexOfTableItem(event.target);
if (idx >= this.numSuggestions) {
this.selectedButtonIndex = idx - this.numSuggestions;
return;
}
this.selectedIndex = idx;
},
_onMouseup: function (event) {
@ -366,6 +496,15 @@ ContentSearchUIController.prototype = {
this._onCommand(event);
},
_onMouseout: function (event) {
// We only deselect one-off buttons and the settings button when they are
// moused out.
let idx = this._indexOfTableItem(event.originalTarget);
if (idx >= this.numSuggestions) {
this.selectedButtonIndex = -1;
}
},
_onClick: function (event) {
this._onMouseup(event);
},
@ -427,6 +566,10 @@ ContentSearchUIController.prototype = {
}
this._table.hidden = false;
this.input.setAttribute("aria-expanded", "true");
this._originalDefaultEngine = {
name: this.defaultEngine.name,
icon: this.defaultEngine.icon,
};
}
},
@ -447,10 +590,6 @@ ContentSearchUIController.prototype = {
name: engine.name,
icon: this._getFaviconURIFromBuffer(engine.iconBuffer),
};
if (!this._table.hidden) {
this._setUpOneOffButtons();
return;
}
this._pendingOneOffRefresh = true;
},
@ -572,6 +711,9 @@ ContentSearchUIController.prototype = {
_hideSuggestions: function () {
this.input.setAttribute("aria-expanded", "false");
this.selectedIndex = -1;
this.selectedButtonIndex = -1;
this._currentEngineIndex = -1;
this._table.hidden = true;
},
@ -605,11 +747,7 @@ ContentSearchUIController.prototype = {
document.addEventListener("mouseup", () => { delete this._mousedown; });
// Deselect the selected element on mouseout if it wasn't a suggestion.
this._table.addEventListener("mouseout", () => {
if (this.selectedIndex >= this.numSuggestions) {
this.selectAndUpdateInput(-1);
}
});
this._table.addEventListener("mouseout", this);
// If a search is loaded in the same tab, ensure the suggestions dropdown
// is hidden immediately when the page starts loading and not when it first

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

@ -1051,7 +1051,8 @@ nsContextMenu.prototype = {
},
viewFrameInfo: function() {
BrowserPageInfo(this.target.ownerDocument);
BrowserPageInfo(this.target.ownerDocument, null, null,
this.frameOuterWindowID);
},
reloadImage: function() {

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

@ -3,41 +3,11 @@
* 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/. */
XPCOMUtils.defineLazyModuleGetter(this, "Feeds",
"resource:///modules/Feeds.jsm");
function initFeedTab()
function initFeedTab(feeds)
{
const feedTypes = {
"application/rss+xml": gBundle.getString("feedRss"),
"application/atom+xml": gBundle.getString("feedAtom"),
"text/xml": gBundle.getString("feedXML"),
"application/xml": gBundle.getString("feedXML"),
"application/rdf+xml": gBundle.getString("feedXML")
};
// get the feeds
var linkNodes = gDocument.getElementsByTagName("link");
var length = linkNodes.length;
for (var i = 0; i < length; i++) {
var link = linkNodes[i];
if (!link.href)
continue;
var rel = link.rel && link.rel.toLowerCase();
var rels = {};
if (rel) {
for each (let relVal in rel.split(/\s+/))
rels[relVal] = true;
}
if (rels.feed || (link.type && rels.alternate && !rels.stylesheet)) {
var type = Feeds.isValidFeed(link, gDocument.nodePrincipal, "feed" in rels);
if (type) {
type = feedTypes[type] || feedTypes["application/rss+xml"];
addRow(link.title, type, link.href);
}
}
for (let feed of feeds) {
let [name, type, url] = feed;
addRow(name, type, url);
}
var feedListbox = document.getElementById("feedListbox");

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

@ -52,8 +52,19 @@ pageInfoTreeView.prototype = {
{
this.rows = this.data.push(row);
this.rowCountChanged(this.rows - 1, 1);
if (this.selection.count == 0 && this.rowCount && !gImageElement)
if (this.selection.count == 0 && this.rowCount && !gImageElement) {
this.selection.select(0);
}
},
addRows: function(rows)
{
this.data = this.data.concat(rows);
this.rowCountChanged(this.rows, rows.length);
this.rows = this.data.length;
if (this.selection.count == 0 && this.rowCount && !gImageElement) {
this.selection.select(0);
}
},
rowCountChanged: function(index, count)
@ -140,8 +151,7 @@ pageInfoTreeView.prototype = {
};
// mmm, yummy. global variables.
var gWindow = null;
var gDocument = null;
var gDocInfo = null;
var gImageElement = null;
// column number to help using the data array
@ -286,8 +296,7 @@ const XHTMLre = RegExp(XHTMLNSre + "|" + XHTML2NSre, "");
* invoked as "XXXLoadFunc();"
*/
// These functions are called to build the data displayed in the Page
// Info window. The global variables gDocument and gWindow are set.
// These functions are called to build the data displayed in the Page Info window.
var onLoadRegistry = [ ];
// These functions are called to remove old data still displayed in
@ -296,14 +305,6 @@ var onLoadRegistry = [ ];
// tab is cleared.
var onResetRegistry = [ ];
// These are called once for each subframe of the target document and
// the target document itself. The frame is passed as an argument.
var onProcessFrame = [ ];
// These functions are called once for each element (in all subframes, if any)
// in the target document. The element is passed as an argument.
var onProcessElement = [ ];
// These functions are called once when all the elements in all of the target
// document (and all of its subframes, if any) have been processed
var onFinished = [ ];
@ -311,9 +312,6 @@ var onFinished = [ ];
// These functions are called once when the Page Info window is closed.
var onUnloadRegistry = [ ];
// These functions are called once when an image preview is shown.
var onImagePreviewShown = [ ];
/* Called when PageInfo window is loaded. Arguments are:
* window.arguments[0] - (optional) an object consisting of
* - doc: (optional) document to use for source. if not provided,
@ -341,11 +339,6 @@ function onLoadPageInfo()
window.arguments.length >= 1 &&
window.arguments[0];
if (!args || !args.doc) {
gWindow = window.opener.gBrowser.selectedBrowser.contentWindowAsCPOW;
gDocument = gWindow.document;
}
// init media view
var imageTree = document.getElementById("imagetree");
imageTree.view = gImageView;
@ -357,22 +350,52 @@ function onLoadPageInfo()
.notifyObservers(window, "page-info-dialog-loaded", null);
}
function loadPageInfo()
function loadPageInfo(frameOuterWindowID)
{
var titleFormat = gWindow != gWindow.top ? "pageInfo.frame.title"
: "pageInfo.page.title";
document.title = gBundle.getFormattedString(titleFormat, [gDocument.location]);
let mm = window.opener.gBrowser.selectedBrowser.messageManager;
document.getElementById("main-window").setAttribute("relatedUrl", gDocument.location);
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");
// do the easy stuff first
makeGeneralTab();
// Look for pageInfoListener in content.js. Sends message to listener with arguments.
mm.sendAsyncMessage("PageInfo:getData", {strings: gStrings,
frameOuterWindowID: frameOuterWindowID});
// and then the hard stuff
makeTabs(gDocument, gWindow);
let pageInfoData = null;
initFeedTab();
onLoadPermission();
// Get initial pageInfoData needed to display the general, feeds, permission and security tabs.
mm.addMessageListener("PageInfo:data", function onmessage(message) {
mm.removeMessageListener("PageInfo:data", onmessage);
pageInfoData = message.data;
let docInfo = pageInfoData.docInfo;
let windowInfo = pageInfoData.windowInfo;
let uri = makeURI(docInfo.documentURIObject.spec,
docInfo.documentURIObject.originCharset);
gDocInfo = docInfo;
var titleFormat = windowInfo.isTopWindow ? "pageInfo.frame.title"
: "pageInfo.page.title";
document.title = gBundle.getFormattedString(titleFormat, [docInfo.location]);
document.getElementById("main-window").setAttribute("relatedUrl", docInfo.location);
makeGeneralTab(pageInfoData.metaViewRows, docInfo);
initFeedTab(pageInfoData.feeds);
onLoadPermission(uri);
securityOnLoad(uri, windowInfo);
});
// Get the media elements from content script to setup the media tab.
mm.addMessageListener("PageInfo:mediaData", function onmessage(message){
mm.removeMessageListener("PageInfo:mediaData", onmessage);
makeMediaTab(message.data.imageViewRows);
// Loop through onFinished and execute the functions on it.
onFinished.forEach(function(func) { func(pageInfoData); });
});
/* Call registered overlay init functions */
onLoadRegistry.forEach(function(func) { func(); });
@ -443,15 +466,22 @@ function showTab(id)
function loadTab(args)
{
if (args && args.doc) {
gDocument = args.doc;
gWindow = gDocument.defaultView;
// If the "View Image Info" context menu item was used, the related image
// element is provided as an argument. This can't be a background image.
let imageElement = args && args.imageElement;
if (imageElement) {
gImageElement = {currentSrc: imageElement.currentSrc,
width: imageElement.width, height: imageElement.height,
imageText: imageElement.title || imageElement.alt};
}
else {
gImageElement = null;
}
gImageElement = args && args.imageElement;
let frameOuterWindowID = args && args.frameOuterWindowID;
/* Load the page info */
loadPageInfo();
loadPageInfo(frameOuterWindowID);
var initialTab = (args && args.initialTab) || "generalTab";
var radioGroup = document.getElementById("viewGroup");
@ -491,31 +521,29 @@ function openCacheEntry(key, cb)
diskStorage.asyncOpenURI(Services.io.newURI(key, null, null), "", nsICacheStorage.OPEN_READONLY, checkCacheListener);
}
function makeGeneralTab()
function makeGeneralTab(metaViewRows, docInfo)
{
var title = (gDocument.title) ? gBundle.getFormattedString("pageTitle", [gDocument.title]) : gBundle.getString("noPageTitle");
var title = (docInfo.title) ? gBundle.getFormattedString("pageTitle", [docInfo.title]) : gBundle.getString("noPageTitle");
document.getElementById("titletext").value = title;
var url = gDocument.location.toString();
var url = docInfo.location;
setItemValue("urltext", url);
var referrer = ("referrer" in gDocument && gDocument.referrer);
var referrer = ("referrer" in docInfo && docInfo.referrer);
setItemValue("refertext", referrer);
var mode = ("compatMode" in gDocument && gDocument.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
var mode = ("compatMode" in docInfo && docInfo.compatMode == "BackCompat") ? "generalQuirksMode" : "generalStrictMode";
document.getElementById("modetext").value = gBundle.getString(mode);
// find out the mime type
var mimeType = gDocument.contentType;
var mimeType = docInfo.contentType;
setItemValue("typetext", mimeType);
// get the document characterset
var encoding = gDocument.characterSet;
var encoding = docInfo.characterSet;
document.getElementById("encodingtext").value = encoding;
// get the meta tags
var metaNodes = gDocument.getElementsByTagName("meta");
var length = metaNodes.length;
let length = metaViewRows.length;
var metaGroup = document.getElementById("metaTags");
if (!length)
@ -529,15 +557,14 @@ function makeGeneralTab()
var metaTree = document.getElementById("metatree");
metaTree.view = gMetaView;
for (var i = 0; i < length; i++)
gMetaView.addRow([metaNodes[i].name || metaNodes[i].httpEquiv || metaNodes[i].getAttribute("property"),
metaNodes[i].content]);
// Add the metaViewRows onto the general tab's meta info tree.
gMetaView.addRows(metaViewRows);
metaGroup.collapsed = false;
}
// get the date of last modification
var modifiedText = formatDate(gDocument.lastModified, gStrings.notSet);
var modifiedText = formatDate(docInfo.lastModified, gStrings.notSet);
document.getElementById("modifiedtext").value = modifiedText;
// get cache info
@ -551,57 +578,16 @@ function makeGeneralTab()
}
setItemValue("sizetext", sizeText);
});
securityOnLoad();
}
//******** Generic Build-a-tab
// Assumes the views are empty. Only called once to build the tabs, and
// does so by farming the task off to another thread via setTimeout().
// The actual work is done with a TreeWalker that calls doGrab() once for
// each element node in the document.
var gFrameList = [ ];
function makeTabs(aDocument, aWindow)
function makeMediaTab(imageViewRows)
{
goThroughFrames(aDocument, aWindow);
processFrames();
}
function goThroughFrames(aDocument, aWindow)
{
gFrameList.push(aDocument);
if (aWindow && aWindow.frames.length > 0) {
var num = aWindow.frames.length;
for (var i = 0; i < num; i++)
goThroughFrames(aWindow.frames[i].document, aWindow.frames[i]); // recurse through the frames
// Call addImage passing in the image rows to add to the view on the Media Tab.
for (let image of imageViewRows) {
let [url, type, alt, elem, isBg] = image;
addImage(url, type, alt, elem, isBg);
}
}
function processFrames()
{
if (gFrameList.length) {
var doc = gFrameList[0];
onProcessFrame.forEach(function(func) { func(doc); });
var iterator = doc.createTreeWalker(doc, NodeFilter.SHOW_ELEMENT, grabAll);
gFrameList.shift();
setTimeout(doGrab, 10, iterator);
onFinished.push(selectImage);
}
else
onFinished.forEach(function(func) { func(); });
}
function doGrab(iterator)
{
for (var i = 0; i < 500; ++i)
if (!iterator.nextNode()) {
processFrames();
return;
}
setTimeout(doGrab, 10, iterator);
selectImage();
}
function addImage(url, type, alt, elem, isBg)
@ -639,80 +625,19 @@ function addImage(url, type, alt, elem, isBg)
else {
var i = gImageHash[url][type][alt];
gImageView.data[i][COL_IMAGE_COUNT]++;
if (elem == gImageElement)
// The same image can occur several times on the page at different sizes.
// If the "View Image Info" context menu item was used, ensure we select
// the correct element.
if (!gImageView.data[i][COL_IMAGE_BG] &&
gImageElement && url == gImageElement.currentSrc &&
gImageElement.width == elem.width &&
gImageElement.height == elem.height &&
gImageElement.imageText == elem.imageText) {
gImageView.data[i][COL_IMAGE_NODE] = elem;
}
}
}
function grabAll(elem)
{
// check for images defined in CSS (e.g. background, borders), any node may have multiple
var computedStyle = elem.ownerDocument.defaultView.getComputedStyle(elem, "");
if (computedStyle) {
var addImgFunc = function (label, val) {
if (val.primitiveType == CSSPrimitiveValue.CSS_URI) {
addImage(val.getStringValue(), label, gStrings.notSet, elem, true);
}
else if (val.primitiveType == CSSPrimitiveValue.CSS_STRING) {
// This is for -moz-image-rect.
// TODO: Reimplement once bug 714757 is fixed
var strVal = val.getStringValue();
if (strVal.search(/^.*url\(\"?/) > -1) {
url = strVal.replace(/^.*url\(\"?/,"").replace(/\"?\).*$/,"");
addImage(url, label, gStrings.notSet, elem, true);
}
}
else if (val.cssValueType == CSSValue.CSS_VALUE_LIST) {
// recursively resolve multiple nested CSS value lists
for (var i = 0; i < val.length; i++)
addImgFunc(label, val.item(i));
}
};
addImgFunc(gStrings.mediaBGImg, computedStyle.getPropertyCSSValue("background-image"));
addImgFunc(gStrings.mediaBorderImg, computedStyle.getPropertyCSSValue("border-image-source"));
addImgFunc(gStrings.mediaListImg, computedStyle.getPropertyCSSValue("list-style-image"));
addImgFunc(gStrings.mediaCursor, computedStyle.getPropertyCSSValue("cursor"));
}
// one swi^H^H^Hif-else to rule them all
if (elem instanceof HTMLImageElement)
addImage(elem.src, gStrings.mediaImg,
(elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
else if (elem instanceof SVGImageElement) {
try {
// Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
// or the URI formed from the baseURI and the URL is not a valid URI
var href = makeURLAbsolute(elem.baseURI, elem.href.baseVal);
addImage(href, gStrings.mediaImg, "", elem, false);
} catch (e) { }
}
else if (elem instanceof HTMLVideoElement) {
addImage(elem.currentSrc, gStrings.mediaVideo, "", elem, false);
}
else if (elem instanceof HTMLAudioElement) {
addImage(elem.currentSrc, gStrings.mediaAudio, "", elem, false);
}
else if (elem instanceof HTMLLinkElement) {
if (elem.rel && /\bicon\b/i.test(elem.rel))
addImage(elem.href, gStrings.mediaLink, "", elem, false);
}
else if (elem instanceof HTMLInputElement || elem instanceof HTMLButtonElement) {
if (elem.type.toLowerCase() == "image")
addImage(elem.src, gStrings.mediaInput,
(elem.hasAttribute("alt")) ? elem.alt : gStrings.notSet, elem, false);
}
else if (elem instanceof HTMLObjectElement)
addImage(elem.data, gStrings.mediaObject, getValueText(elem), elem, false);
else if (elem instanceof HTMLEmbedElement)
addImage(elem.src, gStrings.mediaEmbed, "", elem, false);
onProcessElement.forEach(function(func) { func(elem); });
return NodeFilter.FILTER_ACCEPT;
}
//******** Link Stuff
function openURL(target)
{
@ -814,14 +739,15 @@ function saveMedia()
else if (item instanceof HTMLAudioElement)
titleKey = "SaveAudioTitle";
saveURL(url, null, titleKey, false, false, makeURI(item.baseURI), gDocument);
saveURL(url, null, titleKey, false, false, makeURI(item.baseURI),
null, gDocInfo.isContentWindowPrivate);
}
} else {
selectSaveFolder(function(aDirectory) {
if (aDirectory) {
var saveAnImage = function(aURIString, aChosenData, aBaseURI) {
internalSave(aURIString, null, null, null, null, false, "SaveImageTitle",
aChosenData, aBaseURI, gDocument);
aChosenData, aBaseURI, null, gDocInfo.isContentWindowPrivate);
};
for (var i = 0; i < rowArray.length; i++) {
@ -893,6 +819,7 @@ function onImageSelect()
}
}
// Makes the media preview (image, video, etc) for the selected row on the media tab.
function makePreview(row)
{
var imageTree = document.getElementById("imagetree");
@ -902,18 +829,7 @@ function makePreview(row)
var isAudio = false;
setItemValue("imageurltext", url);
var imageText;
if (!isBG &&
!(item instanceof SVGImageElement) &&
!(gDocument instanceof ImageDocument)) {
imageText = item.title || item.alt;
if (!imageText && !(item instanceof HTMLImageElement))
imageText = getValueText(item);
}
setItemValue("imagetext", imageText);
setItemValue("imagetext", item.imageText);
setItemValue("imagelongdesctext", item.longDesc);
// get cache info
@ -931,32 +847,8 @@ function makePreview(row)
sizeText = gBundle.getString("mediaUnknownNotCached");
setItemValue("imagesizetext", sizeText);
var mimeType;
var numFrames = 1;
if (item instanceof HTMLObjectElement ||
item instanceof HTMLEmbedElement ||
item instanceof HTMLLinkElement)
mimeType = item.type;
if (!mimeType && !isBG && item instanceof nsIImageLoadingContent) {
var imageRequest = item.getRequest(nsIImageLoadingContent.CURRENT_REQUEST);
if (imageRequest) {
mimeType = imageRequest.mimeType;
var image = imageRequest.image;
if (image)
numFrames = image.numFrames;
}
}
if (!mimeType)
mimeType = getContentTypeFromHeaders(cacheEntry);
// if we have a data url, get the MIME type from the url
if (!mimeType && url.startsWith("data:")) {
let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
if (dataMimeType)
mimeType = dataMimeType[1].toLowerCase();
}
var mimeType = item.mimeType || this.getContentTypeFromHeaders(cacheEntry);
var numFrames = item.numFrames;
var imageType;
if (mimeType) {
@ -991,10 +883,10 @@ function makePreview(row)
var physWidth = 0, physHeight = 0;
var width = 0, height = 0;
if ((item instanceof HTMLLinkElement || item instanceof HTMLInputElement ||
item instanceof HTMLImageElement ||
item instanceof SVGImageElement ||
(item instanceof HTMLObjectElement && mimeType && mimeType.startsWith("image/")) || isBG) && isProtocolAllowed) {
if ((item.HTMLLinkElement || item.HTMLInputElement ||
item.HTMLImageElement || item.SVGImageElement ||
(item.HTMLObjectElement && mimeType && mimeType.startsWith("image/")) ||
isBG) && isProtocolAllowed) {
newImage.setAttribute("src", url);
physWidth = newImage.width || 0;
physHeight = newImage.height || 0;
@ -1013,9 +905,9 @@ function makePreview(row)
newImage.height = newImage.naturalHeight;
}
if (item instanceof SVGImageElement) {
newImage.width = item.width.baseVal.value;
newImage.height = item.height.baseVal.value;
if (item.SVGImageElement) {
newImage.width = item.SVGImageElementWidth;
newImage.height = item.SVGImageElementHeight;
}
width = newImage.width;
@ -1024,7 +916,7 @@ function makePreview(row)
document.getElementById("theimagecontainer").collapsed = false
document.getElementById("brokenimagecontainer").collapsed = true;
}
else if (item instanceof HTMLVideoElement && isProtocolAllowed) {
else if (item.HTMLVideoElement && isProtocolAllowed) {
newImage = document.createElementNS("http://www.w3.org/1999/xhtml", "video");
newImage.id = "thepreviewimage";
newImage.src = url;
@ -1035,7 +927,7 @@ function makePreview(row)
document.getElementById("theimagecontainer").collapsed = false;
document.getElementById("brokenimagecontainer").collapsed = true;
}
else if (item instanceof HTMLAudioElement && isProtocolAllowed) {
else if (item.HTMLAudioElement && isProtocolAllowed) {
newImage = new Audio;
newImage.id = "thepreviewimage";
newImage.src = url;
@ -1073,8 +965,6 @@ function makePreview(row)
imageContainer.removeChild(oldImage);
imageContainer.appendChild(newImage);
onImagePreviewShown.forEach(function(func) { func(); });
});
}
@ -1130,69 +1020,9 @@ function getContentTypeFromHeaders(cacheEntryDescriptor)
if (!cacheEntryDescriptor)
return null;
return (/^Content-Type:\s*(.*?)\s*(?:\;|$)/mi
.exec(cacheEntryDescriptor.getMetaDataElement("response-head")))[1];
}
//******** Other Misc Stuff
// Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
// parse a node to extract the contents of the node
function getValueText(node)
{
var valueText = "";
// form input elements don't generally contain information that is useful to our callers, so return nothing
if (node instanceof HTMLInputElement ||
node instanceof HTMLSelectElement ||
node instanceof HTMLTextAreaElement)
return valueText;
// otherwise recurse for each child
var length = node.childNodes.length;
for (var i = 0; i < length; i++) {
var childNode = node.childNodes[i];
var nodeType = childNode.nodeType;
// text nodes are where the goods are
if (nodeType == Node.TEXT_NODE)
valueText += " " + childNode.nodeValue;
// and elements can have more text inside them
else if (nodeType == Node.ELEMENT_NODE) {
// images are special, we want to capture the alt text as if the image weren't there
if (childNode instanceof HTMLImageElement)
valueText += " " + getAltText(childNode);
else
valueText += " " + getValueText(childNode);
}
}
return stripWS(valueText);
}
// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
// traverse the tree in search of an img or area element and grab its alt tag
function getAltText(node)
{
var altText = "";
if (node.alt)
return node.alt;
var length = node.childNodes.length;
for (var i = 0; i < length; i++)
if ((altText = getAltText(node.childNodes[i]) != undefined)) // stupid js warning...
return altText;
return "";
}
// Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
// strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space
function stripWS(text)
{
var middleRE = /\s+/g;
var endRE = /(^\s+)|(\s+$)/g;
text = text.replace(middleRE, " ");
return text.replace(endRE, "");
let headers = cacheEntryDescriptor.getMetaDataElement("response-head");
let type = /^Content-Type:\s*(.*?)\s*(?:\;|$)/mi.exec(headers);
return type && type[1];
}
function setItemValue(id, value)
@ -1281,8 +1111,13 @@ function selectImage()
var tree = document.getElementById("imagetree");
for (var i = 0; i < tree.view.rowCount; i++) {
if (gImageElement == gImageView.data[i][COL_IMAGE_NODE] &&
!gImageView.data[i][COL_IMAGE_BG]) {
// If the image row element is the image selected from the "View Image Info" context menu item.
let image = gImageView.data[i][COL_IMAGE_NODE];
if (!gImageView.data[i][COL_IMAGE_BG] &&
gImageElement.currentSrc == gImageView.data[i][COL_IMAGE_ADDRESS] &&
gImageElement.width == image.width &&
gImageElement.height == image.height &&
gImageElement.imageText == image.imageText) {
tree.view.selection.select(i);
tree.treeBoxObject.ensureRowIsVisible(i);
tree.focus();

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

@ -28,9 +28,8 @@ var permissionObserver = {
}
};
function onLoadPermission()
function onLoadPermission(uri)
{
var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
var permTab = document.getElementById("permTab");
if (SitePermissions.isSupportedURI(uri)) {
gPermURI = uri;

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

@ -9,6 +9,11 @@ XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper",
"resource://gre/modules/LoginHelper.jsm");
var security = {
init: function(uri, windowInfo) {
this.uri = uri;
this.windowInfo = windowInfo;
},
// Display the server certificate (static)
viewCert : function () {
var cert = security._cert;
@ -24,14 +29,10 @@ var security = {
// We don't have separate info for a frame, return null until further notice
// (see bug 138479)
if (gWindow != gWindow.top)
if (!this.windowInfo.isTopWindow)
return null;
var hName = null;
try {
hName = gWindow.location.host;
}
catch (exception) { }
var hostName = this.windowInfo.hostName;
var ui = security._getSecurityUI();
if (!ui)
@ -56,7 +57,7 @@ var security = {
this.mapIssuerOrganization(cert.issuerOrganization) || cert.issuerName;
var retval = {
hostName : hName,
hostName : hostName,
cAName : issuerName,
encryptionAlgorithm : undefined,
encryptionStrength : undefined,
@ -64,8 +65,7 @@ var security = {
isBroken : isBroken,
isMixed : isMixed,
isEV : isEV,
cert : cert,
fullLocation : gWindow.location
cert : cert
};
var version;
@ -95,7 +95,7 @@ var security = {
return retval;
} else {
return {
hostName : hName,
hostName : hostName,
cAName : "",
encryptionAlgorithm : "",
encryptionStrength : 0,
@ -103,8 +103,8 @@ var security = {
isBroken : isBroken,
isMixed : isMixed,
isEV : isEV,
cert : null,
fullLocation : gWindow.location
cert : null
};
}
},
@ -140,13 +140,12 @@ var security = {
getService(Components.interfaces.nsIEffectiveTLDService);
var eTLD;
var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
try {
eTLD = eTLDService.getBaseDomain(uri);
eTLD = eTLDService.getBaseDomain(this.uri);
}
catch (e) {
// getBaseDomain will fail if the host is an IP address or is empty
eTLD = uri.asciiHost;
eTLD = this.uri.asciiHost;
}
if (win) {
@ -168,7 +167,9 @@ var security = {
_cert : null
};
function securityOnLoad() {
function securityOnLoad(uri, windowInfo) {
security.init(uri, windowInfo);
var info = security._getSecurityInfo();
if (!info) {
document.getElementById("securityTab").hidden = true;
@ -226,7 +227,6 @@ function securityOnLoad() {
var yesStr = pageInfoBundle.getString("yes");
var noStr = pageInfoBundle.getString("no");
var uri = BrowserUtils.makeURIFromCPOW(gDocument.documentURIObject);
setText("security-privacy-cookies-value",
hostHasCookies(uri) ? yesStr : noStr);
setText("security-privacy-passwords-value",

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

@ -14,7 +14,7 @@ function test() {
pageInfo.addEventListener("load", function () {
pageInfo.removeEventListener("load", arguments.callee, true);
pageInfo.onImagePreviewShown.push(function () {
pageInfo.onFinished.push(function () {
executeSoon(function () {
var pageInfoImg = pageInfo.document.getElementById("thepreviewimage");

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

@ -15,7 +15,8 @@ let Clipboard = Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard
let HasFindClipboard = Clipboard.supportsFindClipboard();
function addTabWithText(aText, aCallback) {
let newTab = gBrowser.addTab("data:text/html,<h1 id='h1'>" + aText + "</h1>");
let newTab = gBrowser.addTab("data:text/html;charset=utf-8,<h1 id='h1'>" +
aText + "</h1>");
tabs.push(newTab);
gBrowser.selectedTab = newTab;
}
@ -78,6 +79,12 @@ function continueTests1() {
function continueTests2() {
gBrowser.removeEventListener("DOMContentLoaded", continueTests2, true);
waitForCondition(() => !gFindBar.getElement("highlight").checked,
continueTests3,
"Highlight never reset!");
}
function continueTests3() {
ok(!gFindBar.getElement("highlight").checked, "Highlight button reset!");
gFindBar.close();
ok(gFindBar.hidden, "First tab doesn't show find bar!");

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

@ -1,138 +1,140 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
/*
* Test the identity mode UI for a variety of page types
*/
const DUMMY = "browser/browser/base/content/test/general/dummy_page.html";
function loadNewTab(aURL, aCallback) {
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(aURL);
gBrowser.selectedBrowser.addEventListener("load", function() {
if (gBrowser.selectedBrowser.currentURI.spec != aURL)
return;
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
aCallback(gBrowser.selectedTab);
}, true);
function loadNewTab(url) {
return BrowserTestUtils.openNewForegroundTab(gBrowser, url);
}
function getIdentityMode() {
return document.getElementById("identity-box").className;
}
var TESTS = [
function test_webpage() {
// This test is slow on Linux debug e10s
requestLongerTimeout(2);
add_task(function* test_webpage() {
let oldTab = gBrowser.selectedTab;
loadNewTab("http://example.com/" + DUMMY, function(aNewTab) {
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
let newTab = yield loadNewTab("http://example.com/" + DUMMY);
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = aNewTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.removeTab(aNewTab);
gBrowser.removeTab(newTab);
});
runNextTest();
});
},
function test_blank() {
add_task(function* test_blank() {
let oldTab = gBrowser.selectedTab;
loadNewTab("about:blank", function(aNewTab) {
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
let newTab = yield loadNewTab("about:blank");
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = aNewTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.removeTab(aNewTab);
gBrowser.removeTab(newTab);
});
runNextTest();
});
},
function test_chrome() {
add_task(function* test_chrome() {
let oldTab = gBrowser.selectedTab;
// Since users aren't likely to type in full chrome URLs, we won't show
// the positive security indicator on it, but we will show it on about:addons.
loadNewTab("chrome://mozapps/content/extensions/extensions.xul", function(aNewTab) {
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
let newTab = yield loadNewTab("chrome://mozapps/content/extensions/extensions.xul");
is(getIdentityMode(), "fileURI", "Identity should be file");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = aNewTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "fileURI", "Identity should be file");
gBrowser.removeTab(aNewTab);
gBrowser.removeTab(newTab);
});
runNextTest();
});
},
function test_https() {
add_task(function* test_https() {
let oldTab = gBrowser.selectedTab;
loadNewTab("https://example.com/" + DUMMY, function(aNewTab) {
is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
let newTab = yield loadNewTab("https://example.com/" + DUMMY);
is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = aNewTab;
is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "verifiedDomain", "Identity should be verified");
gBrowser.removeTab(aNewTab);
gBrowser.removeTab(newTab);
});
runNextTest();
});
},
function test_addons() {
add_task(function* test_addons() {
let oldTab = gBrowser.selectedTab;
loadNewTab("about:addons", function(aNewTab) {
is(getIdentityMode(), "chromeUI", "Identity should be chrome");
let newTab = yield loadNewTab("about:addons");
is(getIdentityMode(), "chromeUI", "Identity should be chrome");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = aNewTab;
is(getIdentityMode(), "chromeUI", "Identity should be chrome");
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "chromeUI", "Identity should be chrome");
gBrowser.removeTab(aNewTab);
gBrowser.removeTab(newTab);
});
runNextTest();
});
}
];
add_task(function* test_file() {
let oldTab = gBrowser.selectedTab;
let fileURI = getTestFilePath("");
var gTestStart = null;
let newTab = yield loadNewTab(fileURI);
is(getIdentityMode(), "fileURI", "Identity should be file");
function runNextTest() {
if (gTestStart)
info("Test part took " + (Date.now() - gTestStart) + "ms");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
if (TESTS.length == 0) {
finish();
return;
}
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "fileURI", "Identity should be file");
info("Running " + TESTS[0].name);
gTestStart = Date.now();
TESTS.shift()();
};
gBrowser.removeTab(newTab);
});
function test() {
waitForExplicitFinish();
add_task(function test_resource_uri() {
let oldTab = gBrowser.selectedTab;
let dataURI = "resource://gre/modules/Services.jsm"
runNextTest();
}
let newTab = yield loadNewTab(dataURI);
is(getIdentityMode(), "fileURI", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "fileURI", "Identity should be unknown");
gBrowser.removeTab(newTab);
});
add_task(function test_data_uri() {
let oldTab = gBrowser.selectedTab;
let dataURI = "data:text/html,hi"
let newTab = yield loadNewTab(dataURI);
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = oldTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.selectedTab = newTab;
is(getIdentityMode(), "unknownIdentity", "Identity should be unknown");
gBrowser.removeTab(newTab);
});

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

@ -102,7 +102,7 @@ add_task(function* rightLeftKeys() {
// trigger suggestions again and cycle through them by pressing Down until
// nothing is selected again.
state = yield msg("key", "VK_RIGHT");
checkState(state, "xfoo", [], 0);
checkState(state, "xfoo", [], -1);
state = yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
checkState(state, "xfoo", ["xfoofoo", "xfoobar"], -1);
@ -125,20 +125,202 @@ add_task(function* rightLeftKeys() {
yield msg("reset");
});
add_task(function* tabKey() {
yield setUp();
yield msg("key", { key: "x", waitForSuggestions: true });
let state = yield msg("key", "VK_TAB");
checkState(state, "x", ["xfoo", "xbar"], 2);
state = yield msg("key", "VK_TAB");
checkState(state, "x", ["xfoo", "xbar"], 3);
state = yield msg("key", { key: "VK_TAB", modifiers: { shiftKey: true }});
checkState(state, "x", ["xfoo", "xbar"], 2);
state = yield msg("key", { key: "VK_TAB", modifiers: { shiftKey: true }});
checkState(state, "x", [], -1);
yield setUp();
yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
for (let i = 0; i < 3; ++i) {
state = yield msg("key", "VK_TAB");
}
checkState(state, "x", [], -1);
yield setUp();
yield msg("key", { key: "VK_DOWN", waitForSuggestions: true });
state = yield msg("key", "VK_DOWN");
checkState(state, "xfoo", ["xfoo", "xbar"], 0);
state = yield msg("key", "VK_TAB");
checkState(state, "xfoo", ["xfoo", "xbar"], 0, 0);
state = yield msg("key", "VK_TAB");
checkState(state, "xfoo", ["xfoo", "xbar"], 0, 1);
state = yield msg("key", "VK_DOWN");
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
state = yield msg("key", "VK_DOWN");
checkState(state, "x", ["xfoo", "xbar"], 2);
state = yield msg("key", "VK_UP");
checkState(state, "xbar", ["xfoo", "xbar"], 1);
state = yield msg("key", "VK_TAB");
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
state = yield msg("key", "VK_TAB");
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
state = yield msg("key", "VK_TAB");
checkState(state, "xbar", [], -1);
yield msg("reset");
});
add_task(function* cycleSuggestions() {
yield setUp();
yield msg("key", { key: "x", waitForSuggestions: true });
let cycle = Task.async(function* (aSelectedButtonIndex) {
let modifiers = {
shiftKey: true,
accelKey: true,
};
let state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex);
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, aSelectedButtonIndex);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "xfoo", ["xfoo", "xbar"], 0, aSelectedButtonIndex);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "x", ["xfoo", "xbar"], -1, aSelectedButtonIndex);
});
yield cycle();
// Repeat with a one-off selected.
let state = yield msg("key", "VK_TAB");
checkState(state, "x", ["xfoo", "xbar"], 2);
yield cycle(0);
// Repeat with the settings button selected.
state = yield msg("key", "VK_TAB");
checkState(state, "x", ["xfoo", "xbar"], 3);
yield cycle(1);
yield msg("reset");
});
add_task(function* cycleOneOffs() {
yield setUp();
yield msg("key", { key: "x", waitForSuggestions: true });
yield msg("addDuplicateOneOff");
let state = yield msg("key", "VK_DOWN");
state = yield msg("key", "VK_DOWN");
checkState(state, "xbar", ["xfoo", "xbar"], 1);
let modifiers = {
altKey: true,
};
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1);
// If the settings button is selected, pressing alt+up/down should select the
// last/first one-off respectively (and deselect the settings button).
yield msg("key", "VK_TAB");
yield msg("key", "VK_TAB");
state = yield msg("key", "VK_TAB"); // Settings button selected.
checkState(state, "xbar", ["xfoo", "xbar"], 1, 2);
state = yield msg("key", { key: "VK_UP", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, 1);
state = yield msg("key", "VK_TAB");
checkState(state, "xbar", ["xfoo", "xbar"], 1, 2);
state = yield msg("key", { key: "VK_DOWN", modifiers: modifiers });
checkState(state, "xbar", ["xfoo", "xbar"], 1, 0);
yield msg("removeLastOneOff");
yield msg("reset");
});
add_task(function* mouse() {
yield setUp();
let state = yield msg("key", { key: "x", waitForSuggestions: true });
checkState(state, "x", ["xfoo", "xbar"], -1);
for (let i = 0; i < 4; ++i) {
state = yield msg("mousemove", i);
checkState(state, "x", ["xfoo", "xbar"], i);
}
state = yield msg("mousemove", 0);
checkState(state, "x", ["xfoo", "xbar"], 0);
state = yield msg("mousemove", 1);
checkState(state, "x", ["xfoo", "xbar"], 1);
state = yield msg("mousemove", 2);
checkState(state, "x", ["xfoo", "xbar"], 1, 0);
state = yield msg("mousemove", 3);
checkState(state, "x", ["xfoo", "xbar"], 1, 1);
state = yield msg("mousemove", -1);
checkState(state, "x", ["xfoo", "xbar"], 1);
yield msg("reset");
yield setUp();
state = yield msg("key", { key: "x", waitForSuggestions: true });
checkState(state, "x", ["xfoo", "xbar"], -1);
state = yield msg("mousemove", 0);
checkState(state, "x", ["xfoo", "xbar"], 0);
state = yield msg("mousemove", 2);
checkState(state, "x", ["xfoo", "xbar"], 0, 0);
state = yield msg("mousemove", -1);
checkState(state, "x", ["xfoo", "xbar"], 0);
yield msg("reset");
});
@ -197,6 +379,33 @@ add_task(function* formHistory() {
yield msg("reset");
});
add_task(function* cycleEngines() {
yield setUp();
yield msg("key", "VK_DOWN");
function promiseEngineChange(newEngineName) {
let deferred = Promise.defer();
Services.obs.addObserver(function resolver(subj, topic, data) {
if (data != "engine-current") {
return;
}
is(subj.name, newEngineName, "Engine cycled correctly");
Services.obs.removeObserver(resolver, "browser-search-engine-modified");
deferred.resolve();
}, "browser-search-engine-modified", false);
}
let p = promiseEngineChange(TEST_ENGINE_PREFIX + " " + TEST_ENGINE_2_BASENAME);
yield msg("key", { key: "VK_DOWN", modifiers: { accelKey: true }});
yield p;
p = promiseEngineChange(TEST_ENGINE_PREFIX + " " + TEST_ENGINE_BASENAME);
yield msg("key", { key: "VK_UP", modifiers: { accelKey: true }});
yield p;
yield msg("reset");
});
add_task(function* search() {
yield setUp();
@ -297,6 +506,42 @@ add_task(function* search() {
yield promiseTab();
yield setUp();
// Test selecting a suggestion, then clicking a one-off without deselecting the
// suggestion.
yield msg("key", { key: "x", waitForSuggestions: true });
p = msg("waitForSearch");
yield msg("mousemove", 1);
yield msg("mousemove", 3);
yield msg("click", { eltIdx: 3, modifiers: modifiers });
mesg = yield p;
eventData.searchString = "xfoo"
eventData.selection = {
index: 1,
kind: "mouse",
};
SimpleTest.isDeeply(eventData, mesg, "Search event data");
yield promiseTab();
yield setUp();
// Same as above, but with the keyboard.
delete modifiers.button;
yield msg("key", { key: "x", waitForSuggestions: true });
p = msg("waitForSearch");
yield msg("key", "VK_DOWN");
yield msg("key", "VK_DOWN");
yield msg("key", "VK_TAB");
yield msg("key", { key: "VK_RETURN", modifiers: modifiers });
mesg = yield p;
eventData.selection = {
index: 1,
kind: "key",
};
SimpleTest.isDeeply(eventData, mesg, "Search event data");
yield promiseTab();
yield setUp();
// Test searching when using IME composition.
let state = yield msg("startComposition", { data: "" });
checkState(state, "", [], -1);
@ -308,8 +553,10 @@ add_task(function* search() {
p = msg("waitForSearch");
yield msg("key", { key: "VK_RETURN", modifiers: modifiers });
mesg = yield p;
eventData.searchString = "x"
eventData.originalEvent = modifiers;
eventData.engineName = TEST_ENGINE_PREFIX + " " + TEST_ENGINE_BASENAME;
delete eventData.selection;
SimpleTest.isDeeply(eventData, mesg, "Search event data");
yield promiseTab();
@ -428,7 +675,7 @@ function msg(type, data=null) {
}
function checkState(actualState, expectedInputVal, expectedSuggestions,
expectedSelectedIdx) {
expectedSelectedIdx, expectedSelectedButtonIdx) {
expectedSuggestions = expectedSuggestions.map(sugg => {
return typeof(sugg) == "object" ? sugg : {
str: sugg,
@ -436,6 +683,10 @@ function checkState(actualState, expectedInputVal, expectedSuggestions,
};
});
if (expectedSelectedIdx == -1 && expectedSelectedButtonIdx != undefined) {
expectedSelectedIdx = expectedSuggestions.length + expectedSelectedButtonIdx;
}
let expectedState = {
selectedIndex: expectedSelectedIdx,
numSuggestions: expectedSuggestions.length,
@ -448,6 +699,15 @@ function checkState(actualState, expectedInputVal, expectedSuggestions,
inputValue: expectedInputVal,
ariaExpanded: expectedSuggestions.length == 0 ? "false" : "true",
};
if (expectedSelectedButtonIdx != undefined) {
expectedState.selectedButtonIndex = expectedSelectedButtonIdx;
}
else if (expectedSelectedIdx < expectedSuggestions.length) {
expectedState.selectedButtonIndex = -1;
}
else {
expectedState.selectedButtonIndex = expectedSelectedIdx - expectedSuggestions.length;
}
SimpleTest.isDeeply(actualState, expectedState, "State");
}

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

@ -15,7 +15,7 @@ function test() {
function observer(win, topic, data) {
Services.obs.removeObserver(observer, "page-info-dialog-loaded");
handlePageInfo();
pageInfo.onFinished.push(handlePageInfo);
}
function handlePageInfo() {

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

@ -22,6 +22,11 @@ function hidden(sel) {
return display === "none";
}
function identityPopupState() {
let win = browser.ownerGlobal;
return win.document.getElementById("identity-popup").state;
}
function clickButton(sel) {
let win = browser.ownerGlobal;
let el = win.document.querySelector(sel);
@ -85,6 +90,8 @@ add_task(function* testExceptionAddition() {
info("Disable TP for the page (which reloads the page)");
let tabReloadPromise = promiseTabLoadEvent(tab);
clickButton("#tracking-action-unblock");
is(identityPopupState(), "closed", "foobar");
yield tabReloadPromise;
testTrackingPageUnblocked();
@ -115,6 +122,8 @@ add_task(function* testExceptionPersistence() {
info("Disable TP for the page (which reloads the page)");
let tabReloadPromise = promiseTabLoadEvent(tab);
clickButton("#tracking-action-unblock");
is(identityPopupState(), "closed", "foobar");
yield tabReloadPromise;
testTrackingPageUnblocked();

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

@ -124,11 +124,27 @@ let messageHandlers = {
ack("addInputValueToFormHistory");
},
addDuplicateOneOff: function () {
let btn = gController._oneOffButtons[gController._oneOffButtons.length - 1];
let newBtn = btn.cloneNode(true);
btn.parentNode.appendChild(newBtn);
gController._oneOffButtons.push(newBtn);
ack("addDuplicateOneOff");
},
removeLastOneOff: function () {
gController._oneOffButtons.pop().remove();
ack("removeLastOneOff");
},
reset: function () {
// Reset both the input and suggestions by select all + delete.
// Reset both the input and suggestions by select all + delete. If there was
// no text entered, this won't have any effect, so also escape to ensure the
// suggestions table is closed.
gController.input.focus();
content.synthesizeKey("a", { accelKey: true });
content.synthesizeKey("VK_DELETE", {});
content.synthesizeKey("VK_ESCAPE", {});
ack("reset");
},
};
@ -165,6 +181,7 @@ function waitForContentSearchEvent(messageType, cb) {
function currentState() {
let state = {
selectedIndex: gController.selectedIndex,
selectedButtonIndex: gController.selectedButtonIndex,
numSuggestions: gController._table.hidden ? 0 : gController.numSuggestions,
suggestionAtIndex: [],
isFormHistorySuggestionAtIndex: [],

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

@ -34,7 +34,7 @@ function doOnOpenPageInfo(continuation) {
function pageInfoObserve(win, topic, data) {
Services.obs.removeObserver(pageInfoObserve, "page-info-dialog-loaded");
executeSoon(gNextTest);
gPageInfo.onFinished.push(() => executeSoon(gNextTest));
}
function finishTest() {

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

@ -27,6 +27,8 @@
value="&identity.connectionSecure;"/>
<label class="identity-popup-connection-not-secure identity-popup-text"
value="&identity.connectionNotSecure;"/>
<label class="identity-popup-connection-file-uri identity-popup-text"
value="&identity.connectionFile;"/>
<label class="identity-popup-connection-internal identity-popup-text"
value="&identity.connectionInternal;"/>
</vbox>
@ -95,6 +97,8 @@
value="&identity.connectionSecure;"/>
<label class="identity-popup-connection-not-secure identity-popup-text"
value="&identity.connectionNotSecure;"/>
<label class="identity-popup-connection-file-uri identity-popup-text"
value="&identity.connectionFile;"/>
<label class="identity-popup-connection-internal identity-popup-text"
value="&identity.connectionInternal;"/>
</vbox>

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

@ -121,12 +121,8 @@ function configureFxAccountIdentity() {
storageManager.initialize(user);
return new AccountState(storageManager);
},
getCertificate(data, keyPair, mustBeValidUntil) {
this.cert = {
validUntil: this.now() + 10000,
cert: "certificate",
};
return Promise.resolve(this.cert.cert);
_getAssertion(audience) {
return Promise.resolve("assertion");
},
getCertificateSigned() {
return Promise.resolve();

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

@ -182,6 +182,10 @@ this.SessionStore = {
return SessionStoreInternal.canRestoreLastSession;
},
get crashedTabCount() {
return SessionStoreInternal._crashedBrowsersCount;
},
set canRestoreLastSession(val) {
SessionStoreInternal.canRestoreLastSession = val;
},
@ -302,6 +306,10 @@ this.SessionStore = {
return SessionStoreInternal.reviveCrashedTab(aTab);
},
reviveAllCrashedTabs() {
return SessionStoreInternal.reviveAllCrashedTabs();
},
navigateAndRestore(tab, loadArguments, historyIndex) {
return SessionStoreInternal.navigateAndRestore(tab, loadArguments, historyIndex);
}
@ -331,6 +339,9 @@ let SessionStoreInternal = {
// they get restored).
_crashedBrowsers: new WeakSet(),
// The number of crashed browsers.
_crashedBrowsersCount: 0,
// A map (xul:browser -> nsIFrameLoader) that maps a browser to the last
// associated frameLoader we heard about.
_lastKnownFrameLoader: new WeakMap(),
@ -1408,6 +1419,10 @@ let SessionStoreInternal = {
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
}
if (this._crashedBrowsers.has(browser.permanentKey)) {
this._crashedBrowsersCount++;
}
},
/**
@ -1437,6 +1452,10 @@ let SessionStoreInternal = {
if (!aNoNotification) {
this.saveStateDelayed(aWindow);
}
if (this._crashedBrowsers.has(browser.permanentKey)) {
this._crashedBrowsersCount--;
}
},
/**
@ -1616,6 +1635,7 @@ let SessionStoreInternal = {
*/
onBrowserCrashed: function(aWindow, aBrowser) {
this._crashedBrowsers.add(aBrowser.permanentKey);
this._crashedBrowsersCount++;
// If we never got around to restoring this tab, clear its state so
// that we don't try restoring if the user switches to it before
// reviving the crashed browser. This is throwing away the information
@ -2171,6 +2191,23 @@ let SessionStoreInternal = {
let data = TabState.collect(aTab);
this.restoreTab(aTab, data);
this._crashedBrowsersCount--;
},
/**
* Revive all crashed tabs and reset the crashed tabs count to 0.
*/
reviveAllCrashedTabs() {
let windowsEnum = Services.wm.getEnumerator("navigator:browser");
while (windowsEnum.hasMoreElements()) {
let window = windowsEnum.getNext();
for (let tab of window.gBrowser.tabs) {
this.reviveCrashedTab(tab);
}
}
this._crashedBrowsersCount = 0;
},
/**

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

@ -341,3 +341,49 @@ add_task(function test_close_tab_after_crash() {
is(gBrowser.tabs.length, 1, "Should have closed the tab");
});
/**
* Checks that "restore all" button is only shown if more than one tab
* has crashed.
*/
add_task(function* test_hide_restore_all_button() {
let newTab = gBrowser.addTab();
gBrowser.selectedTab = newTab;
let browser = newTab.linkedBrowser;
ok(browser.isRemoteBrowser, "Should be a remote browser");
yield promiseBrowserLoaded(browser);
browser.loadURI(PAGE_1);
yield promiseBrowserLoaded(browser);
yield TabStateFlusher.flush(browser);
// Crash the tab
yield crashBrowser(browser);
let doc = browser.contentDocument;
let restoreAllButton = doc.getElementById("restoreAll");
let restoreOneButton = doc.getElementById("restoreTab");
is(restoreAllButton.getAttribute("hidden"), "true", "Restore All button should be hidden");
ok(restoreOneButton.classList.contains("primary"), "Restore Tab button should have the primary class");
let newTab2 = gBrowser.addTab();
gBrowser.selectedTab = newTab;
browser.loadURI(PAGE_2);
yield promiseBrowserLoaded(browser);
// Crash the tab
yield crashBrowser(browser);
doc = browser.contentDocument;
restoreAllButton = doc.getElementById("restoreAll");
restoreOneButton = doc.getElementById("restoreTab");
ok(!restoreAllButton.hasAttribute("hidden"), "Restore All button should not be hidden");
ok(!(restoreOneButton.classList.contains("primary")), "Restore Tab button should not have the primary class");
gBrowser.removeTab(newTab);
gBrowser.removeTab(newTab2);
});

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

@ -18,7 +18,7 @@ function* performTest() {
let [host, , doc] = yield createHost("bottom", "data:text/html," +
"<h1>browser_outputParser.js</h1><div></div>");
let parser = new OutputParser();
let parser = new OutputParser(doc);
testParseCssProperty(doc, parser);
testParseCssVar(doc, parser);

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

@ -134,7 +134,7 @@
tooltiptext="&visibilityToggle.tooltip;"
accesskey="&saveButton.accesskey;"></xul:label>
<hgroup class="stylesheet-info">
<h1><a class="stylesheet-name" tabindex="0"><xul:label crop="start"/></a></h1>
<h1><a class="stylesheet-name" tabindex="0"><xul:label crop="center"/></a></h1>
<div class="stylesheet-more">
<h3 class="stylesheet-title"></h3>
<h3 class="stylesheet-linked-file"></h3>

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

@ -137,7 +137,7 @@ function CssComputedView(inspector, document, pageStyle) {
this.propertyViews = [];
this._outputParser = new OutputParser();
this._outputParser = new OutputParser(document);
let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"]
.getService(Ci.nsIXULChromeRegistry);

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

@ -334,7 +334,11 @@ ElementStyle.prototype = {
let textProps = [];
for (let rule of this.rules) {
if (rule.pseudoElement == pseudo && !rule.keyframes) {
textProps = textProps.concat(rule.textProps.slice(0).reverse());
for (let textProp of rule.textProps.slice(0).reverse()) {
if (textProp.enabled) {
textProps.push(textProp);
}
}
}
}
@ -1161,7 +1165,7 @@ function CssRuleView(inspector, document, aStore, aPageStyle) {
this.store = aStore || {};
this.pageStyle = aPageStyle;
this._outputParser = new OutputParser();
this._outputParser = new OutputParser(document);
this._onKeypress = this._onKeypress.bind(this);
this._onAddRule = this._onAddRule.bind(this);
@ -3163,7 +3167,8 @@ TextPropertyEditor.prototype = {
},
_onStartEditing: function() {
this._previewValue(this.prop.value);
this.element.classList.remove("ruleview-overridden");
this.enable.style.visibility = "hidden";
},
/**
@ -3468,9 +3473,6 @@ TextPropertyEditor.prototype = {
return;
}
this.element.classList.remove("ruleview-overridden");
this.enable.style.visibility = "hidden";
let val = parseSingleValue(aValue);
this.ruleEditor.rule.previewPropertyValue(this.prop, val.value,
val.priority);

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

@ -112,6 +112,11 @@ skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
[browser_ruleview_keyframes-rule_01.js]
[browser_ruleview_keyframes-rule_02.js]
[browser_ruleview_livepreview.js]
[browser_ruleview_mark_overridden_01.js]
[browser_ruleview_mark_overridden_02.js]
[browser_ruleview_mark_overridden_03.js]
[browser_ruleview_mark_overridden_04.js]
[browser_ruleview_mark_overridden_05.js]
[browser_ruleview_mathml-element.js]
[browser_ruleview_media-queries.js]
[browser_ruleview_multiple-properties-duplicates.js]
@ -122,7 +127,6 @@ skip-if = (os == "win" && debug) || e10s # bug 963492: win. bug 1040653: e10s.
[browser_ruleview_multiple_properties_02.js]
[browser_ruleview_original-source-link.js]
[browser_ruleview_cycle-color.js]
[browser_ruleview_override.js]
[browser_ruleview_pseudo-element_01.js]
[browser_ruleview_pseudo-element_02.js]
skip-if = e10s # Bug 1090340

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

@ -4,8 +4,7 @@
"use strict";
// Tests that a disabled property is previewed when the property name or value
// editor is focused and the property remains disabled when the escaping out of
// Tests that a disabled property remains disabled when the escaping out of
// the property editor.
let TEST_URI = [
@ -39,20 +38,19 @@ function* testDisableProperty(inspector, view) {
});
is(newValue, "", "background-color should have been unset.");
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
yield testEditDisableProperty(view, ruleEditor, propEditor,
propEditor.nameSpan, "VK_ESCAPE");
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
yield testEditDisableProperty(view, ruleEditor, propEditor,
propEditor.valueSpan, "VK_ESCAPE");
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
yield testEditDisableProperty(view, ruleEditor, propEditor,
propEditor.valueSpan, "VK_TAB");
yield testPreviewDisableProperty(view, ruleEditor, propEditor,
yield testEditDisableProperty(view, ruleEditor, propEditor,
propEditor.valueSpan, "VK_RETURN");
}
function* testPreviewDisableProperty(view, ruleEditor, propEditor,
function* testEditDisableProperty(view, ruleEditor, propEditor,
editableField, commitKey) {
let editor = yield focusEditableField(view, editableField);
yield ruleEditor.rule._applyingModifications;
ok(!propEditor.element.classList.contains("ruleview-overridden"),
"property is not overridden.");
@ -64,7 +62,7 @@ function* testPreviewDisableProperty(view, ruleEditor, propEditor,
ruleIndex: 0,
name: "background-color"
});
is(newValue, "blue", "background-color should have been previewed.");
is(newValue, "", "background-color should remain unset.");
let onBlur = once(editor.input, "blur");
EventUtils.synthesizeKey(commitKey, {}, view.styleWindow);
@ -84,5 +82,5 @@ function* testPreviewDisableProperty(view, ruleEditor, propEditor,
ruleIndex: 0,
name: "background-color"
});
is(newValue, "", "background-color should have been unset.");
is(newValue, "", "background-color should remain unset.");
}

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

@ -0,0 +1,64 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the rule view marks overridden rules correctly based on the
// specificity of the rule
let TEST_URI = [
"<style type='text/css'>",
"#testid {",
" background-color: blue;",
"}",
".testclass {",
" background-color: green;",
"}",
"</style>",
"<div id='testid' class='testclass'>Styled Node</div>"
].join("\n");
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
yield testMarkOverridden(inspector, view);
});
function* testMarkOverridden(inspector, view) {
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
is(idProp.name, "background-color",
"First ID property should be background-color");
is(idProp.value, "blue", "First ID property value should be blue");
ok(!idProp.overridden, "ID prop should not be overridden.");
ok(!idProp.editor.element.classList.contains("ruleview-overridden"),
"ID property editor should not have ruleview-overridden class");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
is(classProp.name, "background-color",
"First class prop should be background-color");
is(classProp.value, "green", "First class property value should be green");
ok(classProp.overridden, "Class property should be overridden.");
ok(classProp.editor.element.classList.contains("ruleview-overridden"),
"Class property editor should have ruleview-overridden class");
// Override background-color by changing the element style.
let elementRule = elementStyle.rules[0];
elementRule.createProperty("background-color", "purple", "");
yield elementRule._applyingModifications;
let elementProp = elementRule.textProps[0];
ok(!elementProp.overridden,
"Element style property should not be overridden");
ok(idProp.overridden, "ID property should be overridden");
ok(idProp.editor.element.classList.contains("ruleview-overridden"),
"ID property editor should have ruleview-overridden class");
ok(classProp.overridden, "Class property should be overridden");
ok(classProp.editor.element.classList.contains("ruleview-overridden"),
"Class property editor should have ruleview-overridden class");
}

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

@ -0,0 +1,45 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the rule view marks overridden rules correctly for short hand
// properties and the computed list properties
let TEST_URI = [
"<style type='text/css'>",
"#testid {",
" margin-left: 1px;",
"}",
".testclass {",
" margin: 2px;",
"}",
"</style>",
"<div id='testid' class='testclass'>Styled Node</div>"
].join("\n");
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
yield testMarkOverridden(inspector, view);
});
function* testMarkOverridden(inspector, view) {
let elementStyle = view._elementStyle;
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden,
"Class prop shouldn't be overridden, some props are still being used.");
for (let computed of classProp.computed) {
if (computed.name.indexOf("margin-left") == 0) {
ok(computed.overridden, "margin-left props should be overridden.");
} else {
ok(!computed.overridden,
"Non-margin-left props should not be overridden.");
}
}
}

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

@ -0,0 +1,48 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the rule view marks overridden rules correctly based on the
// priority for the rule
let TEST_URI = [
"<style type='text/css'>",
"#testid {",
" background-color: blue;",
"}",
".testclass {",
" background-color: green !important;",
"}",
"</style>",
"<div id='testid' class='testclass'>Styled Node</div>"
].join("\n");
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
yield testMarkOverridden(inspector, view);
});
function* testMarkOverridden(inspector, view) {
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
ok(idProp.overridden, "Not-important rule should be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Important rule should not be overridden.");
let elementRule = elementStyle.rules[0];
let elementProp = elementRule.createProperty("background-color", "purple",
"important");
yield elementRule._applyingModifications;
ok(!elementProp.overridden, "New important prop should not be overriden.");
ok(idProp.overridden, "ID property should be overridden.");
ok(classProp.overridden, "Class property should be overridden.");
}

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

@ -0,0 +1,42 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the rule view marks overridden rules correctly if a property gets
// disabled
let TEST_URI = [
"<style type='text/css'>",
"#testid {",
" background-color: blue;",
"}",
".testclass {",
" background-color: green;",
"}",
"</style>",
"<div id='testid' class='testclass'>Styled Node</div>"
].join("\n");
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
yield testMarkOverridden(inspector, view);
});
function* testMarkOverridden(inspector, view) {
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
idProp.setEnabled(false);
yield idRule._applyingModifications;
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden,
"Class prop should not be overridden after id prop was disabled.");
}

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

@ -0,0 +1,36 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Tests that the rule view marks overridden rules correctly based on the
// order of the property
let TEST_URI = [
"<style type='text/css'>",
"#testid {",
" background-color: green;",
"}",
"</style>",
"<div id='testid' class='testclass'>Styled Node</div>"
].join("\n");
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let {inspector, view} = yield openRuleView();
yield selectNode("#testid", inspector);
yield testMarkOverridden(inspector, view);
});
function* testMarkOverridden(inspector, view) {
let ruleEditor = getRuleViewRuleEditor(view, 1);
yield createNewRuleViewProperty(ruleEditor, "background-color: red;");
let firstProp = ruleEditor.rule.textProps[0];
let secondProp = ruleEditor.rule.textProps[1];
ok(firstProp.overridden, "First property should be overridden.");
ok(!secondProp.overridden, "Second property should not be overridden.");
}

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

@ -36,9 +36,9 @@ function waitRuleViewChanged(view, n) {
}
function* testCreateNewMultiUnfinished(inspector, ruleEditor, view) {
let onMutation = inspector.once("markupmutation");
// There is 6 rule-view updates, one for the rule view creation,
// one for each new property and one last for throttle update.
let onRuleViewChanged = waitRuleViewChanged(view, 6);
// There is 5 rule-view updates, one for the rule view creation,
// one for each new property
let onRuleViewChanged = waitRuleViewChanged(view, 5);
yield createNewRuleViewProperty(ruleEditor,
"color:blue;background : orange ; text-align:center; border-color: ");
yield onMutation;

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

@ -1,151 +0,0 @@
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the display of overridden declarations in the rule-view
add_task(function*() {
yield addTab("data:text/html;charset=utf-8,browser_ruleview_override.js");
let {toolbox, inspector, view} = yield openRuleView();
yield simpleOverride(inspector, view);
yield partialOverride(inspector, view);
yield importantOverride(inspector, view);
yield disableOverride(inspector, view);
});
function* createTestContent(inspector, style) {
let onMutated = inspector.once("markupmutation");
let styleNode = addStyle(content.document, style);
content.document.body.innerHTML = '<div id="testid" class="testclass">Styled Node</div>';
yield onMutated;
yield selectNode("#testid", inspector);
return styleNode;
}
function* removeTestContent(inspector, node) {
let onMutated = inspector.once("markupmutation");
node.remove();
yield onMutated;
}
function* simpleOverride(inspector, view) {
let styleNode = yield createTestContent(inspector, '' +
'#testid {' +
' background-color: blue;' +
'} ' +
'.testclass {' +
' background-color: green;' +
'}');
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
is(idProp.name, "background-color", "First ID prop should be background-color");
ok(!idProp.overridden, "ID prop should not be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
is(classProp.name, "background-color", "First class prop should be background-color");
ok(classProp.overridden, "Class property should be overridden.");
// Override background-color by changing the element style.
let elementRule = elementStyle.rules[0];
elementRule.createProperty("background-color", "purple", "");
yield elementRule._applyingModifications;
let elementProp = elementRule.textProps[0];
is(classProp.name, "background-color", "First element prop should now be background-color");
ok(!elementProp.overridden, "Element style property should not be overridden");
ok(idProp.overridden, "ID property should be overridden");
ok(classProp.overridden, "Class property should be overridden");
yield removeTestContent(inspector, styleNode);
}
function* partialOverride(inspector, view) {
let styleNode = yield createTestContent(inspector, '' +
// Margin shorthand property...
'.testclass {' +
' margin: 2px;' +
'}' +
// ... will be partially overridden.
'#testid {' +
' margin-left: 1px;' +
'}');
let elementStyle = view._elementStyle;
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden,
"Class prop shouldn't be overridden, some props are still being used.");
for (let computed of classProp.computed) {
if (computed.name.indexOf("margin-left") == 0) {
ok(computed.overridden, "margin-left props should be overridden.");
} else {
ok(!computed.overridden, "Non-margin-left props should not be overridden.");
}
}
yield removeTestContent(inspector, styleNode);
}
function* importantOverride(inspector, view) {
let styleNode = yield createTestContent(inspector, '' +
// Margin shorthand property...
'.testclass {' +
' background-color: green !important;' +
'}' +
// ... will be partially overridden.
'#testid {' +
' background-color: blue;' +
'}');
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
ok(idProp.overridden, "Not-important rule should be overridden.");
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Important rule should not be overridden.");
yield removeTestContent(inspector, styleNode);
let elementRule = elementStyle.rules[0];
let elementProp = elementRule.createProperty("background-color", "purple", "important");
yield elementRule._applyingModifications;
ok(classProp.overridden, "New important prop should override class property.");
ok(!elementProp.overridden, "New important prop should not be overriden.");
}
function* disableOverride(inspector, view) {
let styleNode = yield createTestContent(inspector, '' +
'#testid {' +
' background-color: blue;' +
'}' +
'.testclass {' +
' background-color: green;' +
'}');
let elementStyle = view._elementStyle;
let idRule = elementStyle.rules[1];
let idProp = idRule.textProps[0];
idProp.setEnabled(false);
yield idRule._applyingModifications;
let classRule = elementStyle.rules[2];
let classProp = classRule.textProps[0];
ok(!classProp.overridden, "Class prop should not be overridden after id prop was disabled.");
yield removeTestContent(inspector, styleNode);
}

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

@ -310,7 +310,7 @@ function test() {
}
];
let parser = new OutputParser();
let parser = new OutputParser(document);
for (let i = 0; i < testData.length; i ++) {
let data = testData[i];
info("Output-parser test data " + i + ". {" + data.name + " : " + data.value + ";}");

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

@ -414,29 +414,15 @@ function* waitForComputedStyleProperty(selector, pseudo, name, expected) {
* @return a promise that resolves to the inplace-editor element when ready
*/
let focusEditableField = Task.async(function*(ruleView, editable, xOffset=1, yOffset=1, options={}) {
// Focusing the name or value input is going to fire a preview and update the rule view
let expectRuleViewUpdate =
editable.classList.contains("ruleview-propertyname") ||
editable.classList.contains("ruleview-propertyvalue");
let onRuleViewChanged;
if (expectRuleViewUpdate) {
onRuleViewChanged = ruleView.once("ruleview-changed");
}
let onFocus = once(editable.parentNode, "focus", true);
info("Clicking on editable field to turn to edit mode");
EventUtils.synthesizeMouse(editable, xOffset, yOffset, options,
editable.ownerDocument.defaultView);
let event = yield onFocus;
yield onFocus;
info("Editable field gained focus, returning the input field now");
let onEdit = inplaceEditor(editable.ownerDocument.activeElement);
if (expectRuleViewUpdate) {
info("Waiting for rule view update");
yield onRuleViewChanged;
}
return onEdit;
});
@ -801,11 +787,11 @@ function getRuleViewRuleEditor(view, childrenIndex, nodeIndex) {
let focusNewRuleViewProperty = Task.async(function*(ruleEditor) {
info("Clicking on a close ruleEditor brace to start editing a new property");
ruleEditor.closeBrace.scrollIntoView();
let editor = yield focusEditableField(ruleEditor.ruleView, ruleEditor.closeBrace);
let editor = yield focusEditableField(ruleEditor.ruleView,
ruleEditor.closeBrace);
is(inplaceEditor(ruleEditor.newPropSpan), editor, "Focused editor is the new property editor.");
is(ruleEditor.rule.textProps.length, 0, "Starting with one new text property.");
is(ruleEditor.propertyList.children.length, 1, "Starting with two property editors.");
is(inplaceEditor(ruleEditor.newPropSpan), editor,
"Focused editor is the new property editor.");
return editor;
});

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

@ -680,6 +680,7 @@ you can use these alternative items. Otherwise, their values should be empty. -
<!ENTITY identity.connectionSecure "Secure Connection">
<!ENTITY identity.connectionNotSecure "Connection is Not Secure">
<!ENTITY identity.connectionFile "This page is stored on your computer.">
<!ENTITY identity.connectionVerified "&brandShortName; verified that you are securely connected to this site, run by:">
<!ENTITY identity.connectionInternal "This is a secure &brandShortName; page.">

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

@ -88,7 +88,12 @@ this.TabCrashReporter = {
}
},
onAboutTabCrashedLoad: function (aBrowser) {
onAboutTabCrashedLoad: function (aBrowser, aParams) {
// If there was only one tab open that crashed, do not show the "restore all tabs" button
if (aParams.crashedTabCount == 1) {
this.hideRestoreAllButton(aBrowser);
}
if (!this.childMap)
return;
@ -97,6 +102,11 @@ this.TabCrashReporter = {
return;
aBrowser.contentDocument.documentElement.classList.add("crashDumpAvailable");
},
hideRestoreAllButton: function (aBrowser) {
aBrowser.contentDocument.getElementById("restoreAll").setAttribute("hidden", true);
aBrowser.contentDocument.getElementById("restoreTab").setAttribute("class", "primary");
}
}

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

@ -146,6 +146,7 @@ browser.jar:
skin/classic/browser/loop/toolbar-inverted@2x.png (loop/toolbar-inverted@2x.png)
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
skin/classic/browser/controlcenter/arrow-subview.svg (../shared/controlcenter/arrow-subview.svg)
skin/classic/browser/controlcenter/arrow-subview-back.svg (../shared/controlcenter/arrow-subview-back.svg)
skin/classic/browser/controlcenter/conn-not-secure.svg (../shared/controlcenter/conn-not-secure.svg)
skin/classic/browser/controlcenter/conn-secure.svg (../shared/controlcenter/conn-secure.svg)
skin/classic/browser/controlcenter/conn-degraded.svg (../shared/controlcenter/conn-degraded.svg)

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

@ -190,6 +190,7 @@ browser.jar:
skin/classic/browser/yosemite/loop/toolbar@2x.png (loop/toolbar-yosemite@2x.png)
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
skin/classic/browser/controlcenter/arrow-subview.svg (../shared/controlcenter/arrow-subview.svg)
skin/classic/browser/controlcenter/arrow-subview-back.svg (../shared/controlcenter/arrow-subview-back.svg)
skin/classic/browser/controlcenter/conn-not-secure.svg (../shared/controlcenter/conn-not-secure.svg)
skin/classic/browser/controlcenter/conn-secure.svg (../shared/controlcenter/conn-secure.svg)
skin/classic/browser/controlcenter/conn-degraded.svg (../shared/controlcenter/conn-degraded.svg)

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

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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">
<polygon fill="#fff" points="12,3.5 10.5,2 4.625,8 10.5,14 12,12.5 7.625,8" />
</svg>

После

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

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

@ -12,11 +12,16 @@
/* Show the "Connection is not secure" labels only for non-secure sites. */
#identity-popup-security-content:not(.unknownIdentity) > .identity-popup-connection-not-secure,
#identity-popup-securityView:not(.unknownIdentity) > #identity-popup-securityView-header > .identity-popup-connection-not-secure,
/* Show "This page is stored on your computer" only for file URLs. */
#identity-popup-security-content:not(.fileURI) > .identity-popup-connection-file-uri,
#identity-popup-securityView:not(.fileURI) > #identity-popup-securityView-header > .identity-popup-connection-file-uri,
/* Show "This is a secure internal page" only for whitelisted pages. */
#identity-popup-securityView:not(.chromeUI) > #identity-popup-securityView-header > .identity-popup-connection-internal,
#identity-popup-security-content:not(.chromeUI) > .identity-popup-connection-internal,
/* Hide the subsection arrow for whitelisted chromeUI pages. */
#identity-popup-security-content.chromeUI + .identity-popup-expander,
/* Hide the subsection arrow for whitelisted file URI pages. */
#identity-popup-security-content.fileURI + .identity-popup-expander,
/* Hide the tracking protection section for whitelisted chromeUI pages. */
#identity-popup-mainView.chromeUI > #tracking-protection-container {
display: none;
@ -111,16 +116,8 @@
.identity-popup-expander[panel-multiview-anchor] {
transition: background-color 250ms ease-in;
background-color: Highlight;
background-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted.png"),
background-image: url("chrome://browser/skin/controlcenter/arrow-subview-back.svg"),
linear-gradient(rgba(255,255,255,0.3), transparent);
color: HighlightText;
}
@media (min-resolution: 1.1dppx) {
.identity-popup-expander[panel-multiview-anchor] {
background-image: url("chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png"),
linear-gradient(rgba(255,255,255,0.3), transparent);
}
}
.identity-popup-expander > .button-box {

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

@ -202,6 +202,7 @@ browser.jar:
skin/classic/browser/loop/toolbar-XP@2x.png (loop/toolbar-XP@2x.png)
* skin/classic/browser/controlcenter/panel.css (controlcenter/panel.css)
skin/classic/browser/controlcenter/arrow-subview.svg (../shared/controlcenter/arrow-subview.svg)
skin/classic/browser/controlcenter/arrow-subview-back.svg (../shared/controlcenter/arrow-subview-back.svg)
skin/classic/browser/controlcenter/conn-not-secure.svg (../shared/controlcenter/conn-not-secure.svg)
skin/classic/browser/controlcenter/conn-degraded.svg (../shared/controlcenter/conn-degraded.svg)
skin/classic/browser/controlcenter/conn-secure.svg (../shared/controlcenter/conn-secure.svg)

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

@ -700,15 +700,19 @@ BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
// Signal
JS::Rooted<JS::Value> value(nsContentUtils::RootingCxForThread());
voiceInfo->GetRelSignalStrength(&value);
NS_ENSURE_TRUE_VOID(value.isNumber());
mSignal = (int)ceil(value.toNumber() / 20.0);
if (value.isNumber()) {
mSignal = (int)ceil(value.toNumber() / 20.0);
}
UpdateDeviceCIND();
// Operator name
nsCOMPtr<nsIMobileNetworkInfo> network;
voiceInfo->GetNetwork(getter_AddRefs(network));
NS_ENSURE_TRUE_VOID(network);
if (!network) {
BT_LOGD("Unable to get network information");
return;
}
network->GetLongName(mOperatorName);
// According to GSM 07.07, "<format> indicates if the format is alphanumeric

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

@ -1008,6 +1008,11 @@ BluetoothAdapter::HandlePropertyChanged(const BluetoothValue& aValue)
}
}
if (types.IsEmpty()) {
// No adapter attribute changed
return;
}
DispatchAttributeEvent(types);
}
@ -1134,7 +1139,7 @@ BluetoothAdapter::HandleDeviceUnpaired(const BluetoothValue& aValue)
void
BluetoothAdapter::DispatchAttributeEvent(const Sequence<nsString>& aTypes)
{
NS_ENSURE_TRUE_VOID(aTypes.Length());
MOZ_ASSERT(!aTypes.IsEmpty());
BluetoothAttributeEventInit init;
init.mAttrs = aTypes;

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

@ -285,6 +285,8 @@ private:
/**
* Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
*
* @param aTypes [in] Array of changed attributes. Must be non-empty.
*/
void DispatchAttributeEvent(const Sequence<nsString>& aTypes);

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

@ -293,13 +293,18 @@ BluetoothDevice::HandlePropertyChanged(const BluetoothValue& aValue)
}
}
if (types.IsEmpty()) {
// No device attribute changed
return;
}
DispatchAttributeEvent(types);
}
void
BluetoothDevice::DispatchAttributeEvent(const Sequence<nsString>& aTypes)
{
NS_ENSURE_TRUE_VOID(aTypes.Length());
MOZ_ASSERT(!aTypes.IsEmpty());
BluetoothAttributeEventInit init;
init.mAttrs = aTypes;

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

@ -117,6 +117,8 @@ private:
/**
* Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
*
* @param aTypes [in] Array of changed attributes. Must be non-empty.
*/
void DispatchAttributeEvent(const Sequence<nsString>& aTypes);

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

@ -651,9 +651,10 @@ BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
JS::Rooted<JS::Value> value(nsContentUtils::RootingCxForThread());
voiceInfo->GetRelSignalStrength(&value);
NS_ENSURE_TRUE_VOID(value.isNumber());
uint8_t signal = ceil(value.toNumber() / 20.0);
UpdateCIND(CINDType::SIGNAL, signal);
if (value.isNumber()) {
uint8_t signal = ceil(value.toNumber() / 20.0);
UpdateCIND(CINDType::SIGNAL, signal);
}
/**
* Possible return values for mode are:
@ -667,7 +668,10 @@ BluetoothHfpManager::HandleVoiceConnectionChanged(uint32_t aClientId)
nsCOMPtr<nsIMobileNetworkInfo> network;
voiceInfo->GetNetwork(getter_AddRefs(network));
NS_ENSURE_TRUE_VOID(network);
if (!network) {
BT_LOGD("Unable to get network information");
return;
}
network->GetLongName(mOperatorName);
// According to GSM 07.07, "<format> indicates if the format is alphanumeric

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

@ -37,6 +37,8 @@ public class RestrictedProfiles {
add("wyciwyg");
}};
private static final String ABOUT_ADDONS = "about:addons";
/**
* This is a hack to allow non-GeckoApp activities to safely call into
* RestrictedProfiles without reworking this class or GeckoProfile.
@ -271,6 +273,13 @@ public class RestrictedProfiles {
return !GUEST_RESTRICTIONS.contains(restriction);
}
// Disallow browsing about:addons if 'disallow install extension' restriction is enforced
if (restriction == Restriction.DISALLOW_BROWSE_FILES
&& url.toLowerCase().startsWith(ABOUT_ADDONS)
&& !isAllowed(context, Restriction.DISALLOW_INSTALL_EXTENSION)) {
return false;
}
// NOTE: Restrictions hold the opposite intention, so we need to flip it.
return !getRestriction(context, restriction);
}

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

@ -65,7 +65,7 @@ public class TabMenuStrip extends HorizontalScrollView
super.draw(canvas);
final int height = getHeight();
canvas.drawRect(0, height - shadowSize, getWidth(), height, shadowPaint);
canvas.drawRect(0, height - shadowSize, layout.getWidth(), height, shadowPaint);
}
@Override

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

@ -566,8 +566,8 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY doorhanger_tracking_title "Tracking protection">
<!ENTITY doorhanger_tracking_state_enabled "Enabled">
<!ENTITY doorhanger_tracking_state_disabled "Disabled">
<!ENTITY doorhanger_tracking_message_enabled "Blocking tracking elements that may affect your browsing experience.">
<!ENTITY doorhanger_tracking_message_disabled "Attempts to track your online behavior are not being blocked.">
<!ENTITY doorhanger_tracking_message_enabled1 "Attempts to track your online behavior have been blocked.">
<!ENTITY doorhanger_tracking_message_disabled1 "This site includes content that tracks your browsing.">
<!-- Common mixed and tracking content strings in site identity popup -->
<!ENTITY learn_more "Learn More">

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

@ -472,8 +472,8 @@
<string name="doorhanger_tracking_title">&doorhanger_tracking_title;</string>
<string name="doorhanger_tracking_state_enabled">&doorhanger_tracking_state_enabled;</string>
<string name="doorhanger_tracking_state_disabled">&doorhanger_tracking_state_disabled;</string>
<string name="doorhanger_tracking_message_enabled">&doorhanger_tracking_message_enabled;</string>
<string name="doorhanger_tracking_message_disabled">&doorhanger_tracking_message_disabled;</string>
<string name="doorhanger_tracking_message_enabled">&doorhanger_tracking_message_enabled1;</string>
<string name="doorhanger_tracking_message_disabled">&doorhanger_tracking_message_disabled1;</string>
<string name="learn_more">&learn_more;</string>
<string name="enable_protection">&enable_protection;</string>

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

@ -3050,11 +3050,6 @@ var NativeWindow = {
clipboard.copyString(aString);
},
_shareStringWithDefault: function(aSharedString, aTitle) {
let sharing = Cc["@mozilla.org/uriloader/external-sharing-app-service;1"].getService(Ci.nsIExternalSharingAppService);
sharing.shareWithDefault(aSharedString, "text/plain", aTitle);
},
_stripScheme: function(aString) {
let index = aString.indexOf(":");
return aString.slice(index + 1);
@ -6017,6 +6012,10 @@ var FormAssistant = {
aCallback(false);
return;
}
if (this._isDisabledElement(aElement)) {
aCallback(false);
return;
}
// Don't display the form auto-complete popup after the user starts typing
// to avoid confusing somes IME. See bug 758820 and bug 632744.
@ -6083,6 +6082,17 @@ var FormAssistant = {
_hideFormAssistPopup: function _hideFormAssistPopup() {
Messaging.sendRequest({ type: "FormAssist:Hide" });
},
_isDisabledElement : function(aElement) {
let currentElement = aElement;
while (currentElement) {
if(currentElement.disabled)
return true;
currentElement = currentElement.parentElement;
}
return false;
}
};

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

@ -40,6 +40,12 @@ public class TestJarReader extends InstrumentationTestCase {
stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
assertNull(stream);
// Test looking for a file that doesn't exist in the APK.
// Bug 1174922, prefixed string / length error.
url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME + "BAD";
stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
assertNull(stream);
// Test looking for an jar with an invalid url.
url = "jar:file://" + appPath + "!" + "!/" + AppConstants.OMNIJAR_NAME;
stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");

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

@ -16,14 +16,12 @@ import android.content.Context;
* as loading some invalid jar urls.
*/
public class testJarReader extends BaseTest {
public void testGetJarURL() {
public void testJarReader() {
// Invalid characters are escaped.
final String s = GeckoJarReader.computeJarURI("some[1].apk", "something/else");
mAsserter.ok(!s.contains("["), "Illegal characters are escaped away.", null);
mAsserter.ok(!s.toLowerCase().contains("%2f"), "Path characters aren't escaped.", null);
}
public void testJarReader() {
final Context context = getInstrumentation().getTargetContext().getApplicationContext();
String appPath = getActivity().getApplication().getPackageResourcePath();
mAsserter.isnot(appPath, null, "getPackageResourcePath is non-null");
@ -43,6 +41,12 @@ public class testJarReader extends BaseTest {
stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
mAsserter.is(stream, null, "JarReader returned null for valid file in invalid jar file");
// Test looking for a file that doesn't exist in the APK.
// Bug 1174922, prefixed string / length error.
url = "jar:file://" + appPath + "!/" + AppConstants.OMNIJAR_NAME + "BAD";
stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/favicon32.png");
mAsserter.is(stream, null, "JarReader returned null for valid file in other invalid jar file");
// Test looking for an jar with an invalid url.
url = "jar:file://" + appPath + "!" + "!/" + AppConstants.OMNIJAR_NAME;
stream = GeckoJarReader.getStream(context, "jar:" + url + "!/chrome/chrome/content/branding/nonexistent_file.png");

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

@ -270,7 +270,7 @@ private:
*/
bool Equals(const char *str) const
{
return strncmp(str, buf, length) == 0;
return (strncmp(str, buf, length) == 0 && str[length] == '\0');
}
private:

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

@ -499,60 +499,20 @@ FxAccountsInternal.prototype = {
})
},
/**
* returns a promise that fires with the keypair.
*/
getKeyPair: Task.async(function* (mustBeValidUntil) {
// If the debugging pref to ignore cached authentication credentials is set for Sync,
// then don't use any cached key pair, i.e., generate a new one and get it signed.
// The purpose of this pref is to expedite any auth errors as the result of a
// expired or revoked FxA session token, e.g., from resetting or changing the FxA
// password.
let ignoreCachedAuthCredentials = false;
try {
ignoreCachedAuthCredentials = Services.prefs.getBoolPref("services.sync.debug.ignoreCachedAuthCredentials");
} catch(e) {
// Pref doesn't exist
}
let currentState = this.currentAccountState;
let accountData = yield currentState.getUserAccountData("keyPair");
if (!ignoreCachedAuthCredentials && accountData.keyPair && (accountData.keyPair.validUntil > mustBeValidUntil)) {
log.debug("getKeyPair: already have a keyPair");
return accountData.keyPair.keyPair;
}
// Otherwse, create a keypair and set validity limit.
let willBeValidUntil = this.now() + KEY_LIFETIME;
let kp = yield new Promise((resolve, reject) => {
jwcrypto.generateKeyPair("DS160", (err, kp) => {
if (err) {
return reject(err);
}
log.debug("got keyPair");
let toUpdate = {
keyPair: {
keyPair: kp,
validUntil: willBeValidUntil
},
cert: null
};
currentState.updateUserAccountData(toUpdate).then(() => {
resolve(kp);
}).catch(err => {
log.error("Failed to update account data with keypair and cert");
});
});
});
return kp;
}),
/**
* returns a promise that fires with the assertion. If there is no verified
* signed-in user, fires with null.
*/
getAssertion: function getAssertion(audience) {
return this._getAssertion(audience);
},
// getAssertion() is "public" so screws with our mock story. This
// implementation method *can* be (and is) mocked by tests.
_getAssertion: function _getAssertion(audience) {
log.debug("enter getAssertion()");
let currentState = this.currentAccountState;
let mustBeValidUntil = this.now() + ASSERTION_USE_PERIOD;
return currentState.getUserAccountData().then(data => {
if (!data) {
// No signed-in user
@ -562,12 +522,17 @@ FxAccountsInternal.prototype = {
// Signed-in user has not verified email
return null;
}
return this.getKeyPair(mustBeValidUntil).then(keyPair => {
return this.getCertificate(data, keyPair, mustBeValidUntil)
.then(cert => {
return this.getAssertionFromCert(data, keyPair, cert, audience);
});
});
if (!data.sessionToken) {
// can't get a signed certificate without a session token, but that
// should be impossible - make log noise about it.
log.error("getAssertion called without a session token!");
return null;
}
return this.getKeypairAndCertificate(currentState).then(
({keyPair, certificate}) => {
return this.getAssertionFromCert(data, keyPair, certificate, audience);
}
);
}).then(result => currentState.resolve(result));
},
@ -832,34 +797,91 @@ FxAccountsInternal.prototype = {
},
/**
* returns a promise that fires with a certificate.
* returns a promise that fires with {keyPair, certificate}.
*/
getCertificate: Task.async(function* (data, keyPair, mustBeValidUntil) {
// TODO: get the lifetime from the cert's .exp field
let currentState = this.currentAccountState;
let accountData = yield currentState.getUserAccountData("cert");
if (accountData.cert && accountData.cert.validUntil > mustBeValidUntil) {
log.debug(" getCertificate already had one");
return accountData.cert.cert;
getKeypairAndCertificate: Task.async(function* (currentState) {
// If the debugging pref to ignore cached authentication credentials is set for Sync,
// then don't use any cached key pair/certificate, i.e., generate a new
// one and get it signed.
// The purpose of this pref is to expedite any auth errors as the result of a
// expired or revoked FxA session token, e.g., from resetting or changing the FxA
// password.
let ignoreCachedAuthCredentials = false;
try {
ignoreCachedAuthCredentials = Services.prefs.getBoolPref("services.sync.debug.ignoreCachedAuthCredentials");
} catch(e) {
// Pref doesn't exist
}
let mustBeValidUntil = this.now() + ASSERTION_USE_PERIOD;
let accountData = yield currentState.getUserAccountData(["cert", "keyPair", "sessionToken"]);
let keyPairValid = !ignoreCachedAuthCredentials &&
accountData.keyPair &&
(accountData.keyPair.validUntil > mustBeValidUntil);
let certValid = !ignoreCachedAuthCredentials &&
accountData.cert &&
(accountData.cert.validUntil > mustBeValidUntil);
// TODO: get the lifetime from the cert's .exp field
if (keyPairValid && certValid) {
log.debug("getKeypairAndCertificate: already have keyPair and certificate");
return {
keyPair: accountData.keyPair.rawKeyPair,
certificate: accountData.cert.rawCert
}
}
// We are definately going to generate a new cert, either because it has
// already expired, or the keyPair has - and a new keyPair means we must
// generate a new cert.
// A keyPair has a longer lifetime than a cert, so it's possible we will
// have a valid keypair but an expired cert, which means we can skip
// keypair generation.
// Either way, the cert will require hitting the network, so bail now if
// we know that's going to fail.
if (Services.io.offline) {
throw new Error(ERROR_OFFLINE);
}
let willBeValidUntil = this.now() + CERT_LIFETIME;
let cert = yield this.getCertificateSigned(data.sessionToken,
keyPair.serializedPublicKey,
CERT_LIFETIME);
log.debug("getCertificate got a new one: " + !!cert);
if (cert) {
let keyPair;
if (keyPairValid) {
keyPair = accountData.keyPair;
} else {
let keyWillBeValidUntil = this.now() + KEY_LIFETIME;
keyPair = yield new Promise((resolve, reject) => {
jwcrypto.generateKeyPair("DS160", (err, kp) => {
if (err) {
return reject(err);
}
log.debug("got keyPair");
resolve({
rawKeyPair: kp,
validUntil: keyWillBeValidUntil,
});
});
});
}
// and generate the cert.
let certWillBeValidUntil = this.now() + CERT_LIFETIME;
let certificate = yield this.getCertificateSigned(accountData.sessionToken,
keyPair.rawKeyPair.serializedPublicKey,
CERT_LIFETIME);
log.debug("getCertificate got a new one: " + !!certificate);
if (certificate) {
// Cache both keypair and cert.
let toUpdate = {
keyPair,
cert: {
cert: cert,
validUntil: willBeValidUntil
}
rawCert: certificate,
validUntil: certWillBeValidUntil,
},
};
yield currentState.updateUserAccountData(toUpdate);
}
return cert;
return {
keyPair: keyPair.rawKeyPair,
certificate: certificate,
}
}),
getUserAccountData: function() {

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

@ -166,6 +166,22 @@ function MockFxAccounts() {
});
}
/*
* Some tests want a "real" fxa instance - however, we still mock the storage
* to keep the tests fast on b2g.
*/
function MakeFxAccounts(internal = {}) {
if (!internal.newAccountState) {
// we use a real accountState but mocked storage.
internal.newAccountState = function(credentials) {
let storage = new MockStorageManager();
storage.initialize(credentials);
return new AccountState(storage);
};
}
return new FxAccounts(internal);
}
add_test(function test_non_https_remote_server_uri_with_requireHttps_false() {
Services.prefs.setBoolPref(
"identity.fxaccounts.allowHttp",
@ -195,16 +211,8 @@ add_test(function test_non_https_remote_server_uri() {
});
add_task(function test_get_signed_in_user_initially_unset() {
// This test, unlike many of the the rest, uses a (largely) un-mocked
// FxAccounts instance.
let account = new FxAccounts({
newAccountState(credentials) {
// we use a real accountState but mocked storage.
let storage = new MockStorageManager();
storage.initialize(credentials);
return new AccountState(storage);
},
});
_("Check getSignedInUser initially and after signout reports no user");
let account = MakeFxAccounts();
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
@ -241,38 +249,22 @@ add_task(function test_get_signed_in_user_initially_unset() {
do_check_eq(result, null);
});
add_task(function* test_getCertificate() {
_("getCertificate()");
// This test, unlike many of the the rest, uses a (largely) un-mocked
// FxAccounts instance.
// We do mock the storage to keep the test fast on b2g.
let fxa = new FxAccounts({
newAccountState(credentials) {
// we use a real accountState but mocked storage.
let storage = new MockStorageManager();
storage.initialize(credentials);
return new AccountState(storage);
},
});
add_task(function* test_getCertificateOffline() {
_("getCertificateOffline()");
let fxa = MakeFxAccounts();
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
assertion: "foobar",
sessionToken: "dead",
kA: "beef",
kB: "cafe",
verified: true
verified: true,
};
yield fxa.setSignedInUser(credentials);
// Test that an expired cert throws if we're offline.
fxa.internal.currentAccountState.cert = {
validUntil: Date.parse("Mon, 13 Jan 2000 21:45:06 GMT")
};
let offline = Services.io.offline;
Services.io.offline = true;
// This call would break from missing parameters ...
yield fxa.internal.getCertificate().then(
yield fxa.internal.getKeypairAndCertificate(fxa.internal.currentAccountState).then(
result => {
Services.io.offline = offline;
do_throw("Unexpected success");
@ -283,8 +275,99 @@ add_task(function* test_getCertificate() {
do_check_eq(err, "Error: OFFLINE");
}
);
yield fxa.signOut(/*localOnly = */true);
});
add_task(function* test_getCertificateCached() {
_("getCertificateCached()");
let fxa = MakeFxAccounts();
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
sessionToken: "dead",
verified: true,
// A cached keypair and cert that remain valid.
keyPair: {
validUntil: Date.now() + KEY_LIFETIME + 10000,
rawKeyPair: "good-keypair",
},
cert: {
validUntil: Date.now() + CERT_LIFETIME + 10000,
rawCert: "good-cert",
},
};
yield fxa.setSignedInUser(credentials);
let {keyPair, certificate} = yield fxa.internal.getKeypairAndCertificate(fxa.internal.currentAccountState);
// should have the same keypair and cert.
do_check_eq(keyPair, credentials.keyPair.rawKeyPair);
do_check_eq(certificate, credentials.cert.rawCert);
yield fxa.signOut(/*localOnly = */true);
});
add_task(function* test_getCertificateExpiredCert() {
_("getCertificateExpiredCert()");
let fxa = MakeFxAccounts({
getCertificateSigned() {
return "new cert";
}
});
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
sessionToken: "dead",
verified: true,
// A cached keypair that remains valid.
keyPair: {
validUntil: Date.now() + KEY_LIFETIME + 10000,
rawKeyPair: "good-keypair",
},
// A cached certificate which has expired.
cert: {
validUntil: Date.parse("Mon, 13 Jan 2000 21:45:06 GMT"),
rawCert: "expired-cert",
},
};
yield fxa.setSignedInUser(credentials);
let {keyPair, certificate} = yield fxa.internal.getKeypairAndCertificate(fxa.internal.currentAccountState);
// should have the same keypair but a new cert.
do_check_eq(keyPair, credentials.keyPair.rawKeyPair);
do_check_neq(certificate, credentials.cert.rawCert);
yield fxa.signOut(/*localOnly = */true);
});
add_task(function* test_getCertificateExpiredKeypair() {
_("getCertificateExpiredKeypair()");
let fxa = MakeFxAccounts({
getCertificateSigned() {
return "new cert";
},
});
let credentials = {
email: "foo@example.com",
uid: "1234@lcip.org",
sessionToken: "dead",
verified: true,
// A cached keypair that has expired.
keyPair: {
validUntil: Date.now() - 1000,
rawKeyPair: "expired-keypair",
},
// A cached certificate which remains valid.
cert: {
validUntil: Date.now() + CERT_LIFETIME + 10000,
rawCert: "expired-cert",
},
};
yield fxa.setSignedInUser(credentials);
let {keyPair, certificate} = yield fxa.internal.getKeypairAndCertificate(fxa.internal.currentAccountState);
// even though the cert was valid, the fact the keypair was not means we
// should have fetched both.
do_check_neq(keyPair, credentials.keyPair.rawKeyPair);
do_check_neq(certificate, credentials.cert.rawCert);
yield fxa.signOut(/*localOnly = */true);
});
// Sanity-check that our mocked client is working correctly
add_test(function test_client_mock() {

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

@ -184,14 +184,10 @@ this.configureFxAccountIdentity = function(authService,
let accountState = new AccountState(storageManager);
return accountState;
},
getCertificate(data, keyPair, mustBeValidUntil) {
let cert = {
validUntil: this.now() + CERT_LIFETIME,
cert: "certificate",
};
this.currentAccountState.updateUserAccountData({cert: cert});
return Promise.resolve(cert.cert);
_getAssertion(audience) {
return Promise.resolve("assertion");
},
};
fxa = new FxAccounts(MockInternal);

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

@ -78,6 +78,12 @@ var signonsTreeView = {
return "";
}
},
isEditable : function(row, col) {
if (col.id == "userCol" || col.id == "passwordCol") {
return true;
}
return false;
},
isSeparator : function(index) { return false; },
isSorted : function() { return false; },
isContainer : function(index) { return false; },
@ -89,7 +95,31 @@ var signonsTreeView = {
return "ltr";
return "";
}
},
setCellText : function(row, col, value) {
// If there is a filter, _filterSet needs to be used, otherwise signons is used.
let table = signonsTreeView._filterSet.length ? signonsTreeView._filterSet : signons;
function _editLogin(field) {
if (value == table[row][field]) {
return;
}
let existingLogin = table[row].clone();
table[row][field] = value;
table[row].timePasswordChanged = Date.now();
passwordmanager.modifyLogin(existingLogin, table[row]);
signonsTree.treeBoxObject.invalidateRow(row);
}
if (col.id == "userCol") {
_editLogin("username");
} else if (col.id == "passwordCol") {
if (!value) {
return;
}
_editLogin("password");
}
},
};
@ -207,11 +237,15 @@ function FinalizeSignonDeletions(syncNeeded) {
}
function HandleSignonKeyPress(e) {
// If editing is currently performed, don't do anything.
if (signonsTree.getAttribute("editing")) {
return;
}
if (e.keyCode == KeyEvent.DOM_VK_DELETE
#ifdef XP_MACOSX
|| e.keyCode == KeyEvent.DOM_VK_BACK_SPACE
#endif
) {
) {
DeleteSignon();
}
}
@ -412,3 +446,11 @@ function masterPasswordLogin(noPasswordCallback) {
return token.isLoggedIn();
}
function escapeKeyHandler() {
// If editing is currently performed, don't do anything.
if (signonsTree.getAttribute("editing")) {
return;
}
window.close();
}

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

@ -24,7 +24,7 @@
<keyset>
<key keycode="VK_ESCAPE" oncommand="window.close();"/>
<key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
<key key="&windowClose.key;" modifiers="accel" oncommand="escapeKeyHandler();"/>
<key key="&focusSearch1.key;" modifiers="accel" oncommand="FocusFilterBox();"/>
<key key="&focusSearch2.key;" modifiers="accel" oncommand="FocusFilterBox();"/>
</keyset>
@ -60,6 +60,7 @@
style="height: 20em;"
onkeypress="HandleSignonKeyPress(event)"
onselect="SignonSelected();"
editable="true"
context="signonsTreeContextMenu">
<treecols>
<treecol id="siteCol" label="&treehead.site.label;" flex="40"

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

@ -8,6 +8,8 @@ support-files =
[browser_filldoorhanger.js]
[browser_notifications.js]
skip-if = true # Intermittent failures: Bug 1182296, bug 1148771
[browser_passwordmgr_editing.js]
skip-if = os == "linux"
[browser_passwordmgr_fields.js]
[browser_passwordmgr_observers.js]
[browser_passwordmgr_sort.js]

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

@ -0,0 +1,126 @@
const { ContentTaskUtils } = Cu.import("resource://testing-common/ContentTaskUtils.jsm", {});
const TIME_INTERVAL = 500;
const PWMGR_DLG = "chrome://passwordmgr/content/passwordManager.xul";
let doc;
let pwmgr;
let pwmgrdlg;
let signonsTree;
function addLogin(site, username, password) {
let nsLoginInfo = new Components.Constructor("@mozilla.org/login-manager/loginInfo;1",
Ci.nsILoginInfo, "init");
let login = new nsLoginInfo(site, site, null, username, password, "u", "p");
Services.logins.addLogin(login);
}
function getUsername(row) {
return signonsTree.view.getCellText(row, signonsTree.columns.getNamedColumn("userCol"));
}
function getPassword(row) {
return signonsTree.view.getCellText(row, signonsTree.columns.getNamedColumn("passwordCol"));
}
function synthesizeDblClickOnCell(aTree, column, row) {
let tbo = aTree.treeBoxObject;
let rect = tbo.getCoordsForCellItem(row, aTree.columns[column], "text");
let x = rect.x + rect.width / 2;
let y = rect.y + rect.height / 2;
// Simulate the double click.
EventUtils.synthesizeMouse(aTree.body, x, y, { clickCount: 2 },
aTree.ownerDocument.defaultView);
}
function togglePasswords() {
pwmgrdlg.document.querySelector("#togglePasswords").doCommand();
}
function* editUsernamePromises(site, oldUsername, newUsername) {
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login found");
let login = Services.logins.findLogins({}, site, "", "")[0];
is(login.username, oldUsername, "Correct username saved");
is(getUsername(0), oldUsername, "Correct username shown");
synthesizeDblClickOnCell(signonsTree, 1, 0);
yield ContentTaskUtils.waitForCondition(() => signonsTree.getAttribute("editing"),
"Waiting for editing");
EventUtils.sendString(newUsername, pwmgrdlg);
let signonsIntro = doc.querySelector("#signonsIntro");
EventUtils.sendMouseEvent({type: "click"}, signonsIntro, pwmgrdlg);
yield ContentTaskUtils.waitForCondition(() => !signonsTree.getAttribute("editing"),
"Waiting for editing to stop");
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login replaced");
login = Services.logins.findLogins({}, site, "", "")[0];
is(login.username, newUsername, "Correct username updated");
is(getUsername(0), newUsername, "Correct username shown");
}
function* editPasswordPromises(site, oldPassword, newPassword) {
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login found");
let login = Services.logins.findLogins({}, site, "", "")[0];
is(login.password, oldPassword, "Correct password saved");
is(getPassword(0), oldPassword, "Correct password shown");
synthesizeDblClickOnCell(signonsTree, 2, 0);
yield ContentTaskUtils.waitForCondition(() => signonsTree.getAttribute("editing"),
"Waiting for editing");
EventUtils.sendString(newPassword, pwmgrdlg);
let signonsIntro = doc.querySelector("#signonsIntro");
EventUtils.sendMouseEvent({type: "click"}, signonsIntro, pwmgrdlg);
yield ContentTaskUtils.waitForCondition(() => !signonsTree.getAttribute("editing"),
"Waiting for editing to stop");
is(Services.logins.findLogins({}, site, "", "").length, 1, "Correct login replaced");
login = Services.logins.findLogins({}, site, "", "")[0];
is(login.password, newPassword, "Correct password updated");
is(getPassword(0), newPassword, "Correct password shown");
}
add_task(function* test_setup() {
registerCleanupFunction(function() {
Services.logins.removeAllLogins();
});
Services.logins.removeAllLogins();
// Open the password manager dialog.
pwmgrdlg = window.openDialog(PWMGR_DLG, "Toolkit:PasswordManager", "");
Services.ww.registerNotification(function (aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
let win = aSubject.QueryInterface(Ci.nsIDOMEventTarget);
SimpleTest.waitForFocus(function() {
EventUtils.sendKey("RETURN", win);
}, win);
} else if (aSubject.location == pwmgrdlg.location && aTopic == "domwindowclosed") {
// Unregister ourself.
Services.ww.unregisterNotification(arguments.callee);
}
});
yield new Promise((resolve) => {
SimpleTest.waitForFocus(() => {
doc = pwmgrdlg.document;
signonsTree = doc.querySelector("#signonsTree");
resolve();
}, pwmgrdlg);
});
});
add_task(function* test_edit_multiple_logins() {
function* testLoginChange(site, oldUsername, oldPassword, newUsername, newPassword) {
addLogin(site, oldUsername, oldPassword);
yield* editUsernamePromises(site, oldUsername, newUsername);
togglePasswords();
yield* editPasswordPromises(site, oldPassword, newPassword);
togglePasswords();
}
yield* testLoginChange("http://c.tn/", "userC", "passC", "usernameC", "passwordC");
yield* testLoginChange("http://b.tn/", "userB", "passB", "usernameB", "passwordB");
yield* testLoginChange("http://a.tn/", "userA", "passA", "usernameA", "passwordA");
pwmgrdlg.close();
});

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

@ -162,11 +162,21 @@ function generateUUID() {
return str.substring(1, str.length - 1);
}
function getMsSinceProcessStart() {
try {
return Telemetry.msSinceProcessStart();
} catch (ex) {
// If this fails return a special value.
return -1;
}
}
/**
* This is a policy object used to override behavior for testing.
*/
let Policy = {
now: () => new Date(),
monotonicNow: getMsSinceProcessStart,
generateSessionUUID: () => generateUUID(),
generateSubsessionUUID: () => generateUUID(),
setSchedulerTickTimeout: (callback, delayMs) => setTimeout(callback, delayMs),
@ -710,6 +720,7 @@ this.TelemetrySession = Object.freeze({
Impl._subsessionCounter = 0;
Impl._profileSubsessionCounter = 0;
Impl._subsessionStartActiveTicks = 0;
Impl._subsessionStartTimeMonotonic = 0;
this.uninstall();
return this.setup();
},
@ -795,6 +806,9 @@ let Impl = {
_profileSubsessionCounter: 0,
// Date of the last session split
_subsessionStartDate: null,
// Start time of the current subsession using a monotonic clock for the subsession
// length measurements.
_subsessionStartTimeMonotonic: 0,
// The active ticks counted when the subsession starts
_subsessionStartActiveTicks: 0,
// A task performing delayed initialization of the chrome process
@ -1079,11 +1093,9 @@ let Impl = {
getMetadata: function getMetadata(reason) {
this._log.trace("getMetadata - Reason " + reason);
let sessionStartDate = toLocalTimeISOString(Utils.truncateToDays(this._sessionStartDate));
let subsessionStartDate = toLocalTimeISOString(Utils.truncateToDays(this._subsessionStartDate));
// Compute the subsession length in milliseconds, then convert to seconds.
let subsessionLength =
Math.floor((Policy.now() - this._subsessionStartDate.getTime()) / 1000);
const sessionStartDate = toLocalTimeISOString(Utils.truncateToDays(this._sessionStartDate));
const subsessionStartDate = toLocalTimeISOString(Utils.truncateToDays(this._subsessionStartDate));
const monotonicNow = Policy.monotonicNow();
let ret = {
reason: reason,
@ -1105,7 +1117,13 @@ let Impl = {
sessionStartDate: sessionStartDate,
subsessionStartDate: subsessionStartDate,
subsessionLength: subsessionLength,
// Compute the session and subsession length in seconds.
// We use monotonic clocks as Date() is affected by jumping clocks (leading
// to negative lengths and other issues).
sessionLength: Math.floor(monotonicNow / 1000),
subsessionLength:
Math.floor((monotonicNow - this._subsessionStartTimeMonotonic) / 1000),
};
// TODO: Remove this when bug 1124128 lands.
@ -1334,6 +1352,7 @@ let Impl = {
*/
startNewSubsession: function () {
this._subsessionStartDate = Policy.now();
this._subsessionStartTimeMonotonic = Policy.monotonicNow();
this._previousSubsessionId = this._subsessionId;
this._subsessionId = Policy.generateSubsessionUUID();
this._subsessionCounter++;

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

@ -39,7 +39,8 @@ Structure::
sessionStartDate: <ISO date>, // daily precision
subsessionStartDate: <ISO date>, // daily precision, ISO date in local time
subsessionLength: <number>, // the subsession length in seconds
sessionLength: <number>, // the session length until now in seconds, monotonic
subsessionLength: <number>, // the subsession length in seconds, monotonic
},
childPayloads: {...}, // only present with e10s; a reduced payload from content processes
@ -58,3 +59,21 @@ Structure::
slowSQL: {...},
slowSQLstartup: {...},
}
info
----
sessionLength
~~~~~~~~~~~~~
The length of the current session so far in seconds.
This uses a monotonic clock, so this may mismatch with other measurements that
are not monotonic like calculations based on ``Date.now()``.
If the monotonic clock failed, this will be ``-1``.
subsessionLength
~~~~~~~~~~~~~~~~
The length of this subsession in seconds.
This uses a monotonic clock, so this may mismatch with other measurements that are not monotonic (e.g. based on Date.now()).
If ``sessionLength`` is ``-1``, the monotonic clock is not working.

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

@ -248,6 +248,12 @@ function fakeNow(...args) {
return new Date(date);
}
function fakeMonotonicNow(ms) {
const m = Cu.import("resource://gre/modules/TelemetrySession.jsm");
m.Policy.monotonicNow = () => ms;
return ms;
}
// Fake the timeout functions for TelemetryController sending.
function fakePingSendTimer(set, clear) {
let module = Cu.import("resource://gre/modules/TelemetrySend.jsm");

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

@ -519,6 +519,7 @@ add_task(function* test_simplePing() {
let now = new Date(2020, 1, 1, 12, 0, 0);
let expectedDate = new Date(2020, 1, 1, 0, 0, 0);
fakeNow(now);
const monotonicStart = fakeMonotonicNow(5000);
const expectedSessionUUID = "bd314d15-95bf-4356-b682-b6c4a8942202";
const expectedSubsessionUUID = "3e2e5f6c-74ba-4e4d-a93f-a48af238a8c7";
@ -529,6 +530,7 @@ add_task(function* test_simplePing() {
// now fake the session duration.
const SESSION_DURATION_IN_MINUTES = 15;
fakeNow(new Date(2020, 1, 1, 12, SESSION_DURATION_IN_MINUTES, 0));
fakeMonotonicNow(monotonicStart + SESSION_DURATION_IN_MINUTES * 60 * 1000);
yield sendPing();
let ping = yield PingServer.promiseNextPing();

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

@ -80,7 +80,7 @@ function forbidCPOW(arg, func, argname)
// - A linked document using Alt-click Save Link As...
//
function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
aSkipPrompt, aReferrer, aSourceDocument)
aSkipPrompt, aReferrer, aSourceDocument, aIsContentWindowPrivate)
{
forbidCPOW(aURL, "saveURL", "aURL");
forbidCPOW(aReferrer, "saveURL", "aReferrer");
@ -88,7 +88,7 @@ function saveURL(aURL, aFileName, aFilePickerTitleKey, aShouldBypassCache,
internalSave(aURL, null, aFileName, null, null, aShouldBypassCache,
aFilePickerTitleKey, null, aReferrer, aSourceDocument,
aSkipPrompt, null);
aSkipPrompt, null, aIsContentWindowPrivate);
}
// Just like saveURL, but will get some info off the image before
@ -264,19 +264,24 @@ const kSaveAsType_Text = 2; // Save document, converting to plain text.
* @param aReferrer
* the referrer URI object (not URL string) to use, or null
* if no referrer should be sent.
* @param aInitiatingDocument
* @param aInitiatingDocument [optional]
* The document from which the save was initiated.
* If this is omitted then aIsContentWindowPrivate has to be provided.
* @param aSkipPrompt [optional]
* If set to true, we will attempt to save the file to the
* default downloads folder without prompting.
* @param aCacheKey [optional]
* If set will be passed to saveURI. See nsIWebBrowserPersist for
* allowed values.
* @param aIsContentWindowPrivate [optional]
* This parameter is provided when the aInitiatingDocument is not a
* real document object. Stores whether aInitiatingDocument.defaultView
* was private or not.
*/
function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
aContentType, aShouldBypassCache, aFilePickerTitleKey,
aChosenData, aReferrer, aInitiatingDocument, aSkipPrompt,
aCacheKey)
aCacheKey, aIsContentWindowPrivate)
{
forbidCPOW(aURL, "internalSave", "aURL");
forbidCPOW(aReferrer, "internalSave", "aReferrer");
@ -357,7 +362,8 @@ function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
sourceCacheKey : aCacheKey,
sourcePostData : nonCPOWDocument ? getPostData(aDocument) : null,
bypassCache : aShouldBypassCache,
initiatingWindow : aInitiatingDocument.defaultView
initiatingWindow : aInitiatingDocument && aInitiatingDocument.defaultView,
isContentWindowPrivate : aIsContentWindowPrivate
};
// Start the actual save process
@ -392,8 +398,12 @@ function internalSave(aURL, aDocument, aDefaultFileName, aContentDisposition,
* "text/plain" is meaningful.
* @param persistArgs.bypassCache
* If true, the document will always be refetched from the server
* @param persistArgs.initiatingWindow
* @param persistArgs.initiatingWindow [optional]
* The window from which the save operation was initiated.
* If this is omitted then isContentWindowPrivate has to be provided.
* @param persistArgs.isContentWindowPrivate [optional]
* If present then isPrivate is set to this value without using
* persistArgs.initiatingWindow.
*/
function internalPersist(persistArgs)
{
@ -414,7 +424,10 @@ function internalPersist(persistArgs)
// Find the URI associated with the target file
var targetFileURL = makeFileURI(persistArgs.targetFile);
let isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(persistArgs.initiatingWindow);
let isPrivate = persistArgs.isContentWindowPrivate;
if (isPrivate === undefined) {
isPrivate = PrivateBrowsingUtils.isContentWindowPrivate(persistArgs.initiatingWindow);
}
// Create download and initiate it (below)
var tr = Components.classes["@mozilla.org/transfer;1"].createInstance(Components.interfaces.nsITransfer);

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

@ -44,12 +44,13 @@ loader.lazyGetter(this, "DOMUtils", function() {
* Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
* const {OutputParser} = devtools.require("devtools/output-parser");
*
* let parser = new OutputParser();
* let parser = new OutputParser(document);
*
* parser.parseCssProperty("color", "red"); // Returns document fragment.
*/
function OutputParser() {
function OutputParser(document) {
this.parsed = [];
this.doc = document;
this.colorSwatches = new WeakMap();
this._onSwatchMouseDown = this._onSwatchMouseDown.bind(this);
}
@ -442,12 +443,10 @@ OutputParser.prototype = {
* @param {String} [value]
* If a value is included it will be appended as a text node inside
* the tag. This is useful e.g. for span tags.
* @return {Node} Newly created Node.
* @return {Node} Newly created Node.
*/
_createNode: function(tagName, attributes, value="") {
let win = Services.appShell.hiddenDOMWindow;
let doc = win.document;
let node = doc.createElementNS(HTML_NS, tagName);
let node = this.doc.createElementNS(HTML_NS, tagName);
let attrs = Object.getOwnPropertyNames(attributes);
for (let attr of attrs) {
@ -457,7 +456,7 @@ OutputParser.prototype = {
}
if (value) {
let textNode = doc.createTextNode(value);
let textNode = this.doc.createTextNode(value);
node.appendChild(textNode);
}
@ -503,13 +502,11 @@ OutputParser.prototype = {
* Document Fragment
*/
_toDOM: function() {
let win = Services.appShell.hiddenDOMWindow;
let doc = win.document;
let frag = doc.createDocumentFragment();
let frag = this.doc.createDocumentFragment();
for (let item of this.parsed) {
if (typeof item === "string") {
frag.appendChild(doc.createTextNode(item));
frag.appendChild(this.doc.createTextNode(item));
} else {
frag.appendChild(item);
}