Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-04-07 11:56:04 +02:00
Родитель a64c4b22bd 1542317855
Коммит e6e089c791
142 изменённых файлов: 2890 добавлений и 1363 удалений

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

@ -48,6 +48,9 @@
return decodeURIComponent(url.slice(error + 2, duffUrl));
}
// Set to true on init if the error code is nssBadCert.
var gIsCertError;
function getCSSClass()
{
var url = document.documentURI;
@ -103,7 +106,7 @@
'none': 'block',
'block': 'none'
};
node.style.display = toggle[node.style.display];
return (node.style.display = toggle[node.style.display]);
}
function showCertificateErrorReporting() {
@ -118,17 +121,27 @@
document.getElementById("errorTryAgain").style.display = "none";
// Get the hostname and add it to the panel
var panel = document.getElementById("weakCryptoAdvancedPanel");
var panelId = gIsCertError ? "badCertAdvancedPanel" : "weakCryptoAdvancedPanel";
var panel = document.getElementById(panelId);
for (var span of panel.querySelectorAll("span.hostname")) {
span.textContent = document.location.hostname;
}
panel.replaceChild(document.getElementById("errorLongDesc"),
document.getElementById("advancedLongDesc"));
if (!gIsCertError) {
panel.replaceChild(document.getElementById("errorLongDesc"),
document.getElementById("advancedLongDesc"));
}
// Register click handler for the weakCryptoAdvancedPanel
document.getElementById("advancedButton")
.addEventListener("click", function togglePanelVisibility() {
toggleDisplay(panel);
if (gIsCertError) {
// Toggling the advanced panel must ensure that the debugging
// information panel is hidden as well, since it's opened by the
// error code link in the advanced panel.
var div = document.getElementById("certificateErrorDebugInformation");
div.style.display = "none";
}
if (panel.style.display == "block") {
// send event to trigger telemetry ping
@ -144,9 +157,68 @@
}
}
function initPageCertError() {
document.body.className = "certerror";
document.title = document.getElementById("certErrorPageTitle").textContent;
for (let host of document.querySelectorAll(".hostname")) {
host.textContent = document.location.hostname;
}
showAdvancedButton(true);
var cssClass = getCSSClass();
if (cssClass == "expertBadCert") {
toggleDisplay(document.getElementById("badCertAdvancedPanel"));
// Toggling the advanced panel must ensure that the debugging
// information panel is hidden as well, since it's opened by the
// error code link in the advanced panel.
var div = document.getElementById("certificateErrorDebugInformation");
div.style.display = "none";
}
document.getElementById("learnMoreContainer").style.display = "block";
var checkbox = document.getElementById("automaticallyReportInFuture");
checkbox.addEventListener("change", function ({target: {checked}}) {
document.dispatchEvent(new CustomEvent("AboutNetErrorSetAutomatic", {
detail: checked,
bubbles: true
}));
});
addEventListener("AboutNetErrorOptions", function (event) {
var options = JSON.parse(event.detail);
if (options && options.enabled) {
// Display error reporting UI
document.getElementById("certificateErrorReporting").style.display = "block";
// set the checkbox
checkbox.checked = !!options.automatic;
}
}, true, true);
// Disallow overrides if this is a Strict-Transport-Security
// host and the cert is bad (STS Spec section 7.3) or if the
// certerror is in a frame (bug 633691).
if (cssClass == "badStsCert" || window != top) {
document.getElementById("exceptionDialogButton").setAttribute("hidden", "true");
}
if (cssClass == "badStsCert") {
document.getElementById("badStsCertExplanation").removeAttribute("hidden");
}
document.getElementById("badCertTechnicalInfo").textContent = getDescription();
var event = new CustomEvent("AboutNetErrorLoad", {bubbles:true});
document.getElementById("advancedButton").dispatchEvent(event);
addDomainErrorLinks();
}
function initPage()
{
var err = getErrorCode();
gIsCertError = (err == "nssBadCert");
// if it's an unknown error or there's no title or description
// defined, get the generic message
@ -167,8 +239,18 @@
}
var sd = document.getElementById("errorShortDescText");
if (sd)
sd.textContent = getDescription();
if (sd) {
if (gIsCertError) {
sd.textContent = document.getElementById("ed_nssBadCert").textContent;
}
else {
sd.textContent = getDescription();
}
}
if (gIsCertError) {
initPageCertError();
return;
}
var ld = document.getElementById("errorLongDesc");
if (ld)
@ -211,9 +293,6 @@
favicon.setAttribute("href", "chrome://global/skin/icons/" + className + "_favicon.png");
faviconParent.appendChild(favicon);
}
if (className == "expertBadCert") {
showSecuritySection();
}
if (err == "remoteXUL") {
// Remove the "Try again" button for remote XUL errors given that
@ -265,27 +344,9 @@
var event = new CustomEvent("AboutNetErrorLoad", {bubbles:true});
document.dispatchEvent(event);
if (err == "nssBadCert") {
// Remove the "Try again" button for security exceptions, since it's
// almost certainly useless.
document.getElementById("errorTryAgain").style.display = "none";
document.getElementById("errorPageContainer").setAttribute("class", "certerror");
}
else {
// Remove the override block for non-certificate errors. CSS-hiding
// isn't good enough here, because of bug 39098
var secOverride = document.getElementById("securityOverrideDiv");
secOverride.parentNode.removeChild(secOverride);
}
addDomainErrorLinks();
}
function showSecuritySection() {
// Swap link out, content in
document.getElementById('securityOverrideContent').style.display = '';
document.getElementById('securityOverrideLink').style.display = 'none';
}
/* Try to preserve the links contained in the error description, like
the error code.
@ -297,7 +358,8 @@
*/
function addDomainErrorLinks() {
// Rather than textContent, we need to treat description as HTML
var sd = document.getElementById("errorShortDescText");
var sdid = gIsCertError ? "badCertTechnicalInfo" : "errorShortDescText";
var sd = document.getElementById(sdid);
if (sd) {
var desc = getDescription();
@ -335,6 +397,21 @@
sd.appendChild(document.createTextNode(desc.slice(desc.lastIndexOf("</a>") + "</a>".length)));
}
if (gIsCertError) {
// Initialize the error code link embedded in the error message to
// display debug information about the cert error.
var errorCode = document.getElementById("errorCode");
if (errorCode) {
errorCode.href = "#technicalInformation";
errorCode.addEventListener("click", () => {
var div = document.getElementById("certificateErrorDebugInformation");
if (toggleDisplay(div) == "block") {
div.scrollIntoView({block: "start", behavior: "smooth"});
}
}, false);
}
}
// Initialize the cert domain link.
var link = document.getElementById('cert_domain_link');
if (!link)
@ -375,8 +452,17 @@
// If we set a link, meaning there's something helpful for
// the user here, expand the section by default
if (link.href && getCSSClass() != "expertBadCert")
toggleVisibility("advancedPanel");
if (link.href && getCSSClass() != "expertBadCert") {
var panelId = gIsCertError ? "badCertAdvancedPanel" : "weakCryptoAdvancedPanel"
toggleDisplay(document.getElementById(panelId));
if (gIsCertError) {
// Toggling the advanced panel must ensure that the debugging
// information panel is hidden as well, since it's opened by the
// error code link in the advanced panel.
var div = document.getElementById("certificateErrorDebugInformation");
div.style.display = "none";
}
}
}
function createLink(el, id, text) {
@ -390,6 +476,8 @@
</head>
<body dir="&locale.dir;">
<!-- Contains an alternate page title set on page init for cert errors. -->
<div id="certErrorPageTitle" style="display: none;">&certerror.pagetitle1;</div>
<!-- ERROR ITEM CONTAINER (removed during loading to avoid bug 39098) -->
<div id="errorContainer">
@ -413,7 +501,7 @@
<h1 id="et_contentEncodingError">&contentEncodingError.title;</h1>
<h1 id="et_unsafeContentType">&unsafeContentType.title;</h1>
<h1 id="et_nssFailure2">&nssFailure2.title;</h1>
<h1 id="et_nssBadCert">&nssBadCert.title;</h1>
<h1 id="et_nssBadCert">&certerror.longpagetitle1;</h1>
<h1 id="et_cspBlocked">&cspBlocked.title;</h1>
<h1 id="et_remoteXUL">&remoteXUL.title;</h1>
<h1 id="et_corruptedContentError">&corruptedContentError.title;</h1>
@ -440,7 +528,7 @@
<div id="ed_contentEncodingError">&contentEncodingError.longDesc;</div>
<div id="ed_unsafeContentType">&unsafeContentType.longDesc;</div>
<div id="ed_nssFailure2">&nssFailure2.longDesc2;</div>
<div id="ed_nssBadCert">&nssBadCert.longDesc2;</div>
<div id="ed_nssBadCert">&certerror.introPara;</div>
<div id="ed_cspBlocked">&cspBlocked.longDesc;</div>
<div id="ed_remoteXUL">&remoteXUL.longDesc;</div>
<div id="ed_corruptedContentError">&corruptedContentError.longDesc;</div>
@ -464,17 +552,11 @@
<div id="errorShortDesc">
<p id="errorShortDescText" />
</div>
<p id="badStsCertExplanation" hidden="true">&certerror.whatShouldIDo.badStsCertExplanation;</p>
<!-- Long Description (Note: See netError.dtd for used XHTML tags) -->
<div id="errorLongDesc" />
<!-- Override section - For ssl errors only. Removed on init for other
error types. -->
<div id="securityOverrideDiv">
<a id="securityOverrideLink" href="javascript:showSecuritySection();" >&securityOverride.linkText;</a>
<div id="securityOverrideContent" style="display: none;">&securityOverride.warningContent;</div>
</div>
<div id="learnMoreContainer">
<p><a href="https://support.mozilla.org/kb/what-does-your-connection-is-not-secure-mean" id="learnMoreLink" target="new">&errorReporting.learnMore;</a></p>
</div>
@ -522,6 +604,18 @@
</div>
</div>
<div id="badCertAdvancedPanel">
<p id="badCertTechnicalInfo"/>
<button id="exceptionDialogButton">&securityOverride.exceptionButtonLabel;</button>
</div>
</div>
<div id="certificateErrorDebugInformation">
<a name="technicalInformation"></a>
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
<div id="certificateErrorText"/>
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
</div>
<!--

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

@ -1,17 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* Logical CSS rules belong here, but presentation & theming rules
should live in the CSS of the appropriate theme */
#technicalContentText {
overflow: auto;
white-space: pre-wrap;
}
.expander[hidden],
.expander[hidden] + *,
.expander[collapsed] + * {
display: none;
}

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

@ -1,308 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html [
<!ENTITY % htmlDTD
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"DTD/xhtml1-strict.dtd">
%htmlDTD;
<!ENTITY % globalDTD
SYSTEM "chrome://global/locale/global.dtd">
%globalDTD;
<!ENTITY % certerrorDTD
SYSTEM "chrome://browser/locale/aboutCertError.dtd">
%certerrorDTD;
]>
<!-- 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/. -->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&certerror.pagetitle1;</title>
<link rel="stylesheet" href="chrome://browser/skin/aboutCertError.css" type="text/css" media="all" />
<link rel="stylesheet" href="chrome://browser/content/certerror/aboutCertError.css" type="text/css" media="all" />
<!-- This page currently uses the same favicon as neterror.xhtml.
If the location of the favicon is changed for both pages, the
FAVICON_ERRORPAGE_URL symbol in toolkit/components/places/src/nsFaviconService.h
should be updated. If this page starts using a different favicon
than neterror.xhtml nsFaviconService->SetAndLoadFaviconForPage
should be updated to ignore this one as well. -->
<link rel="icon" type="image/png" id="favicon" href="chrome://global/skin/icons/warning-16.png"/>
<script type="application/javascript"><![CDATA[
// Error url MUST be formatted like this:
// about:certerror?e=error&u=url&d=desc
// Note that this file uses document.documentURI to get
// the URL (with the format from above). This is because
// document.location.href gets the current URI off the docshell,
// which is the URL displayed in the location bar, i.e.
// the URI that the user attempted to load.
function getCSSClass()
{
var url = document.documentURI;
var matches = url.match(/s\=([^&]+)\&/);
// s is optional, if no match just return nothing
if (!matches || matches.length < 2)
return "";
// parenthetical match is the second entry
return decodeURIComponent(matches[1]);
}
function toggleVisibility(id)
{
var node = document.getElementById(id);
node.style.visibility = node.style.visibility == "" ? "hidden" : "";
// Toggling the advanced panel must ensure that the debugging
// information panel is hidden as well, since it's opened by the
// error code link in the advanced panel.
if (id == "advancedPanel") {
var div = document.getElementById("certificateErrorDebugInformation");
div.style.display = "none";
}
}
function getDescription()
{
var url = document.documentURI;
var desc = url.search(/d\=/);
// desc == -1 if not found; if so, return an empty string
// instead of what would turn out to be portions of the URI
if (desc == -1)
return "";
return decodeURIComponent(url.slice(desc + 2));
}
function initPage()
{
for (let host of document.querySelectorAll(".hostname")) {
host.textContent = document.location.hostname;
}
var cssClass = getCSSClass();
if (cssClass == "expertBadCert") {
toggleVisibility('advancedPanel');
}
var checkbox = document.getElementById("automaticallyReportInFuture");
checkbox.addEventListener("change", function ({target: {checked}}) {
document.dispatchEvent(new CustomEvent("AboutCertErrorSetAutomatic", {
detail: checked,
bubbles: true
}));
});
addEventListener("AboutCertErrorOptions", function (event) {
var options = JSON.parse(event.detail);
if (options && options.enabled) {
// Display error reporting UI
document.getElementById("certificateErrorReporting").style.display = "block";
// set the checkbox
checkbox.checked = !!options.automatic;
}
}, true, true);
// Disallow overrides if this is a Strict-Transport-Security
// host and the cert is bad (STS Spec section 7.3) or if the
// certerror is in a frame (bug 633691).
if (cssClass == "badStsCert" || window != top) {
document.getElementById("exceptionDialogButton").setAttribute("hidden", "true");
}
if (cssClass != "badStsCert") {
document.getElementById("badStsCertExplanation").setAttribute("hidden", "true");
}
var tech = document.getElementById("technicalContentText");
if (tech)
tech.textContent = getDescription();
var event = new CustomEvent("AboutCertErrorLoad", {bubbles:true});
document.getElementById("advancedButton").dispatchEvent(event);
addDomainErrorLinks();
}
/* Try to preserve the links contained in the error description, like
the error code.
Also, in the case of SSL error pages about domain mismatch, see if
we can hyperlink the user to the correct site. We don't want
to do this generically since it allows MitM attacks to redirect
users to a site under attacker control, but in certain cases
it is safe (and helpful!) to do so. Bug 402210
*/
function addDomainErrorLinks() {
// Rather than textContent, we need to treat description as HTML
var sd = document.getElementById("technicalContentText");
if (sd) {
var desc = getDescription();
// sanitize description text - see bug 441169
// First, find the index of the <a> tags we care about, being
// careful not to use an over-greedy regex.
var codeRe = /<a id="errorCode" title="([^"]+)">/;
var codeResult = codeRe.exec(desc);
var domainRe = /<a id="cert_domain_link" title="([^"]+)">/;
var domainResult = domainRe.exec(desc);
// The order of these links in the description is fixed in
// TransportSecurityInfo.cpp:formatOverridableCertErrorMessage.
var firstResult = domainResult;
if(!domainResult)
firstResult = codeResult;
if (!firstResult)
return;
// Remove sd's existing children
sd.textContent = "";
// Everything up to the first link should be text content.
sd.appendChild(document.createTextNode(desc.slice(0, firstResult.index)));
// Now create the actual links.
if (domainResult) {
createLink(sd, "cert_domain_link", domainResult[1])
// Append text for anything between the two links.
sd.appendChild(document.createTextNode(desc.slice(desc.indexOf("</a>") + "</a>".length, codeResult.index)));
}
createLink(sd, "errorCode", codeResult[1])
// Finally, append text for anything after the last closing </a>.
sd.appendChild(document.createTextNode(desc.slice(desc.lastIndexOf("</a>") + "</a>".length)));
}
// Initialize the error code link embedded in the error message to
// display debug information about the cert error.
var errorCode = document.getElementById("errorCode");
if (errorCode) {
errorCode.href = "#technicalInformation";
errorCode.addEventListener("click", () => {
var div = document.getElementById("certificateErrorDebugInformation");
if (div.style.display == "block") {
div.style.display = "none";
} else {
div.style.display = "block";
div.scrollIntoView(true);
}
}, false);
}
// Then initialize the cert domain link.
var link = document.getElementById('cert_domain_link');
if (!link)
return;
var okHost = link.getAttribute("title");
var thisHost = document.location.hostname;
var proto = document.location.protocol;
// If okHost is a wildcard domain ("*.example.com") let's
// use "www" instead. "*.example.com" isn't going to
// get anyone anywhere useful. bug 432491
okHost = okHost.replace(/^\*\./, "www.");
/* case #1:
* example.com uses an invalid security certificate.
*
* The certificate is only valid for www.example.com
*
* Make sure to include the "." ahead of thisHost so that
* a MitM attack on paypal.com doesn't hyperlink to "notpaypal.com"
*
* We'd normally just use a RegExp here except that we lack a
* library function to escape them properly (bug 248062), and
* domain names are famous for having '.' characters in them,
* which would allow spurious and possibly hostile matches.
*/
if (okHost.endsWith("." + thisHost))
link.href = proto + okHost;
/* case #2:
* browser.garage.maemo.org uses an invalid security certificate.
*
* The certificate is only valid for garage.maemo.org
*/
if (thisHost.endsWith("." + okHost))
link.href = proto + okHost;
// If we set a link, meaning there's something helpful for
// the user here, expand the section by default
if (link.href && getCSSClass() != "expertBadCert")
toggleVisibility("advancedPanel");
}
function createLink(el, id, text) {
var anchorEl = document.createElement("a");
anchorEl.setAttribute("id", id);
anchorEl.setAttribute("title", text);
anchorEl.appendChild(document.createTextNode(text));
el.appendChild(anchorEl);
}
]]></script>
</head>
<body dir="&locale.dir;">
<!-- PAGE CONTAINER (for styling purposes only) -->
<div id="errorPageContainer">
<!-- Error Title -->
<div id="errorTitle">
<h1 id="errorTitleText">&certerror.longpagetitle1;</h1>
</div>
<!-- LONG CONTENT (the section most likely to require scrolling) -->
<div id="errorLongContent">
<!-- Short Description -->
<div id="errorShortDesc">
<p>&certerror.introPara;</p>
</div>
<p id="badStsCertExplanation">&certerror.whatShouldIDo.badStsCertExplanation;</p>
<div>
<p><a href="https://support.mozilla.org/kb/what-does-your-connection-is-not-secure-mean" id="learnMoreLink" target="new">&certerror.learnMore;</a></p>
</div>
<div id="buttonContainer">
<button id="returnButton" autocomplete="off" autofocus="true">&certerror.returnToPreviousPage.label;</button>
<div id="buttonSpacer"></div>
<button id="advancedButton" autocomplete="off" onclick="toggleVisibility('advancedPanel');" autofocus="true">&certerror.advanced.label;</button>
</div>
</div>
<!-- UI for option to report certificate errors to Mozilla. -->
<div id="certificateErrorReporting">
<p>
<input type="checkbox" id="automaticallyReportInFuture" />
<label for="automaticallyReportInFuture" id="automaticallyReportInFuture">&errorReporting.automatic;</label>
</p>
</div>
<!-- Advanced panel, which is hidden by default -->
<div id="advancedPanel" style="visibility: hidden;">
<p id="technicalContentText"/>
<button id="exceptionDialogButton">&certerror.addException.label;</button>
</div>
</div>
<div id="certificateErrorDebugInformation">
<a name="technicalInformation"></a>
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
<div id="certificateErrorText"/>
<button id="copyToClipboard">&certerror.copyToClipboard.label;</button>
</div>
<!--
- Note: It is important to run the script this way, instead of using
- an onload handler. This is because error pages are loaded as
- LOAD_BACKGROUND, which means that onload handlers will not be executed.
-->
<script type="application/javascript">initPage();</script>
</body>
</html>

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

@ -2664,9 +2664,9 @@ var BrowserOnClick = {
receiveMessage: function (msg) {
switch (msg.name) {
case "Browser:CertExceptionError":
this.onAboutCertError(msg.target, msg.data.elementId,
msg.data.isTopFrame, msg.data.location,
msg.data.securityInfoAsString);
this.onCertError(msg.target, msg.data.elementId,
msg.data.isTopFrame, msg.data.location,
msg.data.securityInfoAsString);
break;
case "Browser:SiteBlockedError":
this.onAboutBlocked(msg.data.elementId, msg.data.reason,
@ -2727,7 +2727,7 @@ var BrowserOnClick = {
uri.host, uri.port);
},
onAboutCertError: function (browser, elementId, isTopFrame, location, securityInfoAsString) {
onCertError: function (browser, elementId, isTopFrame, location, securityInfoAsString) {
let secHistogram = Services.telemetry.getHistogramById("SECURITY_UI");
switch (elementId) {
@ -2778,7 +2778,7 @@ var BrowserOnClick = {
let errorInfo = getDetailedCertErrorInfo(location,
securityInfoAsString);
browser.messageManager.sendAsyncMessage("AboutCertErrorDetails",
browser.messageManager.sendAsyncMessage("CertErrorDetails",
{ info: errorInfo });
break;

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

@ -212,98 +212,10 @@ const TLS_ERROR_REPORT_TELEMETRY_EXPANDED = 1;
const TLS_ERROR_REPORT_TELEMETRY_SUCCESS = 6;
const TLS_ERROR_REPORT_TELEMETRY_FAILURE = 7;
var AboutCertErrorListener = {
init(chromeGlobal) {
addMessageListener("AboutCertErrorDetails", this);
chromeGlobal.addEventListener("AboutCertErrorLoad", this, false, true);
chromeGlobal.addEventListener("AboutCertErrorSetAutomatic", this, false, true);
},
get isAboutCertError() {
return content.document.documentURI.startsWith("about:certerror");
},
handleEvent(event) {
if (!this.isAboutCertError) {
return;
}
switch (event.type) {
case "AboutCertErrorLoad":
this.onLoad(event);
break;
case "AboutCertErrorSetAutomatic":
this.onSetAutomatic(event);
break;
}
},
receiveMessage(msg) {
if (!this.isAboutCertError) {
return;
}
switch (msg.name) {
case "AboutCertErrorDetails":
this.onDetails(msg);
break;
}
},
onLoad(event) {
let originalTarget = event.originalTarget;
let ownerDoc = originalTarget.ownerDocument;
ClickEventHandler.onAboutCertError(originalTarget, ownerDoc);
// Set up the TLS Error Reporting UI - reports are sent automatically
// (from nsHttpChannel::OnStopRequest) if the user has previously enabled
// automatic sending of reports. The UI ensures that a report is sent
// for the certificate error currently displayed if the user enables it
// here.
let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
content.dispatchEvent(new content.CustomEvent("AboutCertErrorOptions", {
detail: JSON.stringify({
enabled: Services.prefs.getBoolPref("security.ssl.errorReporting.enabled"),
automatic,
})
}));
},
onDetails(msg) {
let div = content.document.getElementById("certificateErrorText");
div.textContent = msg.data.info;
},
onSetAutomatic(event) {
sendAsyncMessage("Browser:SetSSLErrorReportAuto", {
automatic: event.detail
});
// if we're enabling reports, send a report for this failure
if (event.detail) {
let serhelper = Cc["@mozilla.org/network/serialization-helper;1"]
.getService(Ci.nsISerializationHelper);
let serializable = docShell.failedChannel.securityInfo
.QueryInterface(Ci.nsITransportSecurityInfo)
.QueryInterface(Ci.nsISerializable);
let serializedSecurityInfo = serhelper.serializeToString(serializable);
let {host, port} = content.document.mozDocumentURIIfNotForErrorPages;
sendAsyncMessage("Browser:SendSSLErrorReport", {
uri: { host, port },
securityInfo: serializedSecurityInfo
});
}
},
};
AboutCertErrorListener.init(this);
var AboutNetErrorListener = {
var AboutNetAndCertErrorListener = {
init: function(chromeGlobal) {
addMessageListener("CertErrorDetails", this);
chromeGlobal.addEventListener('AboutNetErrorLoad', this, false, true);
chromeGlobal.addEventListener('AboutNetErrorSetAutomatic', this, false, true);
chromeGlobal.addEventListener('AboutNetErrorOverride', this, false, true);
@ -313,8 +225,29 @@ var AboutNetErrorListener = {
return content.document.documentURI.startsWith("about:neterror");
},
get isAboutCertError() {
return content.document.documentURI.startsWith("about:certerror");
},
receiveMessage: function(msg) {
if (!this.isAboutCertError) {
return;
}
switch (msg.name) {
case "CertErrorDetails":
this.onCertErrorDetails(msg);
break;
}
},
onCertErrorDetails(msg) {
let div = content.document.getElementById("certificateErrorText");
div.textContent = msg.data.info;
},
handleEvent: function(aEvent) {
if (!this.isAboutNetError) {
if (!this.isAboutNetError && !this.isAboutCertError) {
return;
}
@ -332,6 +265,12 @@ var AboutNetErrorListener = {
},
onPageLoad: function(evt) {
if (this.isAboutCertError) {
let originalTarget = evt.originalTarget;
let ownerDoc = originalTarget.ownerDocument;
ClickEventHandler.onCertError(originalTarget, ownerDoc);
}
let automatic = Services.prefs.getBoolPref("security.ssl.errorReporting.automatic");
content.dispatchEvent(new content.CustomEvent("AboutNetErrorOptions", {
detail: JSON.stringify({
@ -375,7 +314,7 @@ var AboutNetErrorListener = {
}
}
AboutNetErrorListener.init(this);
AboutNetAndCertErrorListener.init(this);
var ClickEventHandler = {
@ -398,7 +337,7 @@ var ClickEventHandler = {
// Handle click events from about pages
if (ownerDoc.documentURI.startsWith("about:certerror")) {
this.onAboutCertError(originalTarget, ownerDoc);
this.onCertError(originalTarget, ownerDoc);
return;
} else if (ownerDoc.documentURI.startsWith("about:blocked")) {
this.onAboutBlocked(originalTarget, ownerDoc);
@ -458,7 +397,7 @@ var ClickEventHandler = {
}
},
onAboutCertError: function (targetElement, ownerDoc) {
onCertError: function (targetElement, ownerDoc) {
let docshell = ownerDoc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);

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

@ -22,11 +22,11 @@ add_task(function* () {
let advancedDiv, advancedDivVisibility, technicalDivCollapsed;
yield remote(() => {
let div = content.document.getElementById("advancedPanel");
let div = content.document.getElementById("badCertAdvancedPanel");
// Confirm that the expert section is collapsed
Assert.ok(div, "Advanced content div should exist");
Assert.equal(div.ownerDocument.defaultView.getComputedStyle(div, "").visibility,
"hidden", "Advanced content should not be visible by default");
Assert.equal(div.ownerDocument.defaultView.getComputedStyle(div, "").display,
"none", "Advanced content should not be visible by default");
});
// Tweak the expert mode pref
@ -39,10 +39,10 @@ add_task(function* () {
yield promise;
yield remote(() => {
let div = content.document.getElementById("advancedPanel");
let div = content.document.getElementById("badCertAdvancedPanel");
Assert.ok(div, "Advanced content div should exist");
Assert.equal(div.ownerDocument.defaultView.getComputedStyle(div, "").visibility,
"visible", "Advanced content should be visible by default");
Assert.equal(div.ownerDocument.defaultView.getComputedStyle(div, "").display,
"block", "Advanced content should be visible by default");
});
// Clean up

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

@ -4,7 +4,7 @@
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab("data:text/html,<iframe width='700' height='700' src='about:certerror'></iframe>");
gBrowser.selectedTab = gBrowser.addTab("data:text/html,<iframe width='700' height='700' src='about:certerror?e=nssBadCert&u='></iframe>");
// Open a html page with about:certerror in an iframe
BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser).then(testIframeCert);
}
@ -12,7 +12,7 @@ function test() {
function testIframeCert(e) {
// Confirm that the expert section is hidden
var doc = gBrowser.contentDocument.getElementsByTagName('iframe')[0].contentDocument;
var aP = doc.getElementById("advancedPanel");
var aP = doc.getElementById("badCertAdvancedPanel");
ok(aP, "Advanced content should exist");
is_element_hidden(aP, "Advanced content should not be visible by default")

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

@ -12,14 +12,14 @@ let gWhitelist = [{
file: "search.properties",
key: "searchForSomethingWith",
type: "single-quote"
}, {
file: "aboutCertError.dtd",
key: "certerror.introPara",
type: "single-quote"
}, {
file: "browser.dtd",
key: "social.activated.description",
type: "single-quote"
}, {
file: "netError.dtd",
key: "certerror.introPara",
type: "single-quote"
}, {
file: "netError.dtd",
key: "weakCryptoAdvanced.longDesc",

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

@ -61,8 +61,6 @@ browser.jar:
content/browser/aboutaccounts/images/graphic_sync_intro.png (content/aboutaccounts/images/graphic_sync_intro.png)
content/browser/aboutaccounts/images/graphic_sync_intro@2x.png (content/aboutaccounts/images/graphic_sync_intro@2x.png)
content/browser/certerror/aboutCertError.xhtml (content/aboutcerterror/aboutCertError.xhtml)
content/browser/certerror/aboutCertError.css (content/aboutcerterror/aboutCertError.css)
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)

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

@ -46,7 +46,7 @@ static RedirEntry kRedirMap[] = {
nsIAboutModule::ALLOW_SCRIPT |
nsIAboutModule::HIDE_FROM_ABOUTABOUT },
#endif
{ "certerror", "chrome://browser/content/certerror/aboutCertError.xhtml",
{ "certerror", "chrome://browser/content/aboutNetError.xhtml",
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
nsIAboutModule::ALLOW_SCRIPT |

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

@ -5,6 +5,3 @@
# This file is included by all browser mozconfigs
. "$topsrcdir/build/mozconfig.common"
# Enable Telemetry
export MOZ_TELEMETRY_REPORTING=1

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

@ -11,6 +11,8 @@ ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
# Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
ac_add_options --enable-warnings-as-errors

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

@ -11,6 +11,8 @@ ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
# Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
ac_add_options --enable-warnings-as-errors

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

@ -14,6 +14,8 @@ ac_add_options --with-mozilla-api-keyfile=/builds/mozilla-desktop-geoloc-api.key
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
# Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
ac_add_options --enable-warnings-as-errors

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

@ -25,6 +25,8 @@ ac_add_options --with-mozilla-api-keyfile=/c/builds/mozilla-desktop-geoloc-api.k
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
. $topsrcdir/build/win32/mozconfig.vs2015-win64
# Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).

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

@ -23,6 +23,8 @@ ac_add_options --with-mozilla-api-keyfile=/c/builds/mozilla-desktop-geoloc-api.k
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
# Treat warnings as errors (modulo ALLOW_COMPILER_WARNINGS).
ac_add_options --enable-warnings-as-errors

49
browser/extensions/loop/bootstrap.js поставляемый
Просмотреть файл

@ -13,6 +13,9 @@ const kBrowserSharingNotificationId = "loop-sharing-notification";
const CURSOR_MIN_DELTA = 3;
const CURSOR_MIN_INTERVAL = 100;
const CURSOR_CLICK_DELAY = 1000;
// Due to bug 1051238 frame scripts are cached forever, so we can't update them
// as a restartless add-on. The Math.random() is the work around for this.
const FRAME_SCRIPT = "chrome://loop/content/modules/tabFrame.js?" + Math.random();
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
@ -57,6 +60,7 @@ var WindowListener = {
let FileReader = window.FileReader;
let menuItem = null;
let isSlideshowOpen = false;
let titleChangedListener = null;
// the "exported" symbols
var LoopUI = {
@ -115,6 +119,10 @@ var WindowListener = {
return this._constants;
},
get mm() {
return window.getGroupMessageManager("browsers");
},
/**
* @return {Promise}
*/
@ -313,6 +321,10 @@ var WindowListener = {
return;
}
// Load the frame script into any tab, plus any that get created in the
// future.
this.mm.loadFrameScript(FRAME_SCRIPT, true);
// Cleanup when the window unloads.
window.addEventListener("unload", () => {
Services.obs.removeObserver(this, "loop-status-changed");
@ -522,9 +534,13 @@ var WindowListener = {
gBrowser.tabContainer.addEventListener("TabSelect", this);
this._listeningToTabSelect = true;
titleChangedListener = this.handleDOMTitleChanged.bind(this);
// Watch for title changes as opposed to location changes as more
// metadata about the page is available when this event fires.
gBrowser.addEventListener("DOMTitleChanged", this);
this.mm.addMessageListener("loop@mozilla.org:DOMTitleChanged",
titleChangedListener);
this._browserSharePaused = false;
// Add this event to the parent gBrowser to avoid adding and removing
@ -561,7 +577,12 @@ var WindowListener = {
this._hideBrowserSharingInfoBar();
gBrowser.tabContainer.removeEventListener("TabSelect", this);
gBrowser.removeEventListener("DOMTitleChanged", this);
if (titleChangedListener) {
this.mm.removeMessageListener("loop@mozilla.org:DOMTitleChanged",
titleChangedListener);
titleChangedListener = null;
}
// Remove shared pointers related events
gBrowser.removeEventListener("mousemove", this);
@ -799,15 +820,27 @@ var WindowListener = {
gBrowser.selectedBrowser.outerWindowID);
},
/**
* Handles events from the frame script.
*
* @param {Object} message The message received from the frame script.
*/
handleDOMTitleChanged: function(message) {
if (!this._listeningToTabSelect || this._browserSharePaused) {
return;
}
if (gBrowser.selectedBrowser == message.target) {
// Get the new title of the shared tab
this._notifyBrowserSwitch();
}
},
/**
* Handles events from gBrowser.
*/
handleEvent: function(event) {
switch (event.type) {
case "DOMTitleChanged":
// Get the new title of the shared tab
this._notifyBrowserSwitch();
break;
case "TabSelect":
let wasVisible = false;
// Hide the infobar from the previous tab.
@ -950,6 +983,10 @@ var WindowListener = {
if (window.LoopUI) {
window.LoopUI.removeMenuItem();
// This stops the frame script being loaded to new tabs, but doesn't
// remove it from existing tabs (there's no way to do that).
window.LoopUI.mm.removeDelayedFrameScript(FRAME_SCRIPT);
// XXX Bug 1229352 - Add in tear-down of the panel.
}
},

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

@ -0,0 +1,22 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* global sendAsyncMessage */
/**
* This script runs in the content process and is attached to browsers when
* they are created.
*/
// Listen for when the title is changed and send a message back to the chrome
// process.
addEventListener("DOMTitleChanged", ({ target }) => {
sendAsyncMessage("loop@mozilla.org:DOMTitleChanged", {
details: "titleChanged"
}, {
target: target
});
});

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

@ -218,6 +218,7 @@ body {
/* min-height because there is a browser min-height on panel */
min-height: 53px;
padding-top: 4px;
transition: opacity .2s ease-in-out .2s;
}
.room-list-empty {
@ -259,6 +260,12 @@ body {
margin-bottom: 8px;
}
/* Disable interacting with the room list when creating */
.room-list-pending-creation {
opacity: .5;
pointer-events: none;
}
.room-list > .room-entry > h2 {
display: inline-block;
vertical-align: middle;

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

@ -255,7 +255,7 @@ loop.panel = function (_, mozL10n) {
loop.requestMulti(["GetUserProfile"], ["GetDoNotDisturb"]).then(function (results) {
this.setState({
signedIn: !!results[0],
doNotDisturb: results[2]
doNotDisturb: results[1]
});
}.bind(this));
}
@ -867,7 +867,9 @@ loop.panel = function (_, mozL10n) {
"room-list": true,
// add extra space to last item so when scrolling to bottom,
// last item is not covered by the gradient
"room-list-add-space": this.state.rooms.length && this.state.rooms.length > 5
"room-list-add-space": this.state.rooms.length && this.state.rooms.length > 5,
// Indicate there's a pending action to disable opening more rooms.
"room-list-pending-creation": this.state.pendingCreation
});
if (this.state.error) {

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

@ -221,7 +221,9 @@ describe("loop.panel", function() {
},
locale: "en-US"
});
sandbox.stub(document.mozL10n, "get").returns("Fake title");
sandbox.stub(document.mozL10n, "get", function(stringName) {
return stringName;
});
});
afterEach(function() {
@ -491,6 +493,18 @@ describe("loop.panel", function() {
view = mountTestComponent();
});
it("should show a message to turn notifications off when they are on", function() {
LoopMochaUtils.stubLoopRequest({
GetDoNotDisturb: function() { return false; }
});
view.showDropdownMenu();
var menuitem = view.getDOMNode().querySelector(".entry-settings-notifications");
expect(menuitem.textContent).eql("settings_menu_item_turnnotificationsoff");
});
it("should toggle mozLoop.doNotDisturb to false", function() {
var stub = sinon.stub();
LoopMochaUtils.stubLoopRequest({
@ -505,6 +519,18 @@ describe("loop.panel", function() {
sinon.assert.calledWithExactly(stub, false);
});
it("should show a message to turn notifications on when they are off", function() {
LoopMochaUtils.stubLoopRequest({
GetDoNotDisturb: function() { return true; }
});
view.showDropdownMenu();
var menuitem = view.getDOMNode().querySelector(".entry-settings-notifications");
expect(menuitem.textContent).eql("settings_menu_item_turnnotificationson");
});
it("should toggle mozLoop.doNotDisturb to true", function() {
var stub = sinon.stub();
LoopMochaUtils.stubLoopRequest({
@ -1155,6 +1181,17 @@ describe("loop.panel", function() {
expect(view.getDOMNode().querySelectorAll(".room-list-loading").length).to.eql(1);
});
it("should disable the room list after room creation", function() {
// Simulate that the user has clicked the browse button with other rooms.
var view = createTestComponent();
roomStore.setStoreState({
pendingCreation: true,
rooms: roomList
});
expect(view.getDOMNode().querySelectorAll(".room-list-pending-creation").length).to.eql(1);
});
it("should show multiple rooms in list with no opened room", function() {
roomStore.setStoreState({ rooms: roomList });
@ -1306,7 +1343,7 @@ describe("loop.panel", function() {
var view = createTestComponent();
var node = view.getDOMNode();
expect(node.querySelector(".room-entry h2").textContent).to.equal("Fake title");
expect(node.querySelector(".room-entry h2").textContent).to.equal("room_name_untitled_page");
});
describe("computeAdjustedTopPosition", function() {

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

@ -45,7 +45,7 @@ fte_slide_1_copy=Egal, ob Sie eine Reise planen oder ein Geschenk einkaufen, mit
fte_slide_2_title2=Zum Teilen des Internets entwickelt
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
## will be replaced by the short name 2.
fte_slide_2_copy2=Wenn Sie jetzt einen Freund zu einer Sitzung einladen teilt {{clientShortname2}} automatisch die aktuelle Webseite. Planen. Einkaufen. Entscheiden. Gemeinsam.
fte_slide_2_copy2=Wenn Sie jetzt einen Freund zu einer Sitzung einladen, teilt {{clientShortname2}} automatisch die aktuelle Webseite. Planen. Einkaufen. Entscheiden. Gemeinsam.
fte_slide_3_title=Laden Sie einen Freund ein, indem Sie ihm einen Link senden
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
## will be replaced by the super short brand name.

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

@ -4,11 +4,11 @@
# Panel Strings
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
## use "..." if \u2026 doesn't suit traditions in your locale.
loopMenuItem_label=Aloita keskustelu…
loopMenuItem_accesskey=k
## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
## These are displayed together at the top of the panel when a user is needed to
@ -16,19 +16,29 @@
## and this is displayed in slightly larger font. Please arrange as necessary for
## your locale.
## {{clientShortname2}} will be replaced by the brand name for either string.
sign_in_again_title_line_one=Kirjaudu uudestaan sisään
sign_in_again_title_line_two2=jatkaaksesi {{clientShortname2}}n käyttöä
sign_in_again_button=Kirjaudu
## LOCALIZATION_NOTE(sign_in_again_use_as_guest_button2): {{clientSuperShortname}}
## will be replaced by the super short brandname.
sign_in_again_use_as_guest_button2=Käytä {{clientSuperShortname}}-palvelua vierastunnuksilla
panel_browse_with_friend_button=Selaa sivua kaverin kanssa
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
## user to create his or her first conversation.
first_time_experience_subheading2=Napsauta Hello-painiketta selataksesi verkkosivuja kaverin kanssa.
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
## ways to use Hello project.
first_time_experience_content=Käytä sitä suunnittelemaan, työskentelemään tai nauramaan yhdessä.
first_time_experience_button_label2=Katso miten se toimii
## First Time Experience Slides
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
## will be replaced by the short name 2.
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
## will be replaced by the short name 2.
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
## will be replaced by the super short brand name.
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
@ -36,35 +46,72 @@
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
## will be replaced by the brand short name.
invite_header_text_bold2=Kutsu kaveri mukaasi!
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
## invite_email_link_button, invite_facebook_button2): These labels appear under
## an iconic button for the invite view.
invite_copy_link_button=Kopioi linkki
invite_copied_link_button=Kopioitu!
invite_email_link_button=Lähetä linkki
invite_facebook_button3=Facebook
invite_your_link=Linkkisi:
# Error bars
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
## These may be displayed at the top of the panel.
session_expired_error_description=Istunto vanhentunut. Kaikki aikaisemmin luodut ja jaetut linkit eivät enää toimi.
could_not_authenticate=Todentaminen ei onnistunut
password_changed_question=Oletko vaihtanut salasanaa?
try_again_later=Yritä uudestaan myöhemmin
could_not_connect=Ei voitu muodostaa yhteyttä palvelimeen
check_internet_connection=Tarkista Internet-yhteytesi
login_expired=Kirjautumisesi on vanhentunut
service_not_available=Palvelu ei ole saatavilla tällä hetkellä
problem_accessing_account=Pääsyssä tilillesi oli ongelma
## LOCALIZATION NOTE(retry_button): Displayed when there is an error to retry
## the appropriate action.
retry_button=Yritä uudestaan
share_email_subject7=Sinut on kutsuttu selaamaan verkkoa yhdessä
## LOCALIZATION NOTE (share_email_body7): In this item, don't translate the
## part between {{..}} and leave the \n\n part alone
share_email_body7=Kaveri odottaa sinua Firefox Hellossa. Napsauta linkkiä liittyäksesi selaamaan verkkoa yhdessä: {{callUrl}}
## LOCALIZATION NOTE (share_email_body_context3): In this item, don't translate
## the part between {{..}} and leave the \n\n part alone.
share_email_body_context3=Kaveri odottaa sinua Firefox Hellossa. Napsauta linkkiä liittyäksesi selaamaan sivustoa {{title}} yhdessä: {{callUrl}}
## LOCALIZATION NOTE (share_email_footer2): Common footer content for both email types
share_email_footer2=\n\n____________\nFirefox Hello mahdollistaa verkon selaamisen kavereiden kanssa. Käytä sitä, kun haluat saada hommat hoidettua: suunnittele, työskentele ja naura yhdessä. Lue lisää osoitteesta http://www.firefox.com/hello
## LOCALIZATION NOTE (share_tweeet): In this item, don't translate the part
## between {{..}}. Please keep the text below 117 characters to make sure it fits
## in a tweet.
share_tweet=Aloita kanssani {{clientShortname2}}-pohjainen videokeskustelu!
share_add_service_button=Lisää palvelu
## LOCALIZATION NOTE (copy_link_menuitem, email_link_menuitem, delete_conversation_menuitem):
## These menu items are displayed from a panel's context menu for a conversation.
copy_link_menuitem=Kopioi linkki
email_link_menuitem=Lähetä linkki
delete_conversation_menuitem2=Poista
panel_footer_signin_or_signup_link=Kirjaudu tai rekisteröidy
settings_menu_item_account=Tili
settings_menu_item_settings=Asetukset
settings_menu_item_signout=Kirjaudu ulos
settings_menu_item_signin=Kirjaudu sisään
settings_menu_item_turnnotificationson=Ota huomautukset käyttöön
settings_menu_item_turnnotificationsoff=Poista huomautukset käytöstä
settings_menu_item_feedback=Lähetä palautetta
settings_menu_button_tooltip=Asetukset
# Conversation Window Strings
initiate_call_button_label2=Oletko valmis aloittamaan keskustelun?
incoming_call_title2=Keskustelupyyntö
incoming_call_block_button=Estä
hangup_button_title=Katkaise
hangup_button_caption2=Lopeta
@ -72,45 +119,81 @@ hangup_button_caption2=Lopeta
## LOCALIZATION NOTE (call_with_contact_title): The title displayed
## when calling a contact. Don't translate the part between {{..}} because
## this will be replaced by the contact's name.
call_with_contact_title=Keskustelussa {{contactName}}
# Outgoing conversation
outgoing_call_title=Aloitetaanko keskustelu?
initiate_audio_video_call_button2=Aloita
initiate_audio_video_call_tooltip2=Aloita videokeskustelu
initiate_audio_call_button2=Äänikeskustelu
peer_ended_conversation2=Keskustelukumppani päätti keskustelun.
restart_call=Liity uudestaan
## LOCALIZATION NOTE (contact_offline_title): Title which is displayed when the
## contact is offline.
contact_offline_title=Tämä henkilö ei ole verkkoyhteyden päässä
## LOCALIZATION NOTE (call_timeout_notification_text): Title which is displayed
## when the call didn't go through.
call_timeout_notification_text=Puhelusi ei mennyt läpi.
## LOCALIZATION NOTE (cancel_button):
## This button is displayed when a call has failed.
cancel_button=Peruuta
rejoin_button=Liity uudestaan keskusteluun
cannot_start_call_session_not_ready=Ei voida soittaa, istunto ei ole valmis.
network_disconnected=Verkkoyhteys katkesi yllättäen.
connection_error_see_console_notification=Puhelu epäonnistui; katso lisätietoja konsolista.
no_media_failure_message=Ei löytynyt kameraa tai mikrofonia.
ice_failure_message=Yhteys epäonnistui. Palomuurisi voi estää puhelut.
## LOCALIZATION NOTE (legal_text_and_links3): In this item, don't translate the
## parts between {{..}} because these will be replaced with links with the labels
## from legal_text_tos and legal_text_privacy. clientShortname will be replaced
## by the brand name.
legal_text_and_links3=Käyttämällä {{clientShortname}} -palvelua hyväksyt sen {{terms_of_use}} ja {{privacy_notice}}.
legal_text_tos=käyttöehdot
legal_text_privacy=tietosuojakäytännön
## LOCALIZATION NOTE (powered_by_beforeLogo, powered_by_afterLogo):
## These 2 strings are displayed before and after a 'Telefonica'
## logo.
powered_by_beforeLogo=Perustuu
powered_by_afterLogo=teknologioihin
## LOCALIZATION_NOTE (feedback_rejoin_button): Displayed on the feedback form after
## a signed-in to signed-in user call.
feedback_rejoin_button=Liity uudestaan
## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
## an abusive user.
feedback_report_user_button=Ilmoita käyttäjä
feedback_window_heading=Miten keskustelu sujui?
feedback_request_button=Anna palautetta
tour_label=Esittely
rooms_list_recently_browsed2=Viimeiset sivustot
rooms_list_currently_browsing2=Avoimet sivut
rooms_signout_alert=Avoimet keskustelut suljetaan
room_name_untitled_page=Nimetön sivu
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
door_hanger_return=Nähdään myöhemmin! Voit palata tähän jaettuun istuntoon milloin tahansa Hello-paneelin kautta.
door_hanger_prompt_name=Haluatko antaa sille nimen, joka on helpompi muistaa? Nykyinen nimi:
door_hanger_button=OK
# Infobar strings
infobar_screenshare_browser_message2=Jaat parhaillaan välilehtiäsi. Kaverisi näkevät kaikki välilehdet, joita napsautat
infobar_button_stop_label2=Lopeta jakaminen
infobar_button_stop_accesskey=L
# E10s not supported strings
e10s_not_supported_button_label=Avaa uusi ikkuna
e10s_not_supported_subheading={{brandShortname}} ei toimi useampaa prosessia hyödyntävässä ikkunassa.
# 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/.
@ -118,12 +201,14 @@ door_hanger_button=OK
## LOCALIZATION NOTE: In this file, don't translate the part between {{..}}
# Text chat strings
chat_textbox_placeholder=Kirjoita tähän…
## LOCALIZATION NOTE(clientShortname2): This should not be localized and
## should remain "Firefox Hello" for all locales.
clientShortname2=Firefox Hello
conversation_has_ended=Keskustelu on päättynyt.
generic_failure_message=Palvelulla on tällä hetkellä teknisiä vaikeuksia…
generic_failure_no_reason2=Yritetäänkö uudestaan?
@ -131,6 +216,8 @@ help_label=Lähetä
mute_local_audio_button_title=Mykistä ääni
unmute_local_audio_button_title=Palauta ääni
mute_local_video_button_title2=Piilota video
unmute_local_video_button_title2=Näytä video
## LOCALIZATION NOTE (retry_call_button):
## This button is displayed when a call has failed.
@ -144,12 +231,15 @@ rooms_room_full_call_to_action_label=Lue lisää {{clientShortname}}ista »
rooms_room_full_call_to_action_nonFx_label=Lataa {{brandShortname}} aloittaaksesi oman keskustelun
rooms_room_full_label=Keskustelussa on jo kaksi henkilöä paikalla.
rooms_room_join_label=Liity keskusteluun
rooms_room_joined_label=Joku on liittynyt keskusteluun!
self_view_hidden_message=Omanäkymä piilotettu, mutta lähetetään edelleen. Muuta ikkunan kokoa nähdäksesi.
## LOCALIZATION NOTE (tos_failure_message): Don't translate {{clientShortname}}
## as this will be replaced by clientShortname2.
tos_failure_message={{clientShortname}} ei ole saatavilla maassasi.
display_name_guest=Vieras
## LOCALIZATION NOTE(clientSuperShortname): This should not be localized and
## should remain "Hello" for all locales.

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

@ -46,7 +46,7 @@ fte_slide_2_title2=Realizzato per condividere il Web
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
## will be replaced by the short name 2.
fte_slide_2_copy2=Adesso ogni volta che inviti un utente a una sessione di chat, {clientShortname2} condividerà automaticamente la pagina web che stai visitando. Organizza. Fai acquisti. Decidi. In compagnia.
fte_slide_3_title=Invita un link a un altro utente per invitarlo
fte_slide_3_title=Invia un link a un altro utente per invitarlo
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
## will be replaced by the super short brand name.
fte_slide_3_copy={{clientSuperShortname}} è compatibile con la maggior parte dei browser per desktop. Il servizio è gratuito e non richiede la registrazione di alcun account.

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

@ -29,6 +29,7 @@ panel_disconnect_button=Deconnectar
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
## user to create his or her first conversation.
first_time_experience_subheading2=Clicca sin il buttun da Hello per navigar cun in ami en il web.
first_time_experience_subheading_button_above=Clicca sin il buttun survart per navigar cun in ami en il web.
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
## ways to use Hello project.
@ -36,17 +37,27 @@ first_time_experience_content=Fa diever da la funcziun per planisar ensemen, lav
first_time_experience_button_label2=Mussar co che quai funcziunescha
## First Time Experience Slides
fte_slide_1_title=Navighescha cun in(a) ami(a) sin paginas web
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
## will be replaced by the short name 2.
fte_slide_1_copy=Sche ti vul planisar en dus in viadi u la cumpra dad in regal, ta gida {{clientShortname2}} da prender pli svelt ina decisiun en temp real.
fte_slide_2_title2=Fatg per cundivider il web
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
## will be replaced by the short name 2.
fte_slide_2_copy2=Sche ti envidas ussa in(a) ami(a) per ina sesida, cundivida {{clientShortname2}} automaticamain mintga pagina d'internet che ti visitas. Planisai. Cumprai. Decidai. En dus.
fte_slide_3_title=Envida in(a) ami(a) cun trametter ina colliaziun
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
## will be replaced by the super short brand name.
fte_slide_3_copy={{clientSuperShortname}} funcziuna cun la gronda part dals navigaturs per computers classics. I na dovra nagin conto e la connexiun è gratuita.
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
## will be replaced by the super short brand name.
fte_slide_4_title=Cun agid da l'icona da {{clientSuperShortname}} cumenzi
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
## will be replaced by the brand short name.
fte_slide_4_copy=Cura che ti chattas ina pagina che ti vuls discutar, clicca sin l'icona da {{brandShortname}} per crear ina colliaziun. Lura la pos ti trametter - en ina moda u l'autra - a tia amia u tes ami!
invite_header_text_bold2=Envida in ami a participar!
invite_header_text4=Cundivida questa colliaziun per che vus possias cumenzar a navigar en dus tras il web.
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
## invite_email_link_button, invite_facebook_button2): These labels appear under
## an iconic button for the invite view.
@ -93,6 +104,7 @@ share_add_service_button=Agiuntar in servetsch
## These menu items are displayed from a panel's context menu for a conversation.
copy_link_menuitem=Copiar la colliaziun
email_link_menuitem=Trametter la colliaziun via e-mail
edit_name_menuitem=Modifitgar il num
delete_conversation_menuitem2=Stizzar
panel_footer_signin_or_signup_link=S'annunziar u sa registrar
@ -187,7 +199,9 @@ door_hanger_button=OK
# Infobar strings
infobar_screenshare_no_guest_message=Uschespert che tia amia u tes ami è da la partida, po el(la) vesair mintga tab sin il qual ti cliccas.
infobar_screenshare_browser_message2=Ti cundividas tes tabs. Mintga tab che ti avras vesan era tes amis
infobar_screenshare_browser_message3=Ti cundividas ussa tes tabs. Tia amia u tes ami vesan ussa mintga tab sin il qual ti cliccas.
infobar_screenshare_stop_sharing_message=Ti na cundividas betg pli tes tabs
infobar_button_restart_label2=Puspè cundivider
infobar_button_restart_accesskey=e

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

@ -4,10 +4,11 @@
# Panel Strings
## LOCALIZATION_NOTE(loopMenuItem_label): Label of the menu item that is placed
## inside the browser 'Tools' menu. Use the unicode ellipsis char, \u2026, or
## use "..." if \u2026 doesn't suit traditions in your locale.
loopMenuItem_label=ఒక సంభాషణను ప్రారంభించేందుకు...
loopMenuItem_accesskey=టీ
## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
## These are displayed together at the top of the panel when a user is needed to
@ -15,29 +16,70 @@
## and this is displayed in slightly larger font. Please arrange as necessary for
## your locale.
## {{clientShortname2}} will be replaced by the brand name for either string.
sign_in_again_title_line_one=మళ్ళీ సైన్ ఇన్ చేయండి
sign_in_again_title_line_two2=ఉపయోగించడం కొనసాగించాలని{{clientShortname2}}
sign_in_again_button=సైన్ ఇన్
## LOCALIZATION_NOTE(sign_in_again_use_as_guest_button2): {{clientSuperShortname}}
## will be replaced by the super short brandname.
sign_in_again_use_as_guest_button2=వా డు{{clientSuperShortname}}ఒక అతిథి వలె
panel_browse_with_friend_button=ఒక స్నేహితుడు ఈ పేజీ బ్రౌజ్
panel_disconnect_button=డిస్కనెక్ట్
## LOCALIZATION_NOTE(first_time_experience_subheading2): Message inviting the
## LOCALIZATION_NOTE(first_time_experience_subheading2, first_time_experience_subheading_button_above): Message inviting the
## user to create his or her first conversation.
first_time_experience_subheading2=ఒక స్నేహితుడు తో వెబ్ పేజీలను బ్రౌజ్ హలో బటన్ క్లిక్ చేయండి.
first_time_experience_subheading_button_above=బటన్ పైన స్నేహితునితో వెబ్ పేజీలను బ్రౌజ్ చేయండి.
## LOCALIZATION_NOTE(first_time_experience_content): Message describing
## LOCALIZATION_NOTE(first_time_experience_content, first_time_experience_content2): Message describing
## ways to use Hello project.
first_time_experience_content=కలిసి పని, కలిసి ప్లాన్ దాన్ని ఉపయోగించండి, కలిసి నవ్వు.
first_time_experience_content2=పనులు పూర్తి చేయడానికి దీనిని ఉపయోగిస్తారు: కలిసి నవ్వు, కలిసి ప్లాన్ కలిసి పని.
first_time_experience_button_label2=ఎలా పనిచేస్తుందో చూడండి
## First Time Experience Slides
fte_slide_1_title=ఒక స్నేహితుడు ఈ పేజీ బ్రౌజ్
## LOCALIZATION_NOTE(fte_slide_1_copy): {{clientShortname2}}
## will be replaced by the short name 2.
fte_slide_2_title2=వెబ్ భాగస్వామ్యం కోసం మేడ్
## LOCALIZATION_NOTE(fte_slide_2_copy2): {{clientShortname2}}
## will be replaced by the short name 2.
fte_slide_3_title=ఒక లింక్ పంపడం మిత్రులని ఆహ్వానించండి
## LOCALIZATION_NOTE(fte_slide_3_copy): {{clientSuperShortname}}
## will be replaced by the super short brand name.
## LOCALIZATION_NOTE(fte_slide_4_title): {{clientSuperShortname}}
## will be replaced by the super short brand name.
## LOCALIZATION_NOTE(fte_slide_4_copy): {{brandShortname}}
## will be replaced by the brand short name.
invite_header_text_bold2=మీరు చేరడానికి కు ఆహ్వానించండి !
invite_header_text4=కాబట్టి మీరు కలిసి వెబ్ బ్రౌజింగ్ ప్రారంభించవచ్చు లింక్ను భాగస్వామ్యం చేయండి.
## LOCALIZATION_NOTE(invite_copy_link_button, invite_copied_link_button,
## invite_email_link_button, invite_facebook_button2): These labels appear under
## an iconic button for the invite view.
# Status text
invite_copy_link_button=లంకె నకలుతీయి
invite_copied_link_button=నకలుతీసెను!
invite_email_link_button=లంకెను ఈమెయిలు చెయ్యి
invite_facebook_button3=ఫేస్‌బుక్
invite_your_link=మీ లంకె:
# Error bars
## LOCALIZATION NOTE(session_expired_error_description,could_not_authenticate,password_changed_question,try_again_later,could_not_connect,check_internet_connection,login_expired,service_not_available,problem_accessing_account):
## These may be displayed at the top of the panel.
could_not_authenticate=ధృవీకరించలేక పోయింది
password_changed_question=మీరు మీ సంకేతపదం మార్చారా?
try_again_later=దయచేసి తరువాత ప్రయత్నించు
could_not_connect=సేవికకు అనుసంధానము కాలేకపోయింది
check_internet_connection=మీ అంతర్జాలం అనుసంధానం పరీక్షించండి
login_expired=మీ లాగిన్ గడువుతీరినది
service_not_available=ఈ సమయంలో సేవ అందుబాటులోలేదు
problem_accessing_account=మీ ఖాతాను ఏక్సెస్ చేయుటలో సమస్యవుంది
## LOCALIZATION NOTE(retry_button): Displayed when there is an error to retry
## the appropriate action.
retry_button=పునఃప్రయత్నించండి
share_email_subject7=జాలంలో కలిసి విహరించడానికి ఆహ్వానం
## LOCALIZATION NOTE (share_email_body7): In this item, don't translate the
## part between {{..}} and leave the \n\n part alone
## LOCALIZATION NOTE (share_email_body_context3): In this item, don't translate
@ -47,78 +89,102 @@
## between {{..}}. Please keep the text below 117 characters to make sure it fits
## in a tweet.
share_add_service_button=సేవను చేర్చు
## LOCALIZATION NOTE (copy_link_menuitem, email_link_menuitem, delete_conversation_menuitem):
## These menu items are displayed from a panel's context menu for a conversation.
copy_link_menuitem=లంకె నకలుతీయి
email_link_menuitem=లంకెను ఈమెయిలు చెయ్యి
edit_name_menuitem=పేరుని సంకలనం చెయ్యి
delete_conversation_menuitem2=తొలగించు
panel_footer_signin_or_signup_link=సైన్ ఇన్ లేదా సైన్ అప్
settings_menu_item_account=ఖాతా
settings_menu_item_settings=సెట్టింగ్‌లు
settings_menu_item_signout=నిష్క్రమించు
settings_menu_item_signin=ప్రవేశించు
settings_menu_item_turnnotificationson=గమనింపులను చేతనించు
settings_menu_item_turnnotificationsoff=గమనింపులను అచేతనించు
settings_menu_item_feedback=ప్రతిస్పందనను తెలియజేయండి
settings_menu_button_tooltip=సెట్టింగ్‌లు
# Conversation Window Strings
initiate_call_button_label2=Ready to start your conversation?
hangup_button_title=Hang up
hangup_button_caption2=Exit
initiate_call_button_label2=మీ సంభాషణ ప్రారంభించుటకు సిద్దమేనా?
incoming_call_title2=సంభాషణ అభ్యర్దన
incoming_call_block_button=అడ్డగించు
hangup_button_title=పెట్టెయ్యండి
hangup_button_caption2=నిష్క్రమణ
## LOCALIZATION NOTE (call_with_contact_title): The title displayed
## when calling a contact. Don't translate the part between {{..}} because
## this will be replaced by the contact's name.
call_with_contact_title=Conversation with {{incomingCallIdentity}}
call_with_contact_title={{contactName}} తో సంభాషణ
# Outgoing conversation
outgoing_call_title=Start conversation?
initiate_audio_video_call_button2=Start
initiate_audio_video_call_tooltip2=Start a video conversation
initiate_audio_call_button2=Voice conversation
outgoing_call_title=సంభాషణ ప్రారంభించాలా?
initiate_audio_video_call_button2=ప్రారంభం
initiate_audio_video_call_tooltip2=వీడియో సంభాషణ ప్రారంభించుము
initiate_audio_call_button2=మాట సంభాషణ
peer_ended_conversation2=The person you were calling has ended the conversation.
restart_call=Rejoin
peer_ended_conversation2=మీ కాల్‌చేసిన వ్యక్తి సంభాషణను ముగించారు.
restart_call=తిరిగిచేరు
## LOCALIZATION NOTE (contact_offline_title): Title which is displayed when the
## contact is offline.
contact_offline_title=ఈ వ్యక్తి ఆన్‌లైన్ నందు లేరు
## LOCALIZATION NOTE (call_timeout_notification_text): Title which is displayed
## when the call didn't go through.
call_timeout_notification_text=Your call did not go through.
call_timeout_notification_text=మీ కాల్ వెళ్ళలేదు.
## LOCALIZATION NOTE (cancel_button):
## This button is displayed when a call has failed.
cancel_button=రద్దుచేయి
rejoin_button=మళ్ళీ సంభాషణలో చేరండి
network_disconnected=The network connection terminated abruptly.
connection_error_see_console_notification=Call failed; see console for details.
cannot_start_call_session_not_ready=కాల్ ప్రారంభించలేదు, సెషన్ సిద్ధంగా లేదు.
network_disconnected=అకస్మాత్తుగా నెట్వర్క్ అనుసంధానం పోయింది.
connection_error_see_console_notification=కాల్ విఫలమైంది; వివరాలకు కన్సోల్ చూడు.
no_media_failure_message=కేమెరా లేదా మైక్రోఫోన్ కనబడలేదు.
ice_failure_message=అనుసంధానం విఫలమయ్యింది. మీ ఫైర్‌వాల్ కాల్సుని నిరోధిస్తూండవచ్చు.
## LOCALIZATION NOTE (legal_text_and_links3): In this item, don't translate the
## parts between {{..}} because these will be replaced with links with the labels
## from legal_text_tos and legal_text_privacy. clientShortname will be replaced
## by the brand name.
legal_text_tos=వినియోగ నియమాలు
legal_text_privacy=గోప్యతా విధానం
## LOCALIZATION NOTE (powered_by_beforeLogo, powered_by_afterLogo):
## These 2 strings are displayed before and after a 'Telefonica'
## logo.
powered_by_beforeLogo=వీరి సహకారంతో
powered_by_afterLogo=
## LOCALIZATION_NOTE (feedback_rejoin_button): Displayed on the feedback form after
## a signed-in to signed-in user call.
feedback_rejoin_button=Rejoin
feedback_rejoin_button=తిరిగిచేరు
## LOCALIZATION NOTE (feedback_report_user_button): Used to report a user in the case of
## an abusive user.
feedback_report_user_button=Report User
feedback_report_user_button=వాడుకరిని నివేదించు
feedback_window_heading=మీ సంభాషణ ఎలావుంది?
feedback_request_button=ప్రతిస్పందన తెలియజేయండి
tour_label=Tour
tour_label=ప్రదర్శన
rooms_list_recently_browsed2=ఇటీవల చూసినవి
rooms_list_currently_browsing2=ప్రస్తుతం చూస్తున్నవి
rooms_signout_alert=తెరిచిన సంభాషణలు మూయబడతాయి
## LOCALIZATION NOTE (door_hanger_return, door_hanger_prompt_name, door_hanger_button): Dialog message on leaving conversation
# Infobar strings
# Context in conversation strings
## LOCALIZATION NOTE(no_conversations_message_heading2): Title shown when user
## has no conversations available.
## LOCALIZATION NOTE(no_conversations_start_message2): Subheading inviting the
## user to start a new conversation.
# E10s not supported strings
# This Source Code Form is subject to the terms of the Mozilla Public

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

@ -15,4 +15,5 @@ support-files =
[browser_mozLoop_socialShare.js]
[browser_mozLoop_sharingListeners.js]
[browser_mozLoop_telemetry.js]
[browser_sharingTitleListeners.js]
[browser_toolbarbutton.js]

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

@ -0,0 +1,51 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* This file contains tests for the browser sharing document title listeners.
*/
"use strict";
var [, gHandlers] = LoopAPI.inspect();
function promiseBrowserSwitch() {
return new Promise(resolve => {
LoopAPI.stub([{
sendAsyncMessage: function(messageName, data) {
if (data[0] == "BrowserSwitch") {
LoopAPI.restore();
resolve();
}
}
}]);
});
}
add_task(function* setup() {
Services.prefs.setBoolPref("loop.remote.autostart", true);
gHandlers.AddBrowserSharingListener({ data: [42] }, () => {});
let newTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:blank", true);
registerCleanupFunction(function* () {
// Remove the listener.
gHandlers.RemoveBrowserSharingListener({ data: [42] }, function() {});
yield BrowserTestUtils.removeTab(newTab);
Services.prefs.clearUserPref("loop.remote.autostart");
});
});
add_task(function* test_notifyOnTitleChanged() {
// Hook up the async listener and wait for the BrowserSwitch to happen.
let browserSwitchPromise = promiseBrowserSwitch();
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, "about:mozilla");
// Now check we get the notification of the browser switch.
yield browserSwitchPromise;
Assert.ok(true, "We got notification of the browser switch");
});

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

@ -9,7 +9,7 @@
<Description about="urn:mozilla:install-manifest">
<em:id>loop@mozilla.org</em:id>
<em:bootstrap>true</em:bootstrap>
<em:version>1.2.4</em:version>
<em:version>1.2.6</em:version>
<em:type>2</em:type>
<!-- Target Application this extension can install into,
@ -32,7 +32,7 @@
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Firefox Hello Beta</em:name>
<em:name>Firefox Hello</em:name>
<em:description>Web sharing for Firefox</em:description>
<em:creator>Mozilla</em:creator>
</Description>

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

@ -1,38 +0,0 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY % brandDTD
SYSTEM "chrome://branding/locale/brand.dtd">
%brandDTD;
<!-- These strings are used by Firefox's custom about:certerror page,
a replacement for the standard security certificate errors produced
by NSS/PSM via netError.xhtml. -->
<!ENTITY certerror.pagetitle1 "Insecure Connection">
<!ENTITY certerror.longpagetitle1 "Your connection is not secure">
<!-- Localization note (certerror.introPara) - The text content of the span tag
will be replaced at runtime with the name of the server to which the user
was trying to connect. -->
<!ENTITY certerror.introPara "The owner of <span class='hostname'/> has configured their website improperly. To protect your information from being stolen, &brandShortName; has not connected to this website.">
<!ENTITY certerror.returnToPreviousPage.label "Go Back">
<!ENTITY certerror.learnMore "Learn more…">
<!ENTITY certerror.advanced.label "Advanced">
<!ENTITY certerror.whatShouldIDo.badStsCertExplanation "This site uses HTTP
Strict Transport Security (HSTS) to specify that &brandShortName; only connect
to it securely. As a result, it is not possible to add an exception for this
certificate.">
<!ENTITY certerror.expert.content "If you understand whats going on, you
can tell &brandShortName; to start trusting this sites identification.
<b>Even if you trust the site, this error could mean that someone is
tampering with your connection.</b>">
<!ENTITY certerror.expert.contentPara2 "Dont add an exception unless
you know theres a good reason why this site doesnt use trusted identification.">
<!ENTITY certerror.addException.label "Add Exception…">
<!ENTITY certerror.copyToClipboard.label "Copy text to clipboard">
<!ENTITY errorReporting.automatic "Report errors like this to help Mozilla identify misconfigured sites">

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

@ -139,15 +139,11 @@
</ul>
">
<!ENTITY nssBadCert.title "Secure Connection Failed">
<!ENTITY nssBadCert.longDesc2 "
<ul>
<li>This could be a problem with the servers configuration, or it could be
someone trying to impersonate the server.</li>
<li>If you have connected to this server successfully in the past, the error may
be temporary, and you can try again later.</li>
</ul>
">
<!ENTITY certerror.longpagetitle1 "Your connection is not secure">
<!-- Localization note (certerror.introPara) - The text content of the span tag
will be replaced at runtime with the name of the server to which the user
was trying to connect. -->
<!ENTITY certerror.introPara "The owner of <span class='hostname'/> has configured their website improperly. To protect your information from being stolen, &brandShortName; has not connected to this website.">
<!ENTITY sharedLongDesc "
<ul>
@ -167,22 +163,8 @@ be temporary, and you can try again later.</li>
<!ENTITY corruptedContentError.longDesc "<p>The page you are trying to view cannot be shown because an error in the data transmission was detected.</p><ul><li>Please contact the website owners to inform them of this problem.</li></ul>">
<!ENTITY securityOverride.linkText "Or you can add an exception…">
<!ENTITY securityOverride.getMeOutOfHereButton "Get me out of here!">
<!ENTITY securityOverride.exceptionButtonLabel "Add Exception…">
<!-- LOCALIZATION NOTE (securityOverride.warningContent) - Do not translate the
contents of the <button> tags. It uses strings already defined above. The
button is included here (instead of netError.xhtml) because it exposes
functionality specific to firefox. -->
<!ENTITY securityOverride.warningContent "
<p>You should not add an exception if you are using an internet connection that you do not trust completely or if you are not used to seeing a warning for this server.</p>
<button id='getMeOutOfHereButton'>&securityOverride.getMeOutOfHereButton;</button>
<button id='exceptionDialogButton'>&securityOverride.exceptionButtonLabel;</button>
">
<!ENTITY errorReporting.automatic2 "Report errors like this to help Mozilla identify and block malicious sites">
<!ENTITY errorReporting.learnMore "Learn more…">
@ -201,3 +183,10 @@ functionality specific to firefox. -->
<!ENTITY weakCryptoAdvanced.title "Advanced">
<!ENTITY weakCryptoAdvanced.longDesc "<span class='hostname'></span> uses security technology that is outdated and vulnerable to attack. An attacker could easily reveal information which you thought to be safe.">
<!ENTITY weakCryptoAdvanced.override "(Not secure) Try loading <span class='hostname'></span> using outdated security">
<!ENTITY certerror.pagetitle1 "Insecure Connection">
<!ENTITY certerror.whatShouldIDo.badStsCertExplanation "This site uses HTTP
Strict Transport Security (HSTS) to specify that &brandShortName; only connect
to it securely. As a result, it is not possible to add an exception for this
certificate.">
<!ENTITY certerror.copyToClipboard.label "Copy text to clipboard">

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

@ -8,7 +8,6 @@
% locale browser @AB_CD@ %locale/browser/
* locale/browser/bookmarks.html (generic/profile/bookmarks.html.in)
locale/browser/aboutAccounts.dtd (%chrome/browser/aboutAccounts.dtd)
locale/browser/aboutCertError.dtd (%chrome/browser/aboutCertError.dtd)
locale/browser/aboutDialog.dtd (%chrome/browser/aboutDialog.dtd)
locale/browser/aboutPrivateBrowsing.dtd (%chrome/browser/aboutPrivateBrowsing.dtd)
locale/browser/aboutPrivateBrowsing.properties (%chrome/browser/aboutPrivateBrowsing.properties)

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

@ -1,119 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@import url("chrome://global/skin/in-content/common.css");
body {
display: flex;
box-sizing: border-box;
min-height: 100vh;
padding: 0 48px;
align-items: center;
justify-content: center;
}
#errorPageContainer {
position: relative;
min-width: 320px;
max-width: 512px;
}
#errorTitle {
background: url("chrome://browser/skin/cert-error.svg") left 0 no-repeat;
background-size: 3em;
margin-inline-start: -5em;
padding-inline-start: 5em;
}
#errorTitle:-moz-dir(rtl) {
background-position: right 0;
}
#errorTitleText {
border-bottom: 1px solid #C1C1C1;
padding-bottom: 0.4em;
}
@media (max-width: 675px) {
#errorTitle {
padding-top: 0;
background-image: none;
margin-inline-start: 0;
padding-inline-start: 0;
}
}
#buttonContainer {
display: flex;
flex-flow: row wrap;
}
#buttonSpacer {
flex: 1;
}
#certificateErrorDebugInformation {
display: none;
background-color: var(--in-content-box-background-hover) !important;
border-top: 1px solid var(--in-content-border-color);
position: absolute;
left: 0%;
top: 100%;
width: 65%;
padding: 1em 17.5%;
}
#certificateErrorText {
font-family: monospace;
white-space: pre-wrap;
padding: 1em 0;
}
#errorCode {
white-space: nowrap;
}
#returnButton {
background-color: var(--in-content-primary-button-background);
border: none;
color: var(--in-content-selected-text);
min-width: 250px;
margin-inline-start: 0;
}
#returnButton:hover {
background-color: var(--in-content-primary-button-background-hover) !important;
}
#returnButton:hover:active {
background-color: var(--in-content-primary-button-background-active) !important;
}
#advancedButton {
min-width: 150px;
}
/* Advanced section is hidden via inline styles until the link is clicked */
#advancedPanel {
background-color: white;
color: var(--in-content-text-color);
border: 1px lightgray solid;
/* Don't use top padding because the default p style has top padding, and it
* makes the overall div look uneven */
padding: 0 12px 10px;
margin-top: 10px;
box-shadow: 0 0 4px #ddd;
font-size: 0.9em;
}
.hostname {
font-weight: bold;
}
#reportCertificateErrorRetry,
#certificateErrorReporting,
#reportSendingMessage,
#reportSentMessage {
display: none;
}

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

@ -40,6 +40,10 @@ ul {
-moz-padding-start: 5em;
}
body.certerror #errorTitle {
background-image: url("chrome://browser/skin/cert-error.svg");
}
#errorTitleText {
border-bottom: 1px solid #C1C1C1;
padding-bottom: 0.4em;
@ -115,36 +119,12 @@ button:disabled {
min-width: 150px;
}
#certificateErrorReporting,
#reportSentMessage {
#certificateErrorReporting {
display: none;
}
div#weakCryptoAdvanced {
display: none;
float: right;
/* Align with the "Try Again" button */
margin-top: 24px;
-moz-margin-end: 24px;
}
div#weakCryptoAdvanced a {
text-decoration: none;
}
div#weakCryptoAdvanced a:hover {
text-decoration: underline;
}
span.downArrow {
display: inline-block;
vertical-align: middle;
font-size: 0.6em;
-moz-margin-start: 0.5em;
transform: scaleY(0.7);
}
div#weakCryptoAdvancedPanel {
#weakCryptoAdvancedPanel,
#badCertAdvancedPanel {
/* Hidden until the link is clicked */
display: none;
background-color: white;
@ -154,6 +134,7 @@ div#weakCryptoAdvancedPanel {
padding: 0 12px 12px 12px;
box-shadow: 0 0 4px #ddd;
font-size: 0.9em;
margin-top: 24px;
}
#overrideWeakCryptoPanel {
@ -174,8 +155,38 @@ span#hostname {
cursor: pointer;
}
#errorCode {
body:not(.certerror) #errorCode {
color: var(--in-content-page-color);
cursor: text;
text-decoration: none;
}
body.certerror #errorCode {
white-space: nowrap;
}
#badCertTechnicalInfo {
overflow: auto;
white-space: pre-wrap;
}
#certificateErrorReporting {
display: none;
}
#certificateErrorDebugInformation {
display: none;
background-color: var(--in-content-box-background-hover) !important;
border-top: 1px solid var(--in-content-border-color);
position: absolute;
left: 0%;
top: 100%;
width: 65%;
padding: 1em 17.5%;
}
#certificateErrorText {
font-family: monospace;
white-space: pre-wrap;
padding: 1em 0;
}

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

@ -675,8 +675,15 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
list-style-image: url(chrome://browser/skin/sync-horizontalbar.png);
}
#PanelUI-remotetabs {
--panel-ui-sync-illustration-height: 157.5px;
}
.PanelUI-remotetabs-instruction-label,
#PanelUI-remotetabs-mobile-promo {
/* If you change the margin here, the min-height of the synced tabs panel
(e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync, etc) may
need adjusting (see bug 1248506) */
margin: 15px;
text-align: center;
text-shadow: none;
@ -687,6 +694,9 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
/* The boxes with "instructions" get extra top and bottom padding for space
around the illustration and buttons */
.PanelUI-remotetabs-instruction-box {
/* If you change the padding here, the min-height of the synced tabs panel
(e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync, etc) may
need adjusting (see bug 1248506) */
padding-bottom: 30px;
padding-top: 15px;
}
@ -698,6 +708,9 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
is used for buttons in the toolbox overrides. See bug 1238531 for details */
color: white !important;
border-radius: 2px;
/* If you change the margin or padding below, the min-height of the synced tabs
panel (e.g. #PanelUI-remotetabs[mainview] #PanelUI-remotetabs-setupsync,
etc) may need adjusting (see bug 1248506) */
margin-top: 10px;
margin-bottom: 10px;
padding: 8px;
@ -721,7 +734,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
}
.fxaSyncIllustration {
width: 180px;
height: var(--panel-ui-sync-illustration-height);
list-style-image: url(chrome://browser/skin/fxa/sync-illustration.svg);
}
@ -742,7 +755,12 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-reauthsync,
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-nodevicespane,
#PanelUI-remotetabs[mainview] #PanelUI-remotetabs-tabsdisabledpane {
min-height: 33em;
min-height: calc(var(--panel-ui-sync-illustration-height) +
20px + /* margin of .PanelUI-remotetabs-prefs-button */
16px + /* padding of .PanelUI-remotetabs-prefs-button */
30px + /* margin of .PanelUI-remotetabs-instruction-label */
30px + 15px + /* padding of .PanelUI-remotetabs-instruction-box */
11em);
}
#PanelUI-remotetabs-tabslist > label[itemtype="client"] {

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

@ -7,7 +7,6 @@
# be specified once. As a result, the source file paths are relative
# to the location of the actual manifest.
skin/classic/browser/aboutCertError.css (../shared/aboutCertError.css)
skin/classic/browser/aboutNetError.css (../shared/aboutNetError.css)
* skin/classic/browser/aboutProviderDirectory.css (../shared/aboutProviderDirectory.css)
* skin/classic/browser/aboutSessionRestore.css (../shared/aboutSessionRestore.css)

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

@ -3,98 +3,72 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
var toolbox;
"use strict";
function test() {
addTab("about:blank").then(function() {
let target = TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "webconsole").then(testSelect);
});
}
const PAGE_URL = "data:text/html;charset=utf-8,test select events";
var called = {
inspector: false,
webconsole: false,
styleeditor: false,
//jsdebugger: false,
}
add_task(function*() {
let tab = yield addTab(PAGE_URL);
function testSelect(aToolbox) {
toolbox = aToolbox;
let toolbox = yield openToolboxForTab(tab, "webconsole", "bottom");
yield testSelectEvent("inspector");
yield testSelectEvent("webconsole");
yield testSelectEvent("styleeditor");
yield testSelectEvent("inspector");
yield testSelectEvent("webconsole");
yield testSelectEvent("styleeditor");
info("Toolbox fired a `ready` event");
yield testToolSelectEvent("inspector");
yield testToolSelectEvent("webconsole");
yield testToolSelectEvent("styleeditor");
yield toolbox.destroy();
toolbox.on("select", selectCB);
toolbox = yield openToolboxForTab(tab, "webconsole", "side");
yield testSelectEvent("inspector");
yield testSelectEvent("webconsole");
yield testSelectEvent("styleeditor");
yield testSelectEvent("inspector");
yield testSelectEvent("webconsole");
yield testSelectEvent("styleeditor");
yield toolbox.destroy();
toolbox.selectTool("inspector");
toolbox.selectTool("webconsole");
toolbox.selectTool("styleeditor");
//toolbox.selectTool("jsdebugger");
}
toolbox = yield openToolboxForTab(tab, "webconsole", "window");
yield testSelectEvent("inspector");
yield testSelectEvent("webconsole");
yield testSelectEvent("styleeditor");
yield testSelectEvent("inspector");
yield testSelectEvent("webconsole");
yield testSelectEvent("styleeditor");
yield toolbox.destroy();
function selectCB(event, id) {
called[id] = true;
info("toolbox-select event from " + id);
for (let tool in called) {
if (!called[tool]) {
return;
}
/**
* Assert that selecting the given toolId raises a select event
* @param {toolId} Id of the tool to test
*/
function testSelectEvent(toolId) {
return new Promise(resolve => {
toolbox.once("select", (event, id) => {
is(id, toolId, toolId + " selected");
resolve();
});
toolbox.selectTool(toolId);
});
}
ok(true, "All the tools fired a 'select event'");
toolbox.off("select", selectCB);
reselect();
}
function reselect() {
for (let tool in called) {
called[tool] = false;
/**
* Assert that selecting the given toolId raises its corresponding
* selected event
* @param {toolId} Id of the tool to test
*/
function testToolSelectEvent(toolId) {
return new Promise(resolve => {
toolbox.once(toolId + "-selected", () => {
let msg = toolId + " tool selected";
is(toolbox.currentToolId, toolId, msg);
resolve();
});
toolbox.selectTool(toolId);
});
}
});
toolbox.once("inspector-selected", function() {
tidyUpIfAllCalled("inspector");
});
toolbox.once("webconsole-selected", function() {
tidyUpIfAllCalled("webconsole");
});
/*
toolbox.once("jsdebugger-selected", function() {
tidyUpIfAllCalled("jsdebugger");
});
*/
toolbox.once("styleeditor-selected", function() {
tidyUpIfAllCalled("styleeditor");
});
toolbox.selectTool("inspector");
toolbox.selectTool("webconsole");
toolbox.selectTool("styleeditor");
//toolbox.selectTool("jsdebugger");
}
function tidyUpIfAllCalled(id) {
called[id] = true;
info("select event from " + id);
for (let tool in called) {
if (!called[tool]) {
return;
}
}
ok(true, "All the tools fired a {id}-selected event");
tidyUp();
}
function tidyUp() {
toolbox.destroy();
gBrowser.removeCurrentTab();
toolbox = null;
finish();
}

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

@ -3,6 +3,8 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from shared-head.js */
// shared-head.js handles imports, constants, and utility functions
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);

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

@ -1242,9 +1242,9 @@ Toolbox.prototype = {
if (typeof panel.open == "function") {
built = panel.open();
} else {
let deferred = promise.defer();
deferred.resolve(panel);
built = deferred.promise;
let buildDeferred = promise.defer();
buildDeferred.resolve(panel);
built = buildDeferred.promise;
}
}
@ -1286,12 +1286,32 @@ Toolbox.prototype = {
iframe.removeEventListener("DOMContentLoaded", callback);
onLoad();
};
iframe.addEventListener("DOMContentLoaded", callback);
}
return deferred.promise;
},
/**
* Mark all in collection as unselected; and id as selected
* @param {string} collection
* DOM collection of items
* @param {string} id
* The Id of the item within the collection to select
*/
selectSingleNode: function(collection, id) {
[...collection].forEach(node => {
if (node.id === id) {
node.setAttribute("selected", "true");
node.setAttribute("aria-selected", "true");
} else {
node.removeAttribute("selected");
node.removeAttribute("aria-selected");
}
});
},
/**
* Switch to the tool with the given id
*
@ -1301,15 +1321,8 @@ Toolbox.prototype = {
selectTool: function(id) {
this.emit("before-select", id);
let selected = this.doc.querySelector(".devtools-tab[selected]");
if (selected) {
selected.removeAttribute("selected");
selected.setAttribute("aria-selected", "false");
}
let tab = this.doc.getElementById("toolbox-tab-" + id);
tab.setAttribute("selected", "true");
tab.setAttribute("aria-selected", "true");
let tabs = this.doc.querySelectorAll(".devtools-tab");
this.selectSingleNode(tabs, "toolbox-tab-" + id);
// If options is selected, the separator between it and the
// command buttons should be hidden.
@ -1332,7 +1345,7 @@ Toolbox.prototype = {
throw new Error("Can't select tool, wait for toolbox 'ready' event");
}
tab = this.doc.getElementById("toolbox-tab-" + id);
let tab = this.doc.getElementById("toolbox-tab-" + id);
if (tab) {
if (this.currentToolId) {
@ -1350,9 +1363,8 @@ Toolbox.prototype = {
tabstrip.selectedItem = tab || tabstrip.childNodes[0];
// and select the right iframe
let deck = this.doc.getElementById("toolbox-deck");
let panel = this.doc.getElementById("toolbox-panel-" + id);
deck.selectedPanel = panel;
let toolboxPanels = this.doc.querySelectorAll(".toolbox-panel");
this.selectSingleNode(toolboxPanels, "toolbox-panel-" + id);
this.lastUsedToolId = this.currentToolId;
this.currentToolId = id;

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

@ -142,7 +142,7 @@
height set to a small value without flexing to fill up extra
space. There must be a flex on both to ensure that the console
panel itself is sized properly -->
<deck id="toolbox-deck" flex="1000" minheight="75" />
<box id="toolbox-deck" flex="1000" minheight="75" />
<splitter id="toolbox-console-splitter" class="devtools-horizontal-splitter" hidden="true" />
<box minheight="75" flex="1" id="toolbox-panel-webconsole" collapsed="true" />
</vbox>

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

@ -447,6 +447,25 @@ InspectorPanel.prototype = {
}
},
/**
* Can a new HTML element be inserted into the currently selected element?
* @return {Boolean}
*/
canAddHTMLChild: function() {
let selection = this.selection;
// Don't allow to insert an element into these elements. This should only
// contain elements where walker.insertAdjacentHTML has no effect.
let invalidTagNames = ["html", "iframe"];
return selection.isHTMLNode() &&
selection.isElementNode() &&
!selection.isPseudoElementNode() &&
!selection.isAnonymousNode() &&
invalidTagNames.indexOf(
selection.nodeFront.nodeName.toLowerCase()) === -1;
},
/**
* When a new node is selected.
*/
@ -459,6 +478,15 @@ InspectorPanel.prototype = {
// client know.
let selection = this.selection.nodeFront;
// Update the state of the add button in the toolbar depending on the
// current selection.
let btn = this.panelDoc.querySelector("#inspector-element-add-button");
if (this.canAddHTMLChild()) {
btn.removeAttribute("disabled");
} else {
btn.setAttribute("disabled", "true");
}
// On any new selection made by the user, store the unique css selector
// of the selected node so it can be restored after reload of the same page
if (this.canGetUniqueSelector &&
@ -705,6 +733,14 @@ InspectorPanel.prototype = {
deleteNode.setAttribute("disabled", "true");
}
// Disable add item if needed
let addNode = this.panelDoc.getElementById("node-menu-add");
if (this.canAddHTMLChild()) {
addNode.removeAttribute("disabled");
} else {
addNode.setAttribute("disabled", "true");
}
// Disable / enable "Copy Unique Selector", "Copy inner HTML",
// "Copy outer HTML", "Scroll Into View" & "Screenshot Node" as appropriate
let unique = this.panelDoc.getElementById("node-menu-copyuniqueselector");
@ -1012,6 +1048,27 @@ InspectorPanel.prototype = {
}
},
/**
* Create a new node as the last child of the current selection, expand the
* parent and select the new node.
*/
addNode: Task.async(function*() {
let root = this.selection.nodeFront;
if (!this.canAddHTMLChild(root)) {
return;
}
let html = "<div></div>";
// Insert the html and expect a childList markup mutation.
let onMutations = this.once("markupmutation");
let {nodes} = yield this.walker.insertAdjacentHTML(root, "beforeEnd", html);
yield onMutations;
// Select the new node (this will auto-expand its parent).
this.selection.setNodeFront(nodes[0]);
}),
/**
* Toggle a pseudo class.
*/

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

@ -110,6 +110,10 @@
<menuitem id="node-menu-screenshotnode"
label="&inspectorScreenshotNode.label;"
oncommand="inspector.screenshotNode()" />
<menuitem id="node-menu-add"
label="&inspectorAddNode.label;"
accesskey="&inspectorAddNode.accesskey;"
oncommand="inspector.addNode()"/>
<menuitem id="node-menu-duplicatenode"
label="&inspectorDuplicateNode.label;"
oncommand="inspector.duplicateNode()"/>
@ -157,6 +161,10 @@
<toolbar id="inspector-toolbar"
class="devtools-toolbar"
nowindowdrag="true">
<toolbarbutton id="inspector-element-add-button"
class="devtools-toolbarbutton"
tooltiptext="&inspectorAddNode.label;"
oncommand="inspector.addNode()" />
<spacer flex="1"/>
<box id="inspector-searchlabel" />
<textbox id="inspector-searchbox"

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

@ -72,6 +72,7 @@ support-files =
[browser_rules_completion-new-property_02.js]
[browser_rules_completion-new-property_03.js]
[browser_rules_completion-new-property_04.js]
[browser_rules_completion-new-property_multiline.js]
[browser_rules_computed-lists_01.js]
[browser_rules_computed-lists_02.js]
[browser_rules_completion-popup-hidden-after-navigation.js]

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

@ -0,0 +1,119 @@
/* 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 behaviour of the CSS autocomplete for CSS value displayed on
// multiple lines. Expected behavior is:
// - UP/DOWN should navigate in the input and not increment/decrement numbers
// - typing a new value should still trigger the autocomplete
// - UP/DOWN when the autocomplete popup is displayed should cycle through
// suggestions
const LONG_CSS_VALUE =
"transparent linear-gradient(0deg, blue 0%, white 5%, red 10%, blue 15%, " +
"white 20%, red 25%, blue 30%, white 35%, red 40%, blue 45%, white 50%, " +
"red 55%, blue 60%, white 65%, red 70%, blue 75%, white 80%, red 85%, " +
"blue 90%, white 95% ) repeat scroll 0% 0%";
const EXPECTED_CSS_VALUE = LONG_CSS_VALUE.replace("95%", "95%, red");
const TEST_URI =
`<style>
.title {
background: ${LONG_CSS_VALUE};
}
</style>
<h1 class=title>Header</h1>`;
add_task(function*() {
yield addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
let { inspector, view} = yield openRuleView();
info("Selecting the test node");
yield selectNode("h1", inspector);
info("Focusing the property editable field");
let rule = getRuleViewRuleEditor(view, 1).rule;
let prop = rule.textProps[0];
info("Focusing the css property editable value");
let rect = prop.editor.valueSpan.getBoundingClientRect();
let editor = yield focusEditableField(view, prop.editor.valueSpan,
rect.width / 2, rect.height / 2);
info("Moving the caret next to a number");
let pos = editor.input.value.indexOf("0deg") + 1;
editor.input.setSelectionRange(pos, pos);
is(editor.input.value[editor.input.selectionStart - 1], "0",
"Input caret is after a 0");
info("Check that UP/DOWN navigates in the input, even when next to a number");
EventUtils.synthesizeKey("VK_DOWN", {}, view.styleWindow);
ok(editor.input.selectionStart != pos, "Input caret moved");
is(editor.input.value, LONG_CSS_VALUE, "Input value was not decremented.");
info("Move the caret to the end of the gradient definition.");
pos = editor.input.value.indexOf("95%") + 3;
editor.input.setSelectionRange(pos, pos);
info("Sending \", re\" to the editable field.");
for (let key of ", re") {
yield synthesizeKeyForAutocomplete(key, editor, view.styleWindow);
}
info("Check the autocomplete can still be displayed.");
ok(editor.popup && editor.popup.isOpen, "Autocomplete popup is displayed.");
is(editor.popup.selectedIndex, 0,
"Autocomplete has an item selected by default");
let item = editor.popup.getItemAtIndex(editor.popup.selectedIndex);
is(item.label, "rebeccapurple",
"Check autocomplete displays expected value.");
info("Check autocomplete suggestions can be cycled using UP/DOWN arrows.");
yield synthesizeKeyForAutocomplete("VK_DOWN", editor, view.styleWindow);
ok(editor.popup.selectedIndex, 1, "Using DOWN cycles autocomplete values.");
yield synthesizeKeyForAutocomplete("VK_DOWN", editor, view.styleWindow);
ok(editor.popup.selectedIndex, 2, "Using DOWN cycles autocomplete values.");
yield synthesizeKeyForAutocomplete("VK_UP", editor, view.styleWindow);
is(editor.popup.selectedIndex, 1, "Using UP cycles autocomplete values.");
item = editor.popup.getItemAtIndex(editor.popup.selectedIndex);
is(item.label, "red", "Check autocomplete displays expected value.");
info("Select the background-color suggestion with a mouse click.");
let onRuleviewChanged = view.once("ruleview-changed");
let onInputFocus = once(editor.input, "focus", true);
let node = editor.popup._list.childNodes[editor.popup.selectedIndex];
EventUtils.synthesizeMouseAtCenter(node, {}, view.styleWindow);
yield onInputFocus;
yield onRuleviewChanged;
is(editor.input.value, EXPECTED_CSS_VALUE,
"Input value correctly autocompleted");
info("Press ESCAPE to leave the input.");
onRuleviewChanged = view.once("ruleview-changed");
EventUtils.synthesizeKey("VK_ESCAPE", {}, view.styleWindow);
yield onRuleviewChanged;
});
/**
* Send the provided key to the currently focused input of the provided window.
* Wait for the editor to emit "after-suggest" to make sure the autocompletion
* process is finished.
*
* @param {String} key
* The key to send to the input.
* @param {InplaceEditor} editor
* The inplace editor which owns the focused input.
* @param {Window} win
* Window in which the key event will be dispatched.
*/
function* synthesizeKeyForAutocomplete(key, editor, win) {
let onSuggest = editor.once("after-suggest");
EventUtils.synthesizeKey(key, {}, win);
yield onSuggest;
}

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

@ -2,6 +2,7 @@
tags = devtools
subsuite = devtools
support-files =
doc_inspector_add_node.html
doc_inspector_breadcrumbs.html
doc_inspector_delete-selected-node-01.html
doc_inspector_delete-selected-node-02.html
@ -36,6 +37,9 @@ support-files =
!/devtools/client/shared/test/test-actor.js
!/devtools/client/shared/test/test-actor-registry.js
[browser_inspector_addNode_01.js]
[browser_inspector_addNode_02.js]
[browser_inspector_addNode_03.js]
[browser_inspector_breadcrumbs.js]
[browser_inspector_breadcrumbs_highlight_hover.js]
[browser_inspector_breadcrumbs_keybinding.js]

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

@ -0,0 +1,22 @@
/* vim: set 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 that the add node button and context menu items are present in the UI.
const TEST_URL = "data:text/html;charset=utf-8,<h1>Add node</h1>";
add_task(function*() {
let {inspector} = yield openInspectorForURL(TEST_URL);
let {panelDoc} = inspector;
let toolbarButton =
panelDoc.querySelector("#inspector-toolbar #inspector-element-add-button");
let menuItem =
panelDoc.querySelector("#inspector-node-popup #node-menu-add");
ok(toolbarButton, "The add button is in the toolbar");
ok(menuItem, "The item is in the menu");
});

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

@ -0,0 +1,62 @@
/* vim: set 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 that the add node button and context menu items have the right state
// depending on the current selection.
const TEST_URL = URL_ROOT + "doc_inspector_add_node.html";
add_task(function*() {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Select the DOCTYPE element");
let {nodes} = yield inspector.walker.children(inspector.walker.rootNode);
yield selectNode(nodes[0], inspector);
assertState(false, inspector,
"The button and item are disabled on DOCTYPE");
info("Select the ::before pseudo-element");
let body = yield getNodeFront("body", inspector);
({nodes} = yield inspector.walker.children(body));
yield selectNode(nodes[0], inspector);
assertState(false, inspector,
"The button and item are disabled on a pseudo-element");
info("Select the svg element");
yield selectNode("svg", inspector);
assertState(false, inspector,
"The button and item are disabled on a SVG element");
info("Select the div#foo element");
yield selectNode("#foo", inspector);
assertState(true, inspector,
"The button and item are enabled on a DIV element");
info("Select the documentElement element (html)");
yield selectNode("html", inspector);
assertState(false, inspector,
"The button and item are disabled on the documentElement");
info("Select the iframe element");
yield selectNode("iframe", inspector);
assertState(false, inspector,
"The button and item are disabled on an IFRAME element");
});
function assertState(isEnabled, inspector, desc) {
let doc = inspector.panelDoc;
let btn = doc.querySelector("#inspector-element-add-button");
let item = doc.querySelector("#node-menu-add");
// Force an update of the context menu to make sure menu items are updated
// according to the current selection. This normally happens when the menu is
// opened, but for the sake of this test's simplicity, we directly call the
// private update function instead.
inspector._setupNodeMenu({target: {}});
is(!btn.hasAttribute("disabled"), isEnabled, desc);
is(!item.hasAttribute("disabled"), isEnabled, desc);
}

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

@ -0,0 +1,68 @@
/* vim: set 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 that adding nodes does work as expected: the parent gets expanded and
// the new node gets selected.
const TEST_URL = URL_ROOT + "doc_inspector_add_node.html";
add_task(function*() {
let {inspector} = yield openInspectorForURL(TEST_URL);
info("Adding in element that has no children and is collapsed");
let parentNode = yield getNodeFront("#foo", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children but that has not been expanded yet");
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children that has been expanded then collapsed");
// Select again #bar and collapse it.
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
collapseNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
info("Adding in element with children that is expanded");
parentNode = yield getNodeFront("#bar", inspector);
yield selectNode(parentNode, inspector);
yield testAddNode(parentNode, inspector);
});
function* testAddNode(parentNode, inspector) {
let btn = inspector.panelDoc.querySelector("#inspector-element-add-button");
info("Clicking on the 'add node' button and expecting a markup mutation");
let onMutation = inspector.once("markupmutation");
btn.click();
let mutations = yield onMutation;
info("Expecting an inspector-updated event right after the mutation event "+
"to wait for the new node selection");
yield inspector.once("inspector-updated");
is(mutations.length, 1, "There is one mutation only");
is(mutations[0].added.length, 1, "There is one new node only");
let newNode = mutations[0].added[0];
is(newNode, inspector.selection.nodeFront,
"The new node is selected");
ok(inspector.markup.getContainer(parentNode).expanded,
"The parent node is now expanded");
is(inspector.selection.nodeFront.parentNode(), parentNode,
"The new node is inside the right parent");
}
function collapseNode(node, inspector) {
let container = inspector.markup.getContainer(node);
container.setExpanded(false);
}

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

@ -0,0 +1,22 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Add elements tests</title>
<style>
body::before {
content: "pseudo-element";
}
</style>
</head>
<body>
<div id="foo"></div>
<svg>
<rect x="0" y="0" width="100" height="50"></rect>
</svg>
<div id="bar">
<div id="baz"></div>
</div>
<iframe src="data:text/html;charset=utf-8,Test iframe content"></iframe>
</body>
</html>

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

@ -155,3 +155,9 @@
shown in the inspector contextual-menu for the item that lets users
duplicate the currently selected node. -->
<!ENTITY inspectorDuplicateNode.label "Duplicate Node">
<!-- LOCALIZATION NOTE (inspectorAddNode.label): This is the label shown in
the inspector toolbar for the button that lets users add elements to the
DOM (as children of the currently selected element). -->
<!ENTITY inspectorAddNode.label "Create New Node">
<!ENTITY inspectorAddNode.accesskey "C">

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

@ -31,10 +31,22 @@ let App = createClass({
screenshot: PropTypes.shape(Types.screenshot).isRequired,
},
onBrowserMounted() {
window.postMessage({ type: "browser-mounted" }, "*");
},
onChangeViewportDevice(id, device) {
this.props.dispatch(changeDevice(id, device));
},
onContentResize({ width, height }) {
window.postMessage({
type: "content-resize",
width,
height,
}, "*");
},
onExit() {
window.postMessage({ type: "exit" }, "*");
},
@ -60,7 +72,9 @@ let App = createClass({
} = this.props;
let {
onBrowserMounted,
onChangeViewportDevice,
onContentResize,
onExit,
onResizeViewport,
onRotateViewport,
@ -81,7 +95,9 @@ let App = createClass({
location,
screenshot,
viewports,
onBrowserMounted,
onChangeViewportDevice,
onContentResize,
onRotateViewport,
onResizeViewport,
})

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

@ -2,12 +2,18 @@
* 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/. */
/* eslint-env browser */
"use strict";
const { DOM: dom, createClass, PropTypes, addons } =
const { Task } = require("resource://gre/modules/Task.jsm");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const { getToplevelWindow } = require("sdk/window/utils");
const { DOM: dom, createClass, addons, PropTypes } =
require("devtools/client/shared/vendor/react");
const Types = require("../types");
const { waitForMessage } = require("../utils/e10s");
module.exports = createClass({
@ -15,32 +21,96 @@ module.exports = createClass({
mixins: [ addons.PureRenderMixin ],
/**
* This component is not allowed to depend directly on frequently changing
* data (width, height) due to the use of `dangerouslySetInnerHTML` below.
* Any changes in props will cause the <iframe> to be removed and added again,
* throwing away the current state of the page.
*/
propTypes: {
location: Types.location.isRequired,
width: Types.viewport.width.isRequired,
height: Types.viewport.height.isRequired,
isResizing: PropTypes.bool.isRequired,
onBrowserMounted: PropTypes.func.isRequired,
onContentResize: PropTypes.func.isRequired,
},
/**
* Once the browser element has mounted, load the frame script and enable
* various features, like floating scrollbars.
*/
componentDidMount: Task.async(function*() {
let { onContentResize } = this;
let browser = this.refs.browserContainer.querySelector("iframe.browser");
let mm = browser.frameLoader.messageManager;
// Notify tests when the content has received a resize event. This is not
// quite the same timing as when we _set_ a new size around the browser,
// since it still needs to do async work before the content is actually
// resized to match.
mm.addMessageListener("ResponsiveMode:OnContentResize", onContentResize);
let ready = waitForMessage(mm, "ResponsiveMode:ChildScriptReady");
mm.loadFrameScript("resource://devtools/client/responsivedesign/" +
"responsivedesign-child.js", true);
yield ready;
let browserWindow = getToplevelWindow(window);
let requiresFloatingScrollbars =
!browserWindow.matchMedia("(-moz-overlay-scrollbars)").matches;
let started = waitForMessage(mm, "ResponsiveMode:Start:Done");
mm.sendAsyncMessage("ResponsiveMode:Start", {
requiresFloatingScrollbars,
// Tests expect events on resize to yield on various size changes
notifyOnResize: DevToolsUtils.testing,
});
yield started;
// manager.js waits for this signal before allowing browser tests to start
this.props.onBrowserMounted();
}),
componentWillUnmount() {
let { onContentResize } = this;
let browser = this.refs.browserContainer.querySelector("iframe.browser");
let mm = browser.frameLoader.messageManager;
mm.removeMessageListener("ResponsiveMode:OnContentResize", onContentResize);
mm.sendAsyncMessage("ResponsiveMode:Stop");
},
onContentResize(msg) {
let { onContentResize } = this.props;
let { width, height } = msg.data;
onContentResize({
width,
height,
});
},
render() {
let {
location,
width,
height,
isResizing,
} = this.props;
let className = "browser";
if (isResizing) {
className += " resizing";
}
// innerHTML expects & to be an HTML entity
location = location.replace(/&/g, "&amp;");
return dom.iframe(
return dom.div(
{
className,
src: location,
width,
height,
ref: "browserContainer",
className: "browser-container",
/**
* React uses a whitelist for attributes, so we need some way to set
* attributes it does not know about, such as @mozbrowser. If this were
* the only issue, we could use componentDidMount or ref: node => {} to
* set the atttibutes. In the case of @remote, the attribute must be set
* before the element is added to the DOM to have any effect, which we
* are able to do with this approach.
*/
dangerouslySetInnerHTML: {
__html: `<iframe class="browser" mozbrowser="true" remote="true"
noisolation="true" src="${location}"
width="100%" height="100%"></iframe>`
}
}
);
},

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

@ -26,7 +26,9 @@ module.exports = createClass({
location: Types.location.isRequired,
screenshot: PropTypes.shape(Types.screenshot).isRequired,
viewport: PropTypes.shape(Types.viewport).isRequired,
onBrowserMounted: PropTypes.func.isRequired,
onChangeViewportDevice: PropTypes.func.isRequired,
onContentResize: PropTypes.func.isRequired,
onResizeViewport: PropTypes.func.isRequired,
onRotateViewport: PropTypes.func.isRequired,
},
@ -115,17 +117,23 @@ module.exports = createClass({
location,
screenshot,
viewport,
onBrowserMounted,
onChangeViewportDevice,
onContentResize,
onResizeViewport,
onRotateViewport,
} = this.props;
let resizeHandleClass = "viewport-resize-handle";
if (screenshot.isCapturing) {
resizeHandleClass += " hidden";
}
let contentClass = "viewport-content";
if (this.state.isResizing) {
contentClass += " resizing";
}
return dom.div(
{
className: "resizable-viewport",
@ -137,12 +145,20 @@ module.exports = createClass({
onResizeViewport,
onRotateViewport,
}),
Browser({
location,
width: viewport.width,
height: viewport.height,
isResizing: this.state.isResizing
}),
dom.div(
{
className: contentClass,
style: {
width: viewport.width + "px",
height: viewport.height + "px",
},
},
Browser({
location,
onBrowserMounted,
onContentResize,
})
),
dom.div({
className: resizeHandleClass,
onMouseDown: this.onResizeStart,

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

@ -20,7 +20,9 @@ module.exports = createClass({
location: Types.location.isRequired,
screenshot: PropTypes.shape(Types.screenshot).isRequired,
viewport: PropTypes.shape(Types.viewport).isRequired,
onBrowserMounted: PropTypes.func.isRequired,
onChangeViewportDevice: PropTypes.func.isRequired,
onContentResize: PropTypes.func.isRequired,
onResizeViewport: PropTypes.func.isRequired,
onRotateViewport: PropTypes.func.isRequired,
},
@ -58,6 +60,8 @@ module.exports = createClass({
location,
screenshot,
viewport,
onContentResize,
onBrowserMounted,
} = this.props;
let {
@ -75,7 +79,9 @@ module.exports = createClass({
location,
screenshot,
viewport,
onBrowserMounted,
onChangeViewportDevice,
onContentResize,
onResizeViewport,
onRotateViewport,
}),

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

@ -19,7 +19,9 @@ module.exports = createClass({
location: Types.location.isRequired,
screenshot: PropTypes.shape(Types.screenshot).isRequired,
viewports: PropTypes.arrayOf(PropTypes.shape(Types.viewport)).isRequired,
onBrowserMounted: PropTypes.func.isRequired,
onChangeViewportDevice: PropTypes.func.isRequired,
onContentResize: PropTypes.func.isRequired,
onResizeViewport: PropTypes.func.isRequired,
onRotateViewport: PropTypes.func.isRequired,
},
@ -30,7 +32,9 @@ module.exports = createClass({
location,
screenshot,
viewports,
onBrowserMounted,
onChangeViewportDevice,
onContentResize,
onResizeViewport,
onRotateViewport,
} = this.props;
@ -46,7 +50,9 @@ module.exports = createClass({
location,
screenshot,
viewport,
onBrowserMounted,
onChangeViewportDevice,
onContentResize,
onResizeViewport,
onRotateViewport,
});

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

@ -200,19 +200,28 @@ body {
background-image: url("./images/rotate-viewport.svg");
}
/**
* Viewport Content
*/
.viewport-content.resizing {
pointer-events: none;
}
/**
* Viewport Browser
*/
.browser-container {
width: inherit;
height: inherit;
}
.browser {
display: block;
border: 0;
}
.browser.resizing {
pointer-events: none;
}
/**
* Viewport Resize Handles
*/

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

@ -15,6 +15,7 @@ const { require } = BrowserLoader({
});
const { GetDevices } = require("devtools/client/shared/devices");
const Telemetry = require("devtools/client/shared/telemetry");
const { loadSheet } = require("sdk/stylesheet/utils");
const { createFactory, createElement } =
require("devtools/client/shared/vendor/react");
@ -25,8 +26,7 @@ const App = createFactory(require("./app"));
const Store = require("./store");
const { addDevice, addDeviceType } = require("./actions/devices");
const { changeLocation } = require("./actions/location");
const { addViewport } = require("./actions/viewports");
const { loadSheet } = require("sdk/stylesheet/utils");
const { addViewport, resizeViewport } = require("./actions/viewports");
let bootstrap = {
@ -43,7 +43,6 @@ let bootstrap = {
this.telemetry.toolOpened("responsive");
let store = this.store = Store();
let provider = createElement(Provider, { store }, App());
ReactDOM.render(provider, document.querySelector("#root"));
this.initDevices();
window.postMessage({ type: "init" }, "*");
@ -115,3 +114,32 @@ window.addInitialViewport = contentURI => {
console.error(e);
}
};
/**
* Called by manager.js when tests want to check the viewport size.
*/
window.getViewportSize = () => {
let { width, height } = bootstrap.store.getState().viewports[0];
return { width, height };
};
/**
* Called by manager.js to set viewport size from GCLI.
*/
window.setViewportSize = (width, height) => {
try {
bootstrap.dispatch(resizeViewport(0, width, height));
} catch (e) {
console.error(e);
}
};
/**
* Called by manager.js when tests want to use the viewport's message manager.
* It is packed into an object because this is the format most easily usable
* with ContentTask.spawn().
*/
window.getViewportMessageManager = () => {
let { messageManager } = document.querySelector("iframe.browser").frameLoader;
return { messageManager };
};

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

@ -4,8 +4,10 @@
"use strict";
const { Ci, Cr } = require("chrome");
const promise = require("promise");
const { Task } = require("resource://gre/modules/Task.jsm");
const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm");
const EventEmitter = require("devtools/shared/event-emitter");
const TOOL_URL = "chrome://devtools/content/responsive.html/index.xhtml";
@ -69,14 +71,14 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
* @return Promise
* Resolved (with no value) when closing is complete.
*/
closeIfNeeded(window, tab) {
closeIfNeeded: Task.async(function*(window, tab) {
if (this.isActiveForTab(tab)) {
this.activeTabs.get(tab).destroy();
yield this.activeTabs.get(tab).destroy();
this.activeTabs.delete(tab);
this.emit("off", { tab });
}
return promise.resolve();
},
}),
/**
* Returns true if responsive UI is active for a given tab.
@ -118,8 +120,7 @@ const ResponsiveUIManager = exports.ResponsiveUIManager = {
switch (command) {
case "resize to":
completed = this.openIfNeeded(window, tab);
// TODO: Probably the wrong API
this.activeTabs.get(tab).setSize(args.width, args.height);
this.activeTabs.get(tab).setViewportSize(args.width, args.height);
break;
case "resize on":
completed = this.openIfNeeded(window, tab);
@ -196,19 +197,23 @@ ResponsiveUI.prototype = {
tabBrowser.loadURI(TOOL_URL);
yield tabLoaded(this.tab);
let toolWindow = this.toolWindow = tabBrowser.contentWindow;
toolWindow.addEventListener("message", this);
yield waitForMessage(toolWindow, "init");
toolWindow.addInitialViewport(contentURI);
toolWindow.addEventListener("message", this);
yield waitForMessage(toolWindow, "browser-mounted");
}),
destroy() {
destroy: Task.async(function*() {
let tabBrowser = this.tab.linkedBrowser;
tabBrowser.goBack();
this.window = null;
let browserWindow = this.browserWindow;
this.browserWindow = null;
this.tab = null;
this.inited = null;
this.toolWindow = null;
},
let loaded = waitForDocLoadComplete(browserWindow.gBrowser);
tabBrowser.goBack();
yield loaded;
}),
handleEvent(event) {
let { tab, window } = this;
@ -219,14 +224,37 @@ ResponsiveUI.prototype = {
}
switch (event.data.type) {
case "content-resize":
let { width, height } = event.data;
this.emit("content-resize", {
width,
height,
});
break;
case "exit":
toolWindow.removeEventListener(event.type, this);
ResponsiveUIManager.closeIfNeeded(window, tab);
break;
}
},
getViewportSize() {
return this.toolWindow.getViewportSize();
},
setViewportSize: Task.async(function*(width, height) {
yield this.inited;
this.toolWindow.setViewportSize(width, height);
}),
getViewportMessageManager() {
return this.toolWindow.getViewportMessageManager();
},
};
EventEmitter.decorate(ResponsiveUI.prototype);
function waitForMessage(win, type) {
let deferred = promise.defer();
@ -257,3 +285,27 @@ function tabLoaded(tab) {
tab.linkedBrowser.addEventListener("load", handle, true);
return deferred.promise;
}
/**
* Waits for the next load to complete in the current browser.
*/
function waitForDocLoadComplete(gBrowser) {
let deferred = promise.defer();
let progressListener = {
onStateChange: function(webProgress, req, flags, status) {
let docStop = Ci.nsIWebProgressListener.STATE_IS_NETWORK |
Ci.nsIWebProgressListener.STATE_STOP;
// When a load needs to be retargetted to a new process it is cancelled
// with NS_BINDING_ABORTED so ignore that case
if ((flags & docStop) == docStop && status != Cr.NS_BINDING_ABORTED) {
gBrowser.removeProgressListener(progressListener);
deferred.resolve();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference])
};
gBrowser.addProgressListener(progressListener);
return deferred.promise;
}

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

@ -1,12 +1,17 @@
[DEFAULT]
tags = devtools
subsuite = devtools
skip-if = !e10s && debug # Bug 1262416 - Intermittent crash at MessageLoop::DeletePendingTasks
support-files =
devices.json
head.js
!/devtools/client/commandline/test/helpers.js
!/devtools/client/framework/test/shared-head.js
!/devtools/client/framework/test/shared-redux-head.js
[browser_device_width.js]
skip-if = (!e10s && debug) || (e10s && debug && os == "win") # Bug 1262432 - crashes at nsLayoutUtils::HasDisplayPort(content), Bug 1262416 - Intermittent crash at MessageLoop::DeletePendingTasks
[browser_exit_button.js]
[browser_resize_cmd.js]
[browser_screenshot_button.js]
[browser_viewport_basics.js]

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

@ -0,0 +1,66 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const TEST_URL = "about:logo";
addRDMTask(TEST_URL, function*({ ui, manager }) {
ok(ui, "An instance of the RDM should be attached to the tab.");
yield setViewportSize(ui, manager, 110, 500);
info("Checking initial width/height properties.");
yield doInitialChecks(ui);
info("Changing the RDM size");
yield setViewportSize(ui, manager, 90, 500);
info("Checking for screen props");
yield checkScreenProps(ui);
info("Setting docShell.deviceSizeIsPageSize to false");
yield ContentTask.spawn(ui.getViewportMessageManager(), {}, function*() {
let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell);
docShell.deviceSizeIsPageSize = false;
});
info("Checking for screen props once again.");
yield checkScreenProps2(ui);
});
function* doInitialChecks(ui) {
let { innerWidth, matchesMedia } = yield grabContentInfo(ui);
is(innerWidth, 110, "initial width should be 110px");
ok(!matchesMedia, "media query shouldn't match.");
}
function* checkScreenProps(ui) {
let { matchesMedia, screen } = yield grabContentInfo(ui);
ok(matchesMedia, "media query should match");
isnot(window.screen.width, screen.width,
"screen.width should not be the size of the screen.");
is(screen.width, 90, "screen.width should be the page width");
is(screen.height, 500, "screen.height should be the page height");
}
function* checkScreenProps2(ui) {
let { matchesMedia, screen } = yield grabContentInfo(ui);
ok(!matchesMedia, "media query should be re-evaluated.");
is(window.screen.width, screen.width,
"screen.width should be the size of the screen.");
}
function grabContentInfo(ui) {
return ContentTask.spawn(ui.getViewportMessageManager(), {}, function*() {
return {
screen: {
width: content.screen.width,
height: content.screen.height
},
innerWidth: content.innerWidth,
matchesMedia: content.matchMedia("(max-device-width:100px)").matches
};
});
}

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

@ -14,10 +14,9 @@ addRDMTask(TEST_URL, function*({ ui, manager }) {
// Wait until the viewport has been added
yield waitUntilState(store, state => state.viewports.length == 1);
let browser = toolWindow.document.querySelector(".browser");
let exitButton = toolWindow.document.getElementById("global-exit-button");
yield waitForFrameLoad(browser, TEST_URL);
yield waitForFrameLoad(ui, TEST_URL);
ok(manager.isActiveForTab(ui.tab),
"Responsive Design Mode active for the tab");

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

@ -0,0 +1,148 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/* global ResponsiveUIManager */
/* eslint key-spacing: 0 */
add_task(function*() {
let manager = ResponsiveUIManager;
let done;
function isOpen() {
return ResponsiveUIManager.isActiveForTab(gBrowser.selectedTab);
}
const TEST_URL = "data:text/html;charset=utf-8,hi";
yield helpers.addTabWithToolbar(TEST_URL, (options) => {
return helpers.audit(options, [
{
setup() {
done = once(manager, "on");
return helpers.setInput(options, "resize toggle");
},
check: {
input: "resize toggle",
hints: "",
markup: "VVVVVVVVVVVVV",
status: "VALID"
},
exec: {
output: ""
},
post: Task.async(function*() {
yield done;
ok(isOpen(), "responsive mode is open");
}),
},
{
setup() {
done = once(manager, "off");
return helpers.setInput(options, "resize toggle");
},
check: {
input: "resize toggle",
hints: "",
markup: "VVVVVVVVVVVVV",
status: "VALID"
},
exec: {
output: ""
},
post: Task.async(function*() {
yield done;
ok(!isOpen(), "responsive mode is closed");
}),
},
]);
});
yield helpers.addTabWithToolbar(TEST_URL, (options) => {
return helpers.audit(options, [
{
setup() {
done = once(manager, "on");
return helpers.setInput(options, "resize on");
},
check: {
input: "resize on",
hints: "",
markup: "VVVVVVVVV",
status: "VALID"
},
exec: {
output: ""
},
post: Task.async(function*() {
yield done;
ok(isOpen(), "responsive mode is open");
}),
},
{
setup() {
done = once(manager, "off");
return helpers.setInput(options, "resize off");
},
check: {
input: "resize off",
hints: "",
markup: "VVVVVVVVVV",
status: "VALID"
},
exec: {
output: ""
},
post: Task.async(function*() {
yield done;
ok(!isOpen(), "responsive mode is closed");
}),
},
]);
});
yield helpers.addTabWithToolbar(TEST_URL, (options) => {
return helpers.audit(options, [
{
setup() {
done = once(manager, "on");
return helpers.setInput(options, "resize to 400 400");
},
check: {
input: "resize to 400 400",
hints: "",
markup: "VVVVVVVVVVVVVVVVV",
status: "VALID",
args: {
width: { value: 400 },
height: { value: 400 },
}
},
exec: {
output: ""
},
post: Task.async(function*() {
yield done;
ok(isOpen(), "responsive mode is open");
}),
},
{
setup() {
done = once(manager, "off");
return helpers.setInput(options, "resize off");
},
check: {
input: "resize off",
hints: "",
markup: "VVVVVVVVVV",
status: "VALID"
},
exec: {
output: ""
},
post: Task.async(function*() {
yield done;
ok(!isOpen(), "responsive mode is closed");
}),
},
]);
});
});

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

@ -14,16 +14,17 @@ addRDMTask(TEST_URL, function*({ ui }) {
yield waitUntilState(store, state => state.viewports.length == 1);
// A single viewport of default size appeared
let browser = ui.toolWindow.document.querySelector(".browser");
is(browser.width, "320", "Viewport has default width");
is(browser.height, "480", "Viewport has default height");
let viewport = ui.toolWindow.document.querySelector(".viewport-content");
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("width"),
"320px", "Viewport has default width");
is(ui.toolWindow.getComputedStyle(viewport).getPropertyValue("height"),
"480px", "Viewport has default height");
// Browser's location should match original tab
// TODO: For the moment, we have parent process <iframe>s and we can just
// check the location directly. Bug 1240896 will change this to <iframe
// mozbrowser remote>, which is in the child process, so ContentTask or
// similar will be needed.
yield waitForFrameLoad(browser, TEST_URL);
is(browser.contentWindow.location.href, TEST_URL,
"Viewport location matches");
yield waitForFrameLoad(ui, TEST_URL);
let location = yield spawnViewportTask(ui, {}, function*() {
return content.location.href;
});
is(location, TEST_URL, "Viewport location matches");
});

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

@ -6,6 +6,7 @@
/* eslint no-unused-vars: [2, {"vars": "local"}] */
/* import-globals-from ../../../framework/test/shared-head.js */
/* import-globals-from ../../../framework/test/shared-redux-head.js */
/* import-globals-from ../../../commandline/test/helpers.js */
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
@ -14,9 +15,15 @@ Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-redux-head.js",
this);
// Import the GCLI test helper
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/commandline/test/helpers.js",
this);
const TEST_URI_ROOT = "http://example.com/browser/devtools/client/responsive.html/test/browser/";
SimpleTest.requestCompleteLog();
SimpleTest.waitForExplicitFinish();
DevToolsUtils.testing = true;
Services.prefs.setCharPref("devtools.devices.url",
@ -47,7 +54,7 @@ var openRDM = Task.async(function*(tab) {
var closeRDM = Task.async(function*(tab) {
info("Closing responsive design mode");
let manager = ResponsiveUIManager;
manager.closeIfNeeded(window, tab);
yield manager.closeIfNeeded(window, tab);
info("Responsive design mode closed");
});
@ -78,12 +85,43 @@ function addRDMTask(url, generator) {
});
}
var waitForFrameLoad = Task.async(function*(frame, targetURL) {
let window = frame.contentWindow;
if ((window.document.readyState == "complete" ||
window.document.readyState == "interactive") &&
window.location.href == targetURL) {
return;
function spawnViewportTask(ui, args, task) {
return ContentTask.spawn(ui.getViewportMessageManager(), args, task);
}
function waitForFrameLoad(ui, targetURL) {
return spawnViewportTask(ui, { targetURL }, function*(args) {
if ((content.document.readyState == "complete" ||
content.document.readyState == "interactive") &&
content.location.href == args.targetURL) {
return;
}
yield ContentTaskUtils.waitForEvent(this, "DOMContentLoaded");
});
}
function waitForViewportResizeTo(ui, width, height) {
return new Promise(resolve => {
let onResize = (_, data) => {
if (data.width != width || data.height != height) {
return;
}
ui.off("content-resize", onResize);
info(`Got content-resize to ${width} x ${height}`);
resolve();
};
info(`Waiting for content-resize to ${width} x ${height}`);
ui.on("content-resize", onResize);
});
}
var setViewportSize = Task.async(function*(ui, manager, width, height) {
let size = ui.getViewportSize();
info(`Current size: ${size.width} x ${size.height}, ` +
`set to: ${width} x ${height}`);
if (size.width != width || size.height != height) {
let resized = waitForViewportResizeTo(ui, width, height);
ui.setViewportSize(width, height);
yield resized;
}
yield once(frame, "load");
});

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

@ -0,0 +1,23 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const promise = require("promise");
module.exports = {
waitForMessage(mm, message) {
let deferred = promise.defer();
let onMessage = event => {
mm.removeMessageListener(message, onMessage);
deferred.resolve();
};
mm.addMessageListener(message, onMessage);
return deferred.promise;
},
};

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

@ -5,5 +5,6 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DevToolsModules(
'e10s.js',
'l10n.js',
)

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

@ -42,6 +42,22 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://devtools/shared/event-emitter.js");
const { findMostRelevantCssPropertyIndex } = require("./suggestion-picker");
/**
* Helper to check if the provided key matches one of the expected keys.
* Keys will be prefixed with DOM_VK_ and should match a key in nsIDOMKeyEvent.
*
* @param {String} key
* the key to check (can be a keyCode).
* @param {...String} keys
* list of possible keys allowed.
* @return {Boolean} true if the key matches one of the keys.
*/
function isKeyIn(key, ...keys) {
return keys.some(expectedKey => {
return key === Ci.nsIDOMKeyEvent["DOM_VK_" + expectedKey];
});
}
/**
* Mark a span editable. |editableField| will listen for the span to
* be focused and create an InlineEditor to handle text input.
@ -142,8 +158,7 @@ function editableItem(options, callback) {
// If focused by means other than a click, start editing by
// pressing enter or space.
element.addEventListener("keypress", function(evt) {
if (evt.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN ||
evt.charCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
if (isKeyIn(evt.keyCode, "RETURN") || isKeyIn(evt.charCode, "SPACE")) {
callback(element);
}
}, true);
@ -968,14 +983,20 @@ InplaceEditor.prototype = {
_onKeyPress: function(event) {
let prevent = false;
let isPlainText = this.contentType == CONTENT_TYPES.PLAIN_TEXT;
let increment = isPlainText ? 0 : this._getIncrement(event);
let key = event.keyCode;
let input = this.input;
// Use default cursor movement rather than providing auto-suggestions.
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_HOME ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_END ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN) {
let multilineNavigation = !this._isSingleLine() &&
isKeyIn(key, "UP", "DOWN", "LEFT", "RIGHT");
let isPlainText = this.contentType == CONTENT_TYPES.PLAIN_TEXT;
let isPopupOpen = this.popup && this.popup.isOpen;
let increment = 0;
if (!isPlainText && !multilineNavigation) {
increment = this._getIncrement(event);
}
if (isKeyIn(key, "HOME", "END", "PAGE_UP", "PAGE_DOWN")) {
this._preventSuggestions = true;
}
@ -984,48 +1005,40 @@ InplaceEditor.prototype = {
this._updateSize();
prevent = true;
cycling = true;
} else if (increment && this.popup && this.popup.isOpen) {
cycling = true;
}
if (isPopupOpen && isKeyIn(key, "UP", "DOWN", "PAGE_UP", "PAGE_DOWN")) {
prevent = true;
this._cycleCSSSuggestion(increment > 0);
cycling = true;
this._cycleCSSSuggestion(isKeyIn(key, "UP", "PAGE_UP"));
this._doValidation();
}
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_BACK_SPACE ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_DELETE ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_LEFT ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RIGHT) {
if (this.popup && this.popup.isOpen) {
if (isKeyIn(key, "BACK_SPACE", "DELETE", "LEFT", "RIGHT")) {
if (isPopupOpen) {
this.popup.hidePopup();
}
} else if (!cycling && !event.metaKey && !event.altKey && !event.ctrlKey) {
} else if (!cycling && !multilineNavigation &&
!event.metaKey && !event.altKey && !event.ctrlKey) {
this._maybeSuggestCompletion(true);
}
if (this.multiline &&
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN &&
event.shiftKey) {
if (this.multiline && event.shiftKey && isKeyIn(key, "RETURN")) {
prevent = false;
} else if (this._advanceChars(event.charCode, this.input.value,
this.input.selectionStart) ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB) {
} else if (
this._advanceChars(event.charCode, input.value, input.selectionStart) ||
isKeyIn(key, "RETURN", "TAB")) {
prevent = true;
let direction = FOCUS_FORWARD;
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB &&
event.shiftKey) {
if (this.stopOnShiftTab) {
direction = null;
} else {
direction = FOCUS_BACKWARD;
}
}
if ((this.stopOnReturn &&
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_RETURN) ||
(this.stopOnTab && event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB &&
!event.shiftKey)) {
let direction;
if ((this.stopOnReturn && isKeyIn(key, "RETURN")) ||
(this.stopOnTab && !event.shiftKey && isKeyIn(key, "TAB")) ||
(this.stopOnShiftTab && event.shiftKey && isKeyIn(key, "TAB"))) {
direction = null;
} else if (event.shiftKey && isKeyIn(key, "TAB")) {
direction = FOCUS_BACKWARD;
} else {
direction = FOCUS_FORWARD;
}
// Now we don't want to suggest anything as we are moving out.
@ -1037,10 +1050,7 @@ InplaceEditor.prototype = {
this._preventSuggestions = false;
}
let input = this.input;
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_TAB &&
this.contentType == CONTENT_TYPES.CSS_MIXED) {
if (isKeyIn(key, "TAB") && this.contentType == CONTENT_TYPES.CSS_MIXED) {
if (this.popup && input.selectionStart < input.selectionEnd) {
event.preventDefault();
input.setSelectionRange(input.selectionEnd, input.selectionEnd);
@ -1075,7 +1085,7 @@ InplaceEditor.prototype = {
}
this._clear();
} else if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_ESCAPE) {
} else if (isKeyIn(key, "ESCAPE")) {
// Cancel and blur ourselves.
// Now we don't want to suggest anything as we are moving out.
this._preventSuggestions = true;
@ -1088,11 +1098,11 @@ InplaceEditor.prototype = {
this._apply();
this._clear();
event.stopPropagation();
} else if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_SPACE) {
} else if (isKeyIn(key, "SPACE")) {
// No need for leading spaces here. This is particularly
// noticable when adding a property: it's very natural to type
// <name>: (which advances to the next property) then spacebar.
prevent = !this.input.value;
prevent = !input.value;
}
if (prevent) {
@ -1109,18 +1119,16 @@ InplaceEditor.prototype = {
const smallIncrement = 0.1;
let increment = 0;
let key = event.keyCode;
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_UP ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP) {
if (isKeyIn(key, "UP", "PAGE_UP")) {
increment = 1;
} else if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_DOWN ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN) {
} else if (isKeyIn(key, "DOWN", "PAGE_DOWN")) {
increment = -1;
}
if (event.shiftKey && !event.altKey) {
if (event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_UP ||
event.keyCode === Ci.nsIDOMKeyEvent.DOM_VK_PAGE_DOWN) {
if (isKeyIn(key, "PAGE_UP", "PAGE_DOWN")) {
increment *= largeIncrement;
} else {
increment *= mediumIncrement;

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

@ -38,6 +38,12 @@
}
}
/* Add element toolbar button */
#inspector-element-add-button {
list-style-image: url("chrome://devtools/skin/images/add.svg");
}
/* Tooltip: Events */
#devtools-tooltip-events-container {

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

@ -796,6 +796,16 @@
margin: 0;
}
.toolbox-panel {
display: -moz-box;
-moz-box-flex: 1;
visibility: collapse;
}
.toolbox-panel[selected] {
visibility: visible;
}
.devtools-tab {
-moz-appearance: none;
-moz-binding: url("chrome://global/content/bindings/general.xml#control-item");

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

@ -286,7 +286,9 @@ this.PushService = {
break;
case "idle-daily":
this._dropExpiredRegistrations();
this._dropExpiredRegistrations().catch(error => {
console.error("Failed to drop expired registrations on idle", error);
});
break;
case "perm-changed":
@ -441,6 +443,9 @@ this.PushService = {
return this._stopService(STOPPING_SERVICE_EVENT);
}
}
default:
console.error("Unexpected event in _changeServerURL", event);
return Promise.reject(new Error(`Unexpected event ${event}`));
}
},
@ -571,7 +576,7 @@ this.PushService = {
console.debug("stopService()");
if (this._state < PUSH_SERVICE_ACTIVATING) {
return;
return Promise.resolve();
}
this._stopObservers();

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

@ -567,7 +567,7 @@ const std::string Histogram::GetAsciiBucketRange(size_t i) const {
// Update histogram data with new sample.
void Histogram::Accumulate(Sample value, Count count, size_t index) {
sample_.AccumulateWithLinearStats(value, count, index);
sample_.Accumulate(value, count, index);
}
void Histogram::SetBucketRange(size_t i, Sample value) {
@ -720,9 +720,6 @@ void Histogram::WriteAsciiBucketGraph(double current_size, double max_size,
Histogram::SampleSet::SampleSet()
: counts_(),
sum_(0),
sum_squares_(0),
log_sum_(0),
log_sum_squares_(0),
redundant_count_(0),
mutex_("Histogram::SampleSet::SampleSet") {
}
@ -747,12 +744,11 @@ void Histogram::SampleSet::Accumulate(const OffTheBooksMutexAutoLock& ev,
DCHECK_GE(redundant_count_, 0);
}
void Histogram::SampleSet::AccumulateWithLinearStats(Sample value,
Count count,
size_t index) {
void Histogram::SampleSet::Accumulate(Sample value,
Count count,
size_t index) {
OffTheBooksMutexAutoLock locker(mutex_);
Accumulate(locker, value, count, index);
sum_squares_ += static_cast<int64_t>(count) * value * value;
}
Count Histogram::SampleSet::TotalCount(const OffTheBooksMutexAutoLock& ev)
@ -770,9 +766,6 @@ void Histogram::SampleSet::Add(const SampleSet& other) {
OffTheBooksMutexAutoLock locker(mutex_);
DCHECK_EQ(counts_.size(), other.counts_.size());
sum_ += other.sum_;
sum_squares_ += other.sum_squares_;
log_sum_ += other.log_sum_;
log_sum_squares_ += other.log_sum_squares_;
redundant_count_ += other.redundant_count_;
for (size_t index = 0; index < counts_.size(); ++index)
counts_[index] += other.counts_[index];
@ -868,7 +861,7 @@ Histogram::ClassType LinearHistogram::histogram_type() const {
}
void LinearHistogram::Accumulate(Sample value, Count count, size_t index) {
sample_.AccumulateWithLinearStats(value, count, index);
sample_.Accumulate(value, count, index);
}
void LinearHistogram::SetRangeDescriptions(

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

@ -353,7 +353,7 @@ class Histogram {
void Resize(const Histogram& histogram);
// Accessor for histogram to make routine additions.
void AccumulateWithLinearStats(Sample value, Count count, size_t index);
void Accumulate(Sample value, Count count, size_t index);
// Arithmetic manipulation of corresponding elements of the set.
void Add(const SampleSet& other);
@ -375,15 +375,6 @@ class Histogram {
int64_t sum(const OffTheBooksMutexAutoLock& ev) const {
return sum_;
}
uint64_t sum_squares(const OffTheBooksMutexAutoLock& ev) const {
return sum_squares_;
}
double log_sum(const OffTheBooksMutexAutoLock& ev) const {
return log_sum_;
}
double log_sum_squares(const OffTheBooksMutexAutoLock& ev) const {
return log_sum_squares_;
}
int64_t redundant_count(const OffTheBooksMutexAutoLock& ev) const {
return redundant_count_;
}
@ -396,9 +387,6 @@ class Histogram {
const SampleSet& operator=(const SampleSet& other) {
counts_ = other.counts_;
sum_ = other.sum_;
sum_squares_ = other.sum_squares_;
log_sum_ = other.log_sum_;
log_sum_squares_ = other.log_sum_squares_;
redundant_count_ = other.redundant_count_;
return *this;
}
@ -415,13 +403,6 @@ class Histogram {
// Save simple stats locally. Note that this MIGHT get done in base class
// without shared memory at some point.
int64_t sum_; // sum of samples.
uint64_t sum_squares_; // sum of squares of samples.
// These fields may or may not be updated at the discretion of the
// histogram. We use the natural log and compute ln(sample+1) so that
// zeros are handled sanely.
double log_sum_; // sum of logs of samples.
double log_sum_squares_; // sum of squares of logs of samples
// To help identify memory corruption, we reduntantly save the number of
// samples we've accumulated into all of our buckets. We can compare this

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

@ -38,8 +38,13 @@
<module name="FileTabCharacter"> <!-- No tabs! -->
<property name="eachLine" value="true"/>
</module>
<module name="RegexpSingleline"> <!-- excess whitespace -->
<property name="format" value="\s+$"/>
<property name="message" value="Trailing whitespace"/>
</module>
<module name="TreeWalker">
<module name="GenericWhitespace"/> <!-- whitespace for generics -->
<module name="NoLineWrap">
<property name="tokens" value="IMPORT,PACKAGE_DEF"/>
</module>

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

@ -4,16 +4,14 @@
package org.mozilla.gecko;
import android.content.res.Resources;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.gfx.BitmapUtils.BitmapLoader;
import org.mozilla.gecko.gfx.ImmutableViewportMetrics;
import org.mozilla.gecko.gfx.Layer;
import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.gfx.LayerView.DrawListener;
import org.mozilla.gecko.menu.GeckoMenu;
import org.mozilla.gecko.menu.GeckoMenuItem;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.text.TextSelection;
import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils;
@ -21,7 +19,6 @@ import org.mozilla.gecko.ActionModeCompat.Callback;
import org.mozilla.gecko.AppConstants.Versions;
import android.content.Context;
import android.app.Activity;
import android.graphics.drawable.Drawable;
import android.view.Menu;
import android.view.MenuItem;
@ -36,8 +33,7 @@ import java.util.TimerTask;
import android.util.Log;
import android.view.View;
class TextSelection extends Layer implements GeckoEventListener,
LayerView.DynamicToolbarListener {
class ActionBarTextSelection extends Layer implements TextSelection, GeckoEventListener, LayerView.DynamicToolbarListener {
private static final String LOGTAG = "GeckoTextSelection";
private static final int SHUTDOWN_DELAY_MS = 250;
@ -74,9 +70,9 @@ class TextSelection extends Layer implements GeckoEventListener,
};
private ActionModeTimerTask mActionModeTimerTask;
TextSelection(TextSelectionHandle anchorHandle,
TextSelectionHandle caretHandle,
TextSelectionHandle focusHandle) {
ActionBarTextSelection(TextSelectionHandle anchorHandle,
TextSelectionHandle caretHandle,
TextSelectionHandle focusHandle) {
this.anchorHandle = anchorHandle;
this.caretHandle = caretHandle;
this.focusHandle = focusHandle;
@ -89,7 +85,10 @@ class TextSelection extends Layer implements GeckoEventListener,
}
}
};
}
@Override
public void create() {
// Only register listeners if we have valid start/middle/end handles
if (anchorHandle == null || caretHandle == null || focusHandle == null) {
Log.e(LOGTAG, "Failed to initialize text selection because at least one handle is null");
@ -106,7 +105,14 @@ class TextSelection extends Layer implements GeckoEventListener,
}
}
void destroy() {
@Override
public boolean dismiss() {
// We do not call endActionMode() here because this is already handled by the activity.
return false;
}
@Override
public void destroy() {
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"TextSelection:ActionbarInit",
"TextSelection:ActionbarStatus",
@ -159,8 +165,8 @@ class TextSelection extends Layer implements GeckoEventListener,
LayerView layerView = GeckoAppShell.getLayerView();
if (layerView != null) {
layerView.addDrawListener(mDrawListener);
layerView.addLayer(TextSelection.this);
layerView.getDynamicToolbarAnimator().addTranslationListener(TextSelection.this);
layerView.addLayer(ActionBarTextSelection.this);
layerView.getDynamicToolbarAnimator().addTranslationListener(ActionBarTextSelection.this);
}
if (handles.length() > 1)
@ -174,8 +180,8 @@ class TextSelection extends Layer implements GeckoEventListener,
LayerView layerView = GeckoAppShell.getLayerView();
if (layerView != null) {
layerView.removeDrawListener(mDrawListener);
layerView.removeLayer(TextSelection.this);
layerView.getDynamicToolbarAnimator().removeTranslationListener(TextSelection.this);
layerView.removeLayer(ActionBarTextSelection.this);
layerView.getDynamicToolbarAnimator().removeTranslationListener(ActionBarTextSelection.this);
}
mActionModeTimerTask = new ActionModeTimerTask();
@ -303,7 +309,7 @@ class TextSelection extends Layer implements GeckoEventListener,
private class TextSelectionActionModeCallback implements Callback {
private JSONArray mItems;
private ActionModeCompat mActionMode;
public TextSelectionActionModeCallback(JSONArray items) {
mItems = items;
}

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

@ -614,6 +614,20 @@ public class BrowserApp extends GeckoApp
mActionBar = (ActionModeCompatView) findViewById(R.id.actionbar);
mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar);
mBrowserToolbar.setTouchEventInterceptor(new TouchEventInterceptor() {
@Override
public boolean onInterceptTouchEvent(View view, MotionEvent event) {
// Manually dismiss text selection bar if it's not overlaying the toolbar.
mTextSelection.dismiss();
return false;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
mProgressView = (ToolbarProgressView) findViewById(R.id.progress);
mBrowserToolbar.setProgressBar(mProgressView);
@ -946,6 +960,10 @@ public class BrowserApp extends GeckoApp
@Override
public void onBackPressed() {
if (mTextSelection.dismiss()) {
return;
}
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
super.onBackPressed();
return;
@ -3786,6 +3804,7 @@ public class BrowserApp extends GeckoApp
// Launched from a "content notification"
if (intent.hasExtra(CheckForUpdatesAction.EXTRA_CONTENT_NOTIFICATION)) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.NOTIFICATION, "content_update");
Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, TelemetryContract.Method.INTENT, "content_update");
}
}

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

@ -234,17 +234,17 @@ public class CrashReporter extends AppCompatActivity
private void savePrefs() {
SharedPreferences.Editor editor = GeckoSharedPrefs.forApp(this).edit();
final boolean allowContact = ((CheckBox) findViewById(R.id.allow_contact)).isChecked();
final boolean includeUrl = ((CheckBox) findViewById(R.id.include_url)).isChecked();
final boolean sendReport = ((CheckBox) findViewById(R.id.send_report)).isChecked();
final String contactEmail = ((EditText) findViewById(R.id.email)).getText().toString();
editor.putBoolean(PREFS_ALLOW_CONTACT, allowContact);
editor.putBoolean(PREFS_INCLUDE_URL, includeUrl);
editor.putBoolean(PREFS_SEND_REPORT, sendReport);
editor.putString(PREFS_CONTACT_EMAIL, contactEmail);
// A slight performance improvement via async apply() vs. blocking on commit().
editor.apply();
}

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

@ -198,7 +198,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
addView(mAutoCompleteList);
}
AutoCompleteListAdapter adapter = new AutoCompleteListAdapter(mContext, R.layout.autocomplete_list_item);
adapter.populateSuggestionsList(suggestions);
mAutoCompleteList.setAdapter(adapter);

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

@ -5,9 +5,6 @@
package org.mozilla.gecko;
import android.content.ContentResolver;
import android.widget.AdapterView;
import android.widget.Button;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
import org.mozilla.gecko.db.BrowserDB;
@ -34,6 +31,8 @@ import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.prompts.PromptService;
import org.mozilla.gecko.restrictions.Restrictions;
import org.mozilla.gecko.tabqueue.TabQueueHelper;
import org.mozilla.gecko.text.FloatingToolbarTextSelection;
import org.mozilla.gecko.text.TextSelection;
import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.ActivityUtils;
@ -48,10 +47,10 @@ import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.PrefUtils;
import org.mozilla.gecko.util.ThreadUtils;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ContentResolver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -96,6 +95,8 @@ import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.AbsoluteLayout;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ListView;
import android.widget.RelativeLayout;
@ -179,7 +180,7 @@ public abstract class GeckoApp
private ContactService mContactService;
private PromptService mPromptService;
private TextSelection mTextSelection;
protected TextSelection mTextSelection;
protected DoorHangerPopup mDoorHangerPopup;
protected FormAssistPopup mFormAssistPopup;
@ -1268,6 +1269,16 @@ public abstract class GeckoApp
// Use global layout state change to kick off additional initialization
mMainLayout.getViewTreeObserver().addOnGlobalLayoutListener(this);
if (Versions.preMarshmallow || !AppConstants.NIGHTLY_BUILD) {
mTextSelection = new ActionBarTextSelection(
(TextSelectionHandle) findViewById(R.id.anchor_handle),
(TextSelectionHandle) findViewById(R.id.caret_handle),
(TextSelectionHandle) findViewById(R.id.focus_handle));
} else {
mTextSelection = new FloatingToolbarTextSelection(this, mLayerView);
}
mTextSelection.create();
// Determine whether we should restore tabs.
mShouldRestore = getSessionRestoreState(savedInstanceState);
if (mShouldRestore && savedInstanceState != null) {
@ -1541,10 +1552,6 @@ public abstract class GeckoApp
mPromptService = new PromptService(this);
mTextSelection = new TextSelection((TextSelectionHandle) findViewById(R.id.anchor_handle),
(TextSelectionHandle) findViewById(R.id.caret_handle),
(TextSelectionHandle) findViewById(R.id.focus_handle));
// Trigger the completion of the telemetry timer that wraps activity startup,
// then grab the duration to give to FHR.
mJavaUiStartupTimer.stop();

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

@ -28,7 +28,7 @@ import org.mozilla.gecko.util.ThreadUtils;
import java.io.File;
import java.lang.reflect.Method;
public class GeckoApplication extends Application
public class GeckoApplication extends Application
implements ContextGetter {
private static final String LOG_TAG = "GeckoApplication";
@ -110,7 +110,7 @@ public class GeckoApplication extends Application
// shutdown, closing the disk cache cleanly. If the android
// low memory killer subsequently kills us, the disk cache will
// be left in a consistent state, avoiding costly cleanup and
// re-creation.
// re-creation.
GeckoThread.onPause();
mPausedGecko = true;

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

@ -39,6 +39,7 @@ import org.mozilla.gecko.util.INISection;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.SharedPreferences;
import android.support.annotation.WorkerThread;
import android.text.TextUtils;
@ -91,7 +92,7 @@ public final class GeckoProfile {
* Access to this member should be synchronized to avoid
* races during creation -- particularly between getDir and GeckoView#init.
*
* Not final because this is lazily computed.
* Not final because this is lazily computed.
*/
private File mProfileDir;
@ -535,6 +536,7 @@ public final class GeckoProfile {
return false;
}
@RobocopTarget
public boolean inGuestMode() {
return mInGuestMode;
}
@ -547,6 +549,7 @@ public final class GeckoProfile {
}
}
@RobocopTarget
public String getName() {
return mName;
}
@ -555,6 +558,7 @@ public final class GeckoProfile {
return CUSTOM_PROFILE.equals(mName);
}
@RobocopTarget
public synchronized File getDir() {
forceCreate();
return mProfileDir;
@ -680,15 +684,38 @@ public final class GeckoProfile {
}
/**
* @return the profile creation date in the format returned by {@link System#currentTimeMillis()} or -1 if the value
* was not found.
* Gets the profile creation date and persists it if it had to be generated.
*
* To get this value, we first look in times.json. If that could not be accessed, we
* return the package's first install date. This is not a perfect solution because a
* user may have large gap between install time and first use.
*
* A more correct algorithm could be the one performed by the JS code in ProfileAge.jsm
* getOldestProfileTimestamp: walk the tree and return the oldest timestamp on the files
* within the profile. However, since times.json will only not exist for the small
* number of really old profiles, we're okay with the package install date compromise for
* simplicity.
*
* @return the profile creation date in the format returned by {@link System#currentTimeMillis()}
* or -1 if the value could not be persisted.
*/
@WorkerThread
public long getProfileCreationDate() {
public long getAndPersistProfileCreationDate(final Context context) {
try {
return getProfileCreationDateFromTimesFile();
} catch (final IOException e) {
return getAndPersistProfileCreationDateFromFilesystem();
Log.d(LOGTAG, "Unable to retrieve profile creation date from times.json. Getting from system...");
final long packageInstallMillis = org.mozilla.gecko.util.ContextUtils.getPackageInstallTime(context);
try {
persistProfileCreationDateToTimesFile(packageInstallMillis);
} catch (final IOException ioEx) {
// We return -1 to ensure the profileCreationDate
// will either be an error (-1) or a consistent value.
Log.w(LOGTAG, "Unable to persist profile creation date - returning -1");
return -1;
}
return packageInstallMillis;
}
}
@ -703,14 +730,17 @@ public final class GeckoProfile {
}
}
/**
* TODO (bug 1246816): Implement ProfileAge.jsm - getOldestProfileTimestamp. Persist results to times.json.
* Update comment in getProfileCreationDate too.
* @return -1 until implemented.
*/
@WorkerThread
private long getAndPersistProfileCreationDateFromFilesystem() {
return -1;
private void persistProfileCreationDateToTimesFile(final long profileCreationMillis) throws IOException {
final JSONObject obj = new JSONObject();
try {
obj.put(PROFILE_CREATION_DATE_JSON_ATTR, profileCreationMillis);
} catch (final JSONException e) {
// Don't log to avoid leaking data in JSONObject.
throw new IOException("Unable to persist profile creation date to times file");
}
Log.d(LOGTAG, "Attempting to write new profile creation date");
writeFile(TIMES_PATH, obj.toString()); // Ideally we'd throw here too.
}
/**

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

@ -939,7 +939,7 @@ public class GeckoSmsManager
for (int i = 1; i < mNumbersCount; ++i) {
formatter.format(", '%s'", mNumbers[i]);
}
formatter.format(") AND ");
}

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

@ -637,7 +637,7 @@ public class GeckoView extends LayerView
* Defaults to cancel requests.
*/
public void onAlert(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result);
/**
* Tell the host application to display a confirmation dialog.
* @param view The GeckoView that initiated the callback.
@ -647,7 +647,7 @@ public class GeckoView extends LayerView
* Defaults to cancel requests.
*/
public void onConfirm(GeckoView view, GeckoView.Browser browser, String message, GeckoView.PromptResult result);
/**
* Tell the host application to display an input prompt dialog.
* @param view The GeckoView that initiated the callback.
@ -658,7 +658,7 @@ public class GeckoView extends LayerView
* Defaults to cancel requests.
*/
public void onPrompt(GeckoView view, GeckoView.Browser browser, String message, String defaultValue, GeckoView.PromptResult result);
/**
* Tell the host application to display a remote debugging request dialog.
* @param view The GeckoView that initiated the callback.
@ -685,7 +685,7 @@ public class GeckoView extends LayerView
* @param url The resource being loaded.
*/
public void onPageStart(GeckoView view, GeckoView.Browser browser, String url);
/**
* A Browser has finished loading content from the network.
* @param view The GeckoView that initiated the callback.

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

@ -53,7 +53,7 @@ class TextSelectionHandle extends ImageView implements View.OnTouchListener {
private float mLeft;
private float mTop;
private boolean mIsRTL;
private boolean mIsRTL;
private PointF mGeckoPoint;
private PointF mTouchStart;

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

@ -110,7 +110,7 @@ public class PropertyAnimator implements Runnable {
float interpolation = mInterpolator.getInterpolation(timePassed * mDurationReciprocal);
for (ElementHolder element : mElementsList) {
for (ElementHolder element : mElementsList) {
float delta = element.from + ((element.to - element.from) * interpolation);
invalidate(element, delta);
}

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

@ -157,7 +157,7 @@ public abstract class SQLiteBridgeContentProvider extends ContentProvider {
}
return bridge;
}
/**
* Returns the absolute path of a database file depending on the specified profile and dbName.
* @param profile
@ -183,7 +183,7 @@ public abstract class SQLiteBridgeContentProvider extends ContentProvider {
* current provider instance.
* @param profile
* the id of the profile to be used to retrieve the related SQLiteBridge
* @return the <code>SQLiteBridge</code> related to the specified profile id or <code>null</code> if it was
* @return the <code>SQLiteBridge</code> related to the specified profile id or <code>null</code> if it was
* not possible to retrieve a valid SQLiteBridge
*/
private SQLiteBridge getDatabaseForProfile(String profile) {
@ -202,7 +202,7 @@ public abstract class SQLiteBridgeContentProvider extends ContentProvider {
return db;
}
final String dbPath = getDatabasePathForProfile(profile, dbName);
if (dbPath == null) {
if (dbPath == null) {
Log.e(mLogTag, "Failed to get a valid db path for profile '" + profile + "'' dbName '" + dbName + "'");
return null;
}
@ -232,7 +232,7 @@ public abstract class SQLiteBridgeContentProvider extends ContentProvider {
* Returns a SQLiteBridge object according to the specified file path.
* @param dbPath
* the path of the file to be used to retrieve the related SQLiteBridge
* @return the <code>SQLiteBridge</code> related to the specified file path or <code>null</code> if it was
* @return the <code>SQLiteBridge</code> related to the specified file path or <code>null</code> if it was
* not possible to retrieve a valid <code>SQLiteBridge</code>
*
*/
@ -255,7 +255,7 @@ public abstract class SQLiteBridgeContentProvider extends ContentProvider {
* Returns a SQLiteBridge object to be used to perform operations on the given <code>Uri</code>.
* @param uri
* the <code>Uri</code> to be used to retrieve the related SQLiteBridge
* @return a <code>SQLiteBridge</code> object to be used on the given uri or <code>null</code> if it was
* @return a <code>SQLiteBridge</code> object to be used on the given uri or <code>null</code> if it was
* not possible to retrieve a valid <code>SQLiteBridge</code>
*
*/

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

@ -567,7 +567,7 @@ public class Distribution {
} else {
value = status / 100;
}
Telemetry.addToHistogram(HISTOGRAM_CODE_CATEGORY, value);
if (status != 200) {

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

@ -11,6 +11,8 @@ import android.content.Intent;
import android.database.Cursor;
import org.json.JSONException;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.TelemetryContract;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserDB;
import org.mozilla.gecko.db.UrlAnnotations;
@ -81,6 +83,8 @@ public class WithdrawSubscriptionsAction extends FeedAction {
log("Removing subscription for feed: " + subscription.getFeedUrl());
urlAnnotations.deleteFeedSubscription(resolver, subscription);
Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.SERVICE, "content_update");
}
}
} catch (JSONException e) {

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

@ -380,7 +380,7 @@ final class DisplayPortCalculator {
margins.left = xAmount / 2.0f;
}
margins.right = xAmount - margins.left;
if (velocity.y > VELOCITY_THRESHOLD) {
margins.top = yAmount * REVERSE_BUFFER;
} else if (velocity.y < -VELOCITY_THRESHOLD) {

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

@ -664,6 +664,10 @@ public class LayerView extends ScrollView implements Tabs.OnTabsChangedListener
return super.getOverScrollMode();
}
public float getZoomFactor() {
return getLayerClient().getViewportMetrics().zoomFactor;
}
@Override
public void onFocusChanged (boolean gainFocus, int direction, Rect previouslyFocusedRect) {
super.onFocusChanged(gainFocus, direction, previouslyFocusedRect);

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

@ -67,7 +67,7 @@ public class TextureGenerator {
Log.e(LOGTAG, String.format("Failed to generate textures: %#x", error), new Exception());
return;
}
for (int i = 0; i < numNeeded; i++) {
mTextureIds.offer(textures[i]);
}

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

@ -52,7 +52,7 @@ public class LightweightTheme implements GeckoEventListener {
private boolean mIsLight;
public static interface OnChangeListener {
// The View should change its background/text color.
// The View should change its background/text color.
public void onLightweightThemeChanged();
// The View should reset to its default background/text color.

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

@ -34,7 +34,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class GeckoMenu extends ListView
public class GeckoMenu extends ListView
implements Menu,
AdapterView.OnItemClickListener,
GeckoMenuItem.OnShowAsActionChangedListener {
@ -76,7 +76,7 @@ public class GeckoMenu extends ListView
/*
* An interface for a presenter of action-items.
* Either an Activity or a View can be a presenter, that can watch for events
* and add/remove action-items. If not ActionItemBarPresenter, the menu uses a
* and add/remove action-items. If not ActionItemBarPresenter, the menu uses a
* DefaultActionItemBar, that shows the action-items as a header over list-view.
*/
public static interface ActionItemBarPresenter {
@ -509,7 +509,7 @@ public class GeckoMenu extends ListView
mPrimaryActionItems.remove(item);
mItems.remove(item);
if (mPrimaryActionItems.size() == 0 &&
if (mPrimaryActionItems.size() == 0 &&
mPrimaryActionItemBar instanceof DefaultActionItemBar) {
removePrimaryActionBarView();
}
@ -840,7 +840,7 @@ public class GeckoMenu extends ListView
// Initialize the view.
view.setShowIcon(mShowIcons);
view.initialize(item);
return (View) view;
return (View) view;
}
@Override

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

@ -11,7 +11,7 @@ import android.view.MenuItem;
import android.view.SubMenu;
import android.view.View;
public class GeckoSubMenu extends GeckoMenu
public class GeckoSubMenu extends GeckoMenu
implements SubMenu {
private static final String LOGTAG = "GeckoSubMenu";
@ -65,7 +65,7 @@ public class GeckoSubMenu extends GeckoMenu
}
@Override
public SubMenu setHeaderView(View view) {
public SubMenu setHeaderView(View view) {
return this;
}

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

@ -78,7 +78,7 @@ public class MenuItemDefault extends TextView
if (item == null)
return;
setTitle(item.getTitle());
setTitle(item.getTitle());
setIcon(item.getIcon());
setEnabled(item.isEnabled());
setCheckable(item.isCheckable());

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

@ -16,7 +16,7 @@ import android.widget.LinearLayout;
/**
* The outer container for the custom menu. On phones with h/w menu button,
* this is given to Android which inflates it to the right panel. On phones
* this is given to Android which inflates it to the right panel. On phones
* with s/w menu button, this is added to a MenuPopup.
*/
public class MenuPanel extends LinearLayout {

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

@ -59,7 +59,7 @@ class MultiChoicePreference extends DialogPreference implements DialogInterface.
public void setEntries(CharSequence[] entries) {
mEntries = entries.clone();
}
/**
* @param entriesResId The entries array as a resource.
*/
@ -108,7 +108,7 @@ class MultiChoicePreference extends DialogPreference implements DialogInterface.
/**
* The list of translated strings corresponding to each preference.
*
*
* @return The array of entries.
*/
public CharSequence[] getEntries() {
@ -117,7 +117,7 @@ class MultiChoicePreference extends DialogPreference implements DialogInterface.
/**
* The list of values corresponding to each preference.
*
*
* @return The array of values.
*/
public CharSequence[] getEntryValues() {
@ -127,7 +127,7 @@ class MultiChoicePreference extends DialogPreference implements DialogInterface.
/**
* The list of initial values for each preference. Each string in this list
* should be either "true" or "false".
*
*
* @return The array of initial values.
*/
public CharSequence[] getInitialValues() {
@ -142,7 +142,7 @@ class MultiChoicePreference extends DialogPreference implements DialogInterface.
/**
* The list of values for each preference. These values are updated after
* the dialog has been displayed.
*
*
* @return The array of values.
*/
public Set<String> getValues() {

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

@ -145,7 +145,7 @@ public class IconGridInput extends PromptInput implements OnItemClickListener {
lp.width = lp.height = mIconSize;
}
}
private class IconGridItem {
final String label;
final String description;

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

@ -280,7 +280,7 @@ public class Prompt implements OnClickListener, OnCancelListener, OnItemClickLis
* @param listItems
* The items to add.
* @param choiceMode
* One of the ListView.CHOICE_MODE constants to designate whether this list shows checkmarks, radios buttons, or nothing.
* One of the ListView.CHOICE_MODE constants to designate whether this list shows checkmarks, radios buttons, or nothing.
*/
private void addListItems(AlertDialog.Builder builder, PromptListItem[] listItems, int choiceMode) {
switch(choiceMode) {

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

@ -218,7 +218,7 @@ public class PromptInput {
mView = (View)input;
} else if (mType.equals("datetime-local") || mType.equals("datetime")) {
DateTimePicker input = new DateTimePicker(context, "yyyy-MM-dd HH:mm", mValue.replace("T"," ").replace("Z", ""),
DateTimePicker.PickersState.DATETIME,
DateTimePicker.PickersState.DATETIME,
mMinValue.replace("T"," ").replace("Z",""), mMaxValue.replace("T"," ").replace("Z", ""));
input.toggleCalendar(true);
mView = (View)input;

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

@ -27,7 +27,7 @@ public class TabHistoryController {
};
public interface OnShowTabHistory {
void onShowHistory(List<TabHistoryPage>historyPageList, int toIndex);
void onShowHistory(List<TabHistoryPage> historyPageList, int toIndex);
}
public TabHistoryController(OnShowTabHistory showTabHistoryListener) {

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