mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-10-06 15:27:30 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
9
toolkit/content/Makefile.in
Normal file
9
toolkit/content/Makefile.in
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#
|
||||
# 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/.
|
||||
|
||||
DEFINES += \
|
||||
-DCXXFLAGS='$(CXXFLAGS)' \
|
||||
-DCPPFLAGS='$(CPPFLAGS)' \
|
||||
$(NULL)
|
||||
48
toolkit/content/TopLevelVideoDocument.js
Normal file
48
toolkit/content/TopLevelVideoDocument.js
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* 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";
|
||||
|
||||
// <video> is used for top-level audio documents as well
|
||||
let videoElement = document.getElementsByTagName("video")[0];
|
||||
|
||||
// 1. Handle fullscreen mode;
|
||||
// 2. Send keystrokes to the video element if the body element is focused,
|
||||
// to be received by the event listener in videocontrols.xml.
|
||||
document.addEventListener("keypress", ev => {
|
||||
if (ev.synthetic) // prevent recursion
|
||||
return;
|
||||
|
||||
// Maximize the standalone video when pressing F11,
|
||||
// but ignore audio elements
|
||||
if (ev.key == "F11" && videoElement.videoWidth != 0 && videoElement.videoHeight != 0) {
|
||||
// If we're in browser fullscreen mode, it means the user pressed F11
|
||||
// while browser chrome or another tab had focus.
|
||||
// Don't break leaving that mode, so do nothing here.
|
||||
if (window.fullScreen) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're not in broser fullscreen mode, prevent entering into that,
|
||||
// so we don't end up there after pressing Esc.
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
|
||||
if (!document.mozFullScreenElement) {
|
||||
videoElement.mozRequestFullScreen();
|
||||
} else {
|
||||
document.mozCancelFullScreen();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the video element is focused, so it already receives
|
||||
// keystrokes, and don't send it another one from here.
|
||||
if (document.activeElement == videoElement)
|
||||
return;
|
||||
|
||||
let newEvent = new KeyboardEvent("keypress", ev);
|
||||
newEvent.synthetic = true;
|
||||
videoElement.dispatchEvent(newEvent);
|
||||
});
|
||||
7
toolkit/content/XPCNativeWrapper.js
Normal file
7
toolkit/content/XPCNativeWrapper.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/* 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/. */
|
||||
|
||||
/*
|
||||
* Moved to C++ implementation in XPConnect. See bug 281988.
|
||||
*/
|
||||
32
toolkit/content/about.js
Normal file
32
toolkit/content/about.js
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* 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/. */
|
||||
|
||||
// get release notes and vendor URL from prefs
|
||||
var formatter = Components.classes["@mozilla.org/toolkit/URLFormatterService;1"]
|
||||
.getService(Components.interfaces.nsIURLFormatter);
|
||||
var releaseNotesURL = formatter.formatURLPref("app.releaseNotesURL");
|
||||
if (releaseNotesURL != "about:blank") {
|
||||
var relnotes = document.getElementById("releaseNotesURL");
|
||||
relnotes.setAttribute("href", releaseNotesURL);
|
||||
relnotes.parentNode.removeAttribute("hidden");
|
||||
}
|
||||
|
||||
var vendorURL = formatter.formatURLPref("app.vendorURL");
|
||||
if (vendorURL != "about:blank") {
|
||||
var vendor = document.getElementById("vendorURL");
|
||||
vendor.setAttribute("href", vendorURL);
|
||||
}
|
||||
|
||||
// insert the version of the XUL application (!= XULRunner platform version)
|
||||
var versionNum = Components.classes["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Components.interfaces.nsIXULAppInfo)
|
||||
.version;
|
||||
var version = document.getElementById("version");
|
||||
version.textContent += " " + versionNum;
|
||||
|
||||
// append user agent
|
||||
var ua = navigator.userAgent;
|
||||
if (ua) {
|
||||
document.getElementById("buildID").textContent += " " + ua;
|
||||
}
|
||||
41
toolkit/content/about.xhtml
Normal file
41
toolkit/content/about.xhtml
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
|
||||
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
||||
%brandDTD;
|
||||
<!ENTITY % aboutDTD SYSTEM "chrome://global/locale/about.dtd" >
|
||||
%aboutDTD;
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
]>
|
||||
|
||||
<!-- 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>About:</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
<div id="aboutLogoContainer">
|
||||
<a id="vendorURL">
|
||||
<img src="about:logo" alt="&brandShortName;"/>
|
||||
<p id="version">&about.version;</p>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<ul id="aboutPageList">
|
||||
<li>&about.credits.beforeLink;<a href="about:credits">&about.credits.linkTitle;</a>&about.credits.afterLink;</li>
|
||||
<li>&about.license.beforeTheLink;<a href="about:license">&about.license.linkTitle;</a>&about.license.afterTheLink;</li>
|
||||
<li hidden="true">&about.relnotes.beforeTheLink;<a id="releaseNotesURL">&about.relnotes.linkTitle;</a>&about.relnotes.afterTheLink;</li>
|
||||
<li>&about.buildconfig.beforeTheLink;<a href="about:buildconfig">&about.buildconfig.linkTitle;</a>&about.buildconfig.afterTheLink;</li>
|
||||
<li id="buildID">&about.buildIdentifier;</li>
|
||||
<script type="application/javascript" src="chrome://global/content/about.js"/>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
47
toolkit/content/aboutAbout.js
Normal file
47
toolkit/content/aboutAbout.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/* 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/. */
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var gProtocols = [];
|
||||
var gContainer;
|
||||
window.onload = function () {
|
||||
gContainer = document.getElementById("abouts");
|
||||
findAbouts();
|
||||
}
|
||||
|
||||
function findAbouts() {
|
||||
var ios = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
||||
for (var cid in Cc) {
|
||||
var result = cid.match(/@mozilla.org\/network\/protocol\/about;1\?what\=(.*)$/);
|
||||
if (result) {
|
||||
var aboutType = result[1];
|
||||
var contract = "@mozilla.org/network/protocol/about;1?what=" + aboutType;
|
||||
try {
|
||||
var am = Cc[contract].getService(Ci.nsIAboutModule);
|
||||
var uri = ios.newURI("about:"+aboutType, null, null);
|
||||
var flags = am.getURIFlags(uri);
|
||||
if (!(flags & Ci.nsIAboutModule.HIDE_FROM_ABOUTABOUT)) {
|
||||
gProtocols.push(aboutType);
|
||||
}
|
||||
} catch (e) {
|
||||
// getService might have thrown if the component doesn't actually
|
||||
// implement nsIAboutModule
|
||||
}
|
||||
}
|
||||
}
|
||||
gProtocols.sort().forEach(createProtocolListing);
|
||||
}
|
||||
|
||||
function createProtocolListing(aProtocol) {
|
||||
var uri = "about:" + aProtocol;
|
||||
var li = document.createElement("li");
|
||||
var link = document.createElement("a");
|
||||
var text = document.createTextNode(uri);
|
||||
|
||||
link.href = uri;
|
||||
link.appendChild(text);
|
||||
li.appendChild(link);
|
||||
gContainer.appendChild(li);
|
||||
}
|
||||
25
toolkit/content/aboutAbout.xhtml
Normal file
25
toolkit/content/aboutAbout.xhtml
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd" [
|
||||
<!ENTITY % aboutAboutDTD SYSTEM "chrome://global/locale/aboutAbout.dtd" >
|
||||
%aboutAboutDTD;
|
||||
<!ENTITY % globalDTD SYSTEM "chrome://global/locale/global.dtd">
|
||||
%globalDTD;
|
||||
]>
|
||||
|
||||
<!-- 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>&aboutAbout.title;</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
|
||||
<script type="application/javascript" src="chrome://global/content/aboutAbout.js"></script>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
<h1>&aboutAbout.title;</h1>
|
||||
<p><em>&aboutAbout.note;</em></p>
|
||||
<ul id="abouts" class="columns"></ul>
|
||||
</body>
|
||||
</html>
|
||||
414
toolkit/content/aboutNetworking.js
Normal file
414
toolkit/content/aboutNetworking.js
Normal file
|
|
@ -0,0 +1,414 @@
|
|||
/* 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';
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var Cc = Components.classes;
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
const FileUtils = Cu.import("resource://gre/modules/FileUtils.jsm").FileUtils
|
||||
const gEnv = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment);
|
||||
const gDashboard = Cc['@mozilla.org/network/dashboard;1']
|
||||
.getService(Ci.nsIDashboard);
|
||||
const gDirServ = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIDirectoryServiceProvider);
|
||||
|
||||
const gRequestNetworkingData = {
|
||||
"http": gDashboard.requestHttpConnections,
|
||||
"sockets": gDashboard.requestSockets,
|
||||
"dns": gDashboard.requestDNSInfo,
|
||||
"websockets": gDashboard.requestWebsocketConnections
|
||||
};
|
||||
const gDashboardCallbacks = {
|
||||
"http": displayHttp,
|
||||
"sockets": displaySockets,
|
||||
"dns": displayDns,
|
||||
"websockets": displayWebsockets
|
||||
};
|
||||
|
||||
const REFRESH_INTERVAL_MS = 3000;
|
||||
|
||||
function col(element) {
|
||||
let col = document.createElement('td');
|
||||
let content = document.createTextNode(element);
|
||||
col.appendChild(content);
|
||||
return col;
|
||||
}
|
||||
|
||||
function displayHttp(data) {
|
||||
let cont = document.getElementById('http_content');
|
||||
let parent = cont.parentNode;
|
||||
let new_cont = document.createElement('tbody');
|
||||
new_cont.setAttribute('id', 'http_content');
|
||||
|
||||
for (let i = 0; i < data.connections.length; i++) {
|
||||
let row = document.createElement('tr');
|
||||
row.appendChild(col(data.connections[i].host));
|
||||
row.appendChild(col(data.connections[i].port));
|
||||
row.appendChild(col(data.connections[i].spdy));
|
||||
row.appendChild(col(data.connections[i].ssl));
|
||||
row.appendChild(col(data.connections[i].active.length));
|
||||
row.appendChild(col(data.connections[i].idle.length));
|
||||
new_cont.appendChild(row);
|
||||
}
|
||||
|
||||
parent.replaceChild(new_cont, cont);
|
||||
}
|
||||
|
||||
function displaySockets(data) {
|
||||
let cont = document.getElementById('sockets_content');
|
||||
let parent = cont.parentNode;
|
||||
let new_cont = document.createElement('tbody');
|
||||
new_cont.setAttribute('id', 'sockets_content');
|
||||
|
||||
for (let i = 0; i < data.sockets.length; i++) {
|
||||
let row = document.createElement('tr');
|
||||
row.appendChild(col(data.sockets[i].host));
|
||||
row.appendChild(col(data.sockets[i].port));
|
||||
row.appendChild(col(data.sockets[i].tcp));
|
||||
row.appendChild(col(data.sockets[i].active));
|
||||
row.appendChild(col(data.sockets[i].sent));
|
||||
row.appendChild(col(data.sockets[i].received));
|
||||
new_cont.appendChild(row);
|
||||
}
|
||||
|
||||
parent.replaceChild(new_cont, cont);
|
||||
}
|
||||
|
||||
function displayDns(data) {
|
||||
let cont = document.getElementById('dns_content');
|
||||
let parent = cont.parentNode;
|
||||
let new_cont = document.createElement('tbody');
|
||||
new_cont.setAttribute('id', 'dns_content');
|
||||
|
||||
for (let i = 0; i < data.entries.length; i++) {
|
||||
let row = document.createElement('tr');
|
||||
row.appendChild(col(data.entries[i].hostname));
|
||||
row.appendChild(col(data.entries[i].family));
|
||||
let column = document.createElement('td');
|
||||
|
||||
for (let j = 0; j < data.entries[i].hostaddr.length; j++) {
|
||||
column.appendChild(document.createTextNode(data.entries[i].hostaddr[j]));
|
||||
column.appendChild(document.createElement('br'));
|
||||
}
|
||||
|
||||
row.appendChild(column);
|
||||
row.appendChild(col(data.entries[i].expiration));
|
||||
new_cont.appendChild(row);
|
||||
}
|
||||
|
||||
parent.replaceChild(new_cont, cont);
|
||||
}
|
||||
|
||||
function displayWebsockets(data) {
|
||||
let cont = document.getElementById('websockets_content');
|
||||
let parent = cont.parentNode;
|
||||
let new_cont = document.createElement('tbody');
|
||||
new_cont.setAttribute('id', 'websockets_content');
|
||||
|
||||
for (let i = 0; i < data.websockets.length; i++) {
|
||||
let row = document.createElement('tr');
|
||||
row.appendChild(col(data.websockets[i].hostport));
|
||||
row.appendChild(col(data.websockets[i].encrypted));
|
||||
row.appendChild(col(data.websockets[i].msgsent));
|
||||
row.appendChild(col(data.websockets[i].msgreceived));
|
||||
row.appendChild(col(data.websockets[i].sentsize));
|
||||
row.appendChild(col(data.websockets[i].receivedsize));
|
||||
new_cont.appendChild(row);
|
||||
}
|
||||
|
||||
parent.replaceChild(new_cont, cont);
|
||||
}
|
||||
|
||||
function requestAllNetworkingData() {
|
||||
for (let id in gRequestNetworkingData)
|
||||
requestNetworkingDataForTab(id);
|
||||
}
|
||||
|
||||
function requestNetworkingDataForTab(id) {
|
||||
gRequestNetworkingData[id](gDashboardCallbacks[id]);
|
||||
}
|
||||
|
||||
function init() {
|
||||
gDashboard.enableLogging = true;
|
||||
if (Services.prefs.getBoolPref("network.warnOnAboutNetworking")) {
|
||||
let div = document.getElementById("warning_message");
|
||||
div.classList.add("active");
|
||||
div.hidden = false;
|
||||
document.getElementById("confpref").addEventListener("click", confirm);
|
||||
}
|
||||
|
||||
requestAllNetworkingData();
|
||||
|
||||
let autoRefresh = document.getElementById("autorefcheck");
|
||||
if (autoRefresh.checked)
|
||||
setAutoRefreshInterval(autoRefresh);
|
||||
|
||||
autoRefresh.addEventListener("click", function() {
|
||||
let refrButton = document.getElementById("refreshButton");
|
||||
if (this.checked) {
|
||||
setAutoRefreshInterval(this);
|
||||
refrButton.disabled = "disabled";
|
||||
} else {
|
||||
clearInterval(this.interval);
|
||||
refrButton.disabled = null;
|
||||
}
|
||||
});
|
||||
|
||||
let refr = document.getElementById("refreshButton");
|
||||
refr.addEventListener("click", requestAllNetworkingData);
|
||||
if (document.getElementById("autorefcheck").checked)
|
||||
refr.disabled = "disabled";
|
||||
|
||||
// Event delegation on #categories element
|
||||
let menu = document.getElementById("categories");
|
||||
menu.addEventListener("click", function click(e) {
|
||||
if (e.target && e.target.parentNode == menu)
|
||||
show(e.target);
|
||||
});
|
||||
|
||||
let dnsLookupButton = document.getElementById("dnsLookupButton");
|
||||
dnsLookupButton.addEventListener("click", function() {
|
||||
doLookup();
|
||||
});
|
||||
|
||||
let setLogButton = document.getElementById("set-log-file-button");
|
||||
setLogButton.addEventListener("click", setLogFile);
|
||||
|
||||
let setModulesButton = document.getElementById("set-log-modules-button");
|
||||
setModulesButton.addEventListener("click", setLogModules);
|
||||
|
||||
let startLoggingButton = document.getElementById("start-logging-button");
|
||||
startLoggingButton.addEventListener("click", startLogging);
|
||||
|
||||
let stopLoggingButton = document.getElementById("stop-logging-button");
|
||||
stopLoggingButton.addEventListener("click", stopLogging);
|
||||
|
||||
try {
|
||||
let file = gDirServ.getFile("TmpD", {});
|
||||
file.append("log.txt");
|
||||
document.getElementById("log-file").value = file.path;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
|
||||
// Update the value of the log file.
|
||||
updateLogFile();
|
||||
|
||||
// Update the active log modules
|
||||
updateLogModules();
|
||||
|
||||
// If we can't set the file and the modules at runtime,
|
||||
// the start and stop buttons wouldn't really do anything.
|
||||
if (setLogButton.disabled && setModulesButton.disabled) {
|
||||
startLoggingButton.disabled = true;
|
||||
stopLoggingButton.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
function updateLogFile() {
|
||||
let logPath = "";
|
||||
|
||||
// Try to get the environment variable for the log file
|
||||
logPath = gEnv.get("MOZ_LOG_FILE") || gEnv.get("NSPR_LOG_FILE");
|
||||
let currentLogFile = document.getElementById("current-log-file");
|
||||
let setLogFileButton = document.getElementById("set-log-file-button");
|
||||
|
||||
// If the log file was set from an env var, we disable the ability to set it
|
||||
// at runtime.
|
||||
if (logPath.length > 0) {
|
||||
currentLogFile.innerText = logPath;
|
||||
setLogFileButton.disabled = true;
|
||||
} else {
|
||||
// There may be a value set by a pref.
|
||||
currentLogFile.innerText = gDashboard.getLogPath();
|
||||
}
|
||||
}
|
||||
|
||||
function updateLogModules() {
|
||||
// Try to get the environment variable for the log file
|
||||
let logModules = gEnv.get("MOZ_LOG") ||
|
||||
gEnv.get("MOZ_LOG_MODULES") ||
|
||||
gEnv.get("NSPR_LOG_MODULES");
|
||||
let currentLogModules = document.getElementById("current-log-modules");
|
||||
let setLogModulesButton = document.getElementById("set-log-modules-button");
|
||||
if (logModules.length > 0) {
|
||||
currentLogModules.innerText = logModules;
|
||||
// If the log modules are set by an environment variable at startup, do not
|
||||
// allow changing them throught a pref. It would be difficult to figure out
|
||||
// which ones are enabled and which ones are not. The user probably knows
|
||||
// what he they are doing.
|
||||
setLogModulesButton.disabled = true;
|
||||
} else {
|
||||
let activeLogModules = [];
|
||||
try {
|
||||
if (Services.prefs.getBoolPref("logging.config.add_timestamp")) {
|
||||
activeLogModules.push("timestamp");
|
||||
}
|
||||
} catch (e) {}
|
||||
try {
|
||||
if (Services.prefs.getBoolPref("logging.config.sync")) {
|
||||
activeLogModules.push("sync");
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
let children = Services.prefs.getBranch("logging.").getChildList("", {});
|
||||
|
||||
for (let pref of children) {
|
||||
if (pref.startsWith("config.")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
let value = Services.prefs.getIntPref(`logging.${pref}`);
|
||||
activeLogModules.push(`${pref}:${value}`);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
currentLogModules.innerText = activeLogModules.join(",");
|
||||
}
|
||||
}
|
||||
|
||||
function setLogFile() {
|
||||
let setLogButton = document.getElementById("set-log-file-button");
|
||||
if (setLogButton.disabled) {
|
||||
// There's no point trying since it wouldn't work anyway.
|
||||
return;
|
||||
}
|
||||
let logFile = document.getElementById("log-file").value.trim();
|
||||
Services.prefs.setCharPref("logging.config.LOG_FILE", logFile);
|
||||
updateLogFile();
|
||||
}
|
||||
|
||||
function clearLogModules() {
|
||||
// Turn off all the modules.
|
||||
let children = Services.prefs.getBranch("logging.").getChildList("", {});
|
||||
for (let pref of children) {
|
||||
if (!pref.startsWith("config.")) {
|
||||
Services.prefs.clearUserPref(`logging.${pref}`);
|
||||
}
|
||||
}
|
||||
Services.prefs.clearUserPref("logging.config.add_timestamp");
|
||||
Services.prefs.clearUserPref("logging.config.sync");
|
||||
updateLogModules();
|
||||
}
|
||||
|
||||
function setLogModules() {
|
||||
let setLogModulesButton = document.getElementById("set-log-modules-button");
|
||||
if (setLogModulesButton.disabled) {
|
||||
// The modules were set via env var, so we shouldn't try to change them.
|
||||
return;
|
||||
}
|
||||
|
||||
let modules = document.getElementById("log-modules").value.trim();
|
||||
|
||||
// Clear previously set log modules.
|
||||
clearLogModules();
|
||||
|
||||
let logModules = modules.split(",");
|
||||
for (let module of logModules) {
|
||||
if (module == "timestamp") {
|
||||
Services.prefs.setBoolPref("logging.config.add_timestamp", true);
|
||||
} else if (module == "rotate") {
|
||||
// XXX: rotate is not yet supported.
|
||||
} else if (module == "append") {
|
||||
// XXX: append is not yet supported.
|
||||
} else if (module == "sync") {
|
||||
Services.prefs.setBoolPref("logging.config.sync", true);
|
||||
} else {
|
||||
let [key, value] = module.split(":");
|
||||
Services.prefs.setIntPref(`logging.${key}`, parseInt(value, 10));
|
||||
}
|
||||
}
|
||||
|
||||
updateLogModules();
|
||||
}
|
||||
|
||||
function startLogging() {
|
||||
setLogFile();
|
||||
setLogModules();
|
||||
}
|
||||
|
||||
function stopLogging() {
|
||||
clearLogModules();
|
||||
// clear the log file as well
|
||||
Services.prefs.clearUserPref("logging.config.LOG_FILE");
|
||||
updateLogFile();
|
||||
}
|
||||
|
||||
function confirm () {
|
||||
let div = document.getElementById("warning_message");
|
||||
div.classList.remove("active");
|
||||
div.hidden = true;
|
||||
let warnBox = document.getElementById("warncheck");
|
||||
Services.prefs.setBoolPref("network.warnOnAboutNetworking", warnBox.checked);
|
||||
}
|
||||
|
||||
function show(button) {
|
||||
let current_tab = document.querySelector(".active");
|
||||
let content = document.getElementById(button.getAttribute("value"));
|
||||
if (current_tab == content)
|
||||
return;
|
||||
current_tab.classList.remove("active");
|
||||
current_tab.hidden = true;
|
||||
content.classList.add("active");
|
||||
content.hidden = false;
|
||||
|
||||
let current_button = document.querySelector("[selected=true]");
|
||||
current_button.removeAttribute("selected");
|
||||
button.setAttribute("selected", "true");
|
||||
|
||||
let autoRefresh = document.getElementById("autorefcheck");
|
||||
if (autoRefresh.checked) {
|
||||
clearInterval(autoRefresh.interval);
|
||||
setAutoRefreshInterval(autoRefresh);
|
||||
}
|
||||
|
||||
let title = document.getElementById("sectionTitle");
|
||||
title.textContent = button.children[0].textContent;
|
||||
}
|
||||
|
||||
function setAutoRefreshInterval(checkBox) {
|
||||
let active_tab = document.querySelector(".active");
|
||||
checkBox.interval = setInterval(function() {
|
||||
requestNetworkingDataForTab(active_tab.id);
|
||||
}, REFRESH_INTERVAL_MS);
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function load() {
|
||||
window.removeEventListener("DOMContentLoaded", load);
|
||||
init();
|
||||
});
|
||||
|
||||
function doLookup() {
|
||||
let host = document.getElementById("host").value;
|
||||
if (host) {
|
||||
gDashboard.requestDNSLookup(host, displayDNSLookup);
|
||||
}
|
||||
}
|
||||
|
||||
function displayDNSLookup(data) {
|
||||
let cont = document.getElementById("dnslookuptool_content");
|
||||
let parent = cont.parentNode;
|
||||
let new_cont = document.createElement("tbody");
|
||||
new_cont.setAttribute("id", "dnslookuptool_content");
|
||||
|
||||
if (data.answer) {
|
||||
for (let address of data.address) {
|
||||
let row = document.createElement("tr");
|
||||
row.appendChild(col(address));
|
||||
new_cont.appendChild(row);
|
||||
}
|
||||
}
|
||||
else {
|
||||
new_cont.appendChild(col(data.error));
|
||||
}
|
||||
|
||||
parent.replaceChild(new_cont, cont);
|
||||
}
|
||||
168
toolkit/content/aboutNetworking.xhtml
Normal file
168
toolkit/content/aboutNetworking.xhtml
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<!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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
|
||||
<!ENTITY % networkingDTD SYSTEM "chrome://global/locale/aboutNetworking.dtd"> %networkingDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&aboutNetworking.title;</title>
|
||||
<link rel="stylesheet" href="chrome://mozapps/skin/aboutNetworking.css" type="text/css" />
|
||||
<script type="application/javascript;version=1.7" src="chrome://global/content/aboutNetworking.js" />
|
||||
</head>
|
||||
<body id="body">
|
||||
<div id="warning_message" class="warningBackground" hidden="true">
|
||||
<div class="container">
|
||||
<h1 class="title">&aboutNetworking.warning;</h1>
|
||||
<div class="toggle-container-with-text">
|
||||
<input id="warncheck" type="checkbox" checked="yes" />
|
||||
<label for="warncheck">&aboutNetworking.showNextTime;</label>
|
||||
</div>
|
||||
<div>
|
||||
<button id="confpref" class="primary">&aboutNetworking.ok;</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="categories">
|
||||
<div class="category" selected="true" value="http">
|
||||
<span class="category-name">&aboutNetworking.HTTP;</span>
|
||||
</div>
|
||||
<div class="category" value="sockets">
|
||||
<span class="category-name">&aboutNetworking.sockets;</span>
|
||||
</div>
|
||||
<div class="category" value="dns">
|
||||
<span class="category-name">&aboutNetworking.dns;</span>
|
||||
</div>
|
||||
<div class="category" value="websockets">
|
||||
<span class="category-name">&aboutNetworking.websockets;</span>
|
||||
</div>
|
||||
<hr></hr>
|
||||
<div class="category" value="dnslookuptool">
|
||||
<span class="category-name">&aboutNetworking.dnsLookup;</span>
|
||||
</div>
|
||||
<div class="category" value="logging">
|
||||
<span class="category-name">&aboutNetworking.logging;</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<div class="header">
|
||||
<div id="sectionTitle" class="header-name">
|
||||
&aboutNetworking.HTTP;
|
||||
</div>
|
||||
<div id="refreshDiv" class="toggle-container-with-text">
|
||||
<button id="refreshButton">&aboutNetworking.refresh;</button>
|
||||
<input id="autorefcheck" type="checkbox" name="Autorefresh" />
|
||||
<label for="autorefcheck">&aboutNetworking.autoRefresh;</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="http" class="tab active">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>&aboutNetworking.hostname;</th>
|
||||
<th>&aboutNetworking.port;</th>
|
||||
<th>&aboutNetworking.spdy;</th>
|
||||
<th>&aboutNetworking.ssl;</th>
|
||||
<th>&aboutNetworking.active;</th>
|
||||
<th>&aboutNetworking.idle;</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="http_content" />
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="sockets" class="tab" hidden="true">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>&aboutNetworking.host;</th>
|
||||
<th>&aboutNetworking.port;</th>
|
||||
<th>&aboutNetworking.tcp;</th>
|
||||
<th>&aboutNetworking.active;</th>
|
||||
<th>&aboutNetworking.sent;</th>
|
||||
<th>&aboutNetworking.received;</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="sockets_content" />
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="dns" class="tab" hidden="true">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>&aboutNetworking.hostname;</th>
|
||||
<th>&aboutNetworking.family;</th>
|
||||
<th>&aboutNetworking.addresses;</th>
|
||||
<th>&aboutNetworking.expires;</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dns_content" />
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="websockets" class="tab" hidden="true">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>&aboutNetworking.hostname;</th>
|
||||
<th>&aboutNetworking.ssl;</th>
|
||||
<th>&aboutNetworking.messagesSent;</th>
|
||||
<th>&aboutNetworking.messagesReceived;</th>
|
||||
<th>&aboutNetworking.bytesSent;</th>
|
||||
<th>&aboutNetworking.bytesReceived;</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="websockets_content" />
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="dnslookuptool" class="tab" hidden="true">
|
||||
&aboutNetworking.dnsDomain;: <input type="text" name="host" id="host"></input>
|
||||
<button id="dnsLookupButton">&aboutNetworking.dnsLookupButton;</button>
|
||||
<hr/>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>&aboutNetworking.dnsLookupTableColumn;</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dnslookuptool_content" />
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="logging" class="tab" hidden="true">
|
||||
<div>
|
||||
&aboutNetworking.logTutorial;
|
||||
</div>
|
||||
<br/>
|
||||
<div>
|
||||
<button id="start-logging-button"> &aboutNetworking.startLogging; </button>
|
||||
<button id="stop-logging-button"> &aboutNetworking.stopLogging; </button>
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<div>
|
||||
&aboutNetworking.currentLogFile; <div id="current-log-file"></div><br/>
|
||||
<input type="text" name="log-file" id="log-file"></input>
|
||||
<button id="set-log-file-button"> &aboutNetworking.setLogFile; </button>
|
||||
</div>
|
||||
<div>
|
||||
&aboutNetworking.currentLogModules; <div id="current-log-modules"></div><br/>
|
||||
<input type="text" name="log-modules" id="log-modules" value="timestamp,sync,nsHttp:5,nsSocketTransport:5,nsStreamPump:5,nsHostResolver:5"></input>
|
||||
<button id="set-log-modules-button"> &aboutNetworking.setLogModules; </button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
339
toolkit/content/aboutProfiles.js
Normal file
339
toolkit/content/aboutProfiles.js
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/* 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 {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import('resource://gre/modules/Services.jsm');
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import('resource://gre/modules/AppConstants.jsm');
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(
|
||||
this,
|
||||
'ProfileService',
|
||||
'@mozilla.org/toolkit/profile-service;1',
|
||||
'nsIToolkitProfileService'
|
||||
);
|
||||
|
||||
const bundle = Services.strings.createBundle(
|
||||
'chrome://global/locale/aboutProfiles.properties');
|
||||
|
||||
// nsIToolkitProfileService.selectProfile can be used only during the selection
|
||||
// of the profile in the ProfileManager. If we are showing about:profiles in a
|
||||
// tab, the selectedProfile returns the default profile.
|
||||
// In this function we use the ProfD to find the current profile.
|
||||
function findCurrentProfile() {
|
||||
let cpd;
|
||||
try {
|
||||
cpd = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("ProfD", Ci.nsIFile);
|
||||
} catch (e) {}
|
||||
|
||||
if (cpd) {
|
||||
let itr = ProfileService.profiles;
|
||||
while (itr.hasMoreElements()) {
|
||||
let profile = itr.getNext().QueryInterface(Ci.nsIToolkitProfile);
|
||||
if (profile.rootDir.path == cpd.path) {
|
||||
return profile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// selectedProfile can trow if nothing is selected or if the selected profile
|
||||
// has been deleted.
|
||||
try {
|
||||
return ProfileService.selectedProfile;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function refreshUI() {
|
||||
let parent = document.getElementById('profiles');
|
||||
while (parent.firstChild) {
|
||||
parent.removeChild(parent.firstChild);
|
||||
}
|
||||
|
||||
let defaultProfile;
|
||||
try {
|
||||
defaultProfile = ProfileService.defaultProfile;
|
||||
} catch (e) {}
|
||||
|
||||
let currentProfile = findCurrentProfile() || defaultProfile;
|
||||
|
||||
let iter = ProfileService.profiles;
|
||||
while (iter.hasMoreElements()) {
|
||||
let profile = iter.getNext().QueryInterface(Ci.nsIToolkitProfile);
|
||||
display({ profile: profile,
|
||||
isDefault: profile == defaultProfile,
|
||||
isCurrentProfile: profile == currentProfile });
|
||||
}
|
||||
|
||||
let createButton = document.getElementById('create-button');
|
||||
createButton.onclick = createProfileWizard;
|
||||
|
||||
let restartSafeModeButton = document.getElementById('restart-in-safe-mode-button');
|
||||
restartSafeModeButton.onclick = function() { restart(true); }
|
||||
|
||||
let restartNormalModeButton = document.getElementById('restart-button');
|
||||
restartNormalModeButton.onclick = function() { restart(false); }
|
||||
}
|
||||
|
||||
function openDirectory(dir) {
|
||||
let nsLocalFile = Components.Constructor("@mozilla.org/file/local;1",
|
||||
"nsILocalFile", "initWithPath");
|
||||
new nsLocalFile(dir).reveal();
|
||||
}
|
||||
|
||||
function display(profileData) {
|
||||
let parent = document.getElementById('profiles');
|
||||
|
||||
let div = document.createElement('div');
|
||||
parent.appendChild(div);
|
||||
|
||||
let nameStr = bundle.formatStringFromName('name', [profileData.profile.name], 1);
|
||||
|
||||
let name = document.createElement('h2');
|
||||
name.appendChild(document.createTextNode(nameStr));
|
||||
|
||||
div.appendChild(name);
|
||||
|
||||
if (profileData.isCurrentProfile) {
|
||||
let currentProfile = document.createElement('h3');
|
||||
let currentProfileStr = bundle.GetStringFromName('currentProfile');
|
||||
currentProfile.appendChild(document.createTextNode(currentProfileStr));
|
||||
div.appendChild(currentProfile);
|
||||
}
|
||||
|
||||
let table = document.createElement('table');
|
||||
div.appendChild(table);
|
||||
|
||||
let tbody = document.createElement('tbody');
|
||||
table.appendChild(tbody);
|
||||
|
||||
function createItem(title, value, dir = false) {
|
||||
let tr = document.createElement('tr');
|
||||
tbody.appendChild(tr);
|
||||
|
||||
let th = document.createElement('th');
|
||||
th.setAttribute('class', 'column');
|
||||
th.appendChild(document.createTextNode(title));
|
||||
tr.appendChild(th);
|
||||
|
||||
let td = document.createElement('td');
|
||||
td.appendChild(document.createTextNode(value));
|
||||
tr.appendChild(td);
|
||||
|
||||
if (dir) {
|
||||
td.appendChild(document.createTextNode(' '));
|
||||
let button = document.createElement('button');
|
||||
let string = 'openDir';
|
||||
if (AppConstants.platform == "win") {
|
||||
string = 'winOpenDir2';
|
||||
} else if (AppConstants.platform == "macosx") {
|
||||
string = 'macOpenDir';
|
||||
}
|
||||
let buttonText = document.createTextNode(bundle.GetStringFromName(string));
|
||||
button.appendChild(buttonText);
|
||||
td.appendChild(button);
|
||||
|
||||
button.addEventListener('click', function(e) {
|
||||
openDirectory(value);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
createItem(bundle.GetStringFromName('isDefault'),
|
||||
profileData.isDefault ? bundle.GetStringFromName('yes') : bundle.GetStringFromName('no'));
|
||||
|
||||
createItem(bundle.GetStringFromName('rootDir'), profileData.profile.rootDir.path, true);
|
||||
|
||||
if (profileData.profile.localDir.path != profileData.profile.rootDir.path) {
|
||||
createItem(bundle.GetStringFromName('localDir'), profileData.profile.localDir.path, true);
|
||||
}
|
||||
|
||||
let renameButton = document.createElement('button');
|
||||
renameButton.appendChild(document.createTextNode(bundle.GetStringFromName('rename')));
|
||||
renameButton.onclick = function() {
|
||||
renameProfile(profileData.profile);
|
||||
};
|
||||
div.appendChild(renameButton);
|
||||
|
||||
if (!profileData.isCurrentProfile) {
|
||||
let removeButton = document.createElement('button');
|
||||
removeButton.appendChild(document.createTextNode(bundle.GetStringFromName('remove')));
|
||||
removeButton.onclick = function() {
|
||||
removeProfile(profileData.profile);
|
||||
};
|
||||
|
||||
div.appendChild(removeButton);
|
||||
}
|
||||
|
||||
if (!profileData.isDefault) {
|
||||
let defaultButton = document.createElement('button');
|
||||
defaultButton.appendChild(document.createTextNode(bundle.GetStringFromName('setAsDefault')));
|
||||
defaultButton.onclick = function() {
|
||||
defaultProfile(profileData.profile);
|
||||
};
|
||||
div.appendChild(defaultButton);
|
||||
}
|
||||
|
||||
if (!profileData.isCurrentProfile) {
|
||||
let runButton = document.createElement('button');
|
||||
runButton.appendChild(document.createTextNode(bundle.GetStringFromName('launchProfile')));
|
||||
runButton.onclick = function() {
|
||||
openProfile(profileData.profile);
|
||||
};
|
||||
div.appendChild(runButton);
|
||||
}
|
||||
|
||||
let sep = document.createElement('hr');
|
||||
div.appendChild(sep);
|
||||
}
|
||||
|
||||
function CreateProfile(profile) {
|
||||
ProfileService.selectedProfile = profile;
|
||||
ProfileService.flush();
|
||||
refreshUI();
|
||||
}
|
||||
|
||||
function createProfileWizard() {
|
||||
// This should be rewritten in HTML eventually.
|
||||
window.openDialog('chrome://mozapps/content/profile/createProfileWizard.xul',
|
||||
'', 'centerscreen,chrome,modal,titlebar',
|
||||
ProfileService);
|
||||
}
|
||||
|
||||
function renameProfile(profile) {
|
||||
let title = bundle.GetStringFromName('renameProfileTitle');
|
||||
let msg = bundle.formatStringFromName('renameProfile', [profile.name], 1);
|
||||
let newName = { value: profile.name };
|
||||
|
||||
if (Services.prompt.prompt(window, title, msg, newName, null,
|
||||
{ value: 0 })) {
|
||||
newName = newName.value;
|
||||
|
||||
if (newName == profile.name) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
profile.name = newName;
|
||||
} catch (e) {
|
||||
let title = bundle.GetStringFromName('invalidProfileNameTitle');
|
||||
let msg = bundle.formatStringFromName('invalidProfileName', [newName], 1);
|
||||
Services.prompt.alert(window, title, msg);
|
||||
return;
|
||||
}
|
||||
|
||||
ProfileService.flush();
|
||||
refreshUI();
|
||||
}
|
||||
}
|
||||
|
||||
function removeProfile(profile) {
|
||||
let deleteFiles = false;
|
||||
|
||||
if (profile.rootDir.exists()) {
|
||||
let title = bundle.GetStringFromName('deleteProfileTitle');
|
||||
let msg = bundle.formatStringFromName('deleteProfileConfirm',
|
||||
[profile.rootDir.path], 1);
|
||||
|
||||
let buttonPressed = Services.prompt.confirmEx(window, title, msg,
|
||||
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_0) +
|
||||
(Services.prompt.BUTTON_TITLE_CANCEL * Services.prompt.BUTTON_POS_1) +
|
||||
(Services.prompt.BUTTON_TITLE_IS_STRING * Services.prompt.BUTTON_POS_2),
|
||||
bundle.GetStringFromName('dontDeleteFiles'),
|
||||
null,
|
||||
bundle.GetStringFromName('deleteFiles'),
|
||||
null, {value:0});
|
||||
if (buttonPressed == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (buttonPressed == 2) {
|
||||
deleteFiles = true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we are deleting the selected or the default profile we must choose a
|
||||
// different one.
|
||||
let isSelected = false;
|
||||
try {
|
||||
isSelected = ProfileService.selectedProfile == profile;
|
||||
} catch (e) {}
|
||||
|
||||
let isDefault = false;
|
||||
try {
|
||||
isDefault = ProfileService.defaultProfile == profile;
|
||||
} catch (e) {}
|
||||
|
||||
if (isSelected || isDefault) {
|
||||
let itr = ProfileService.profiles;
|
||||
while (itr.hasMoreElements()) {
|
||||
let p = itr.getNext().QueryInterface(Ci.nsIToolkitProfile);
|
||||
if (profile == p) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isSelected) {
|
||||
ProfileService.selectedProfile = p;
|
||||
}
|
||||
|
||||
if (isDefault) {
|
||||
ProfileService.defaultProfile = p;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
profile.remove(deleteFiles);
|
||||
ProfileService.flush();
|
||||
refreshUI();
|
||||
}
|
||||
|
||||
function defaultProfile(profile) {
|
||||
ProfileService.defaultProfile = profile;
|
||||
ProfileService.selectedProfile = profile;
|
||||
ProfileService.flush();
|
||||
refreshUI();
|
||||
}
|
||||
|
||||
function openProfile(profile) {
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
|
||||
|
||||
if (cancelQuit.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
Services.startup.createInstanceWithProfile(profile);
|
||||
}
|
||||
|
||||
function restart(safeMode) {
|
||||
let cancelQuit = Cc["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Ci.nsISupportsPRBool);
|
||||
Services.obs.notifyObservers(cancelQuit, "quit-application-requested", "restart");
|
||||
|
||||
if (cancelQuit.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
let flags = Ci.nsIAppStartup.eAttemptQuit | Ci.nsIAppStartup.eRestartNotSameProfile;
|
||||
|
||||
if (safeMode) {
|
||||
Services.startup.restartInSafeMode(flags);
|
||||
} else {
|
||||
Services.startup.quit(flags);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('DOMContentLoaded', function load() {
|
||||
window.removeEventListener('DOMContentLoaded', load);
|
||||
refreshUI();
|
||||
});
|
||||
38
toolkit/content/aboutProfiles.xhtml
Normal file
38
toolkit/content/aboutProfiles.xhtml
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<!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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
|
||||
<!ENTITY % profilesDTD SYSTEM "chrome://global/locale/aboutProfiles.dtd"> %profilesDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&aboutProfiles.title;</title>
|
||||
<link rel="icon" type="image/png" id="favicon" href="chrome://branding/content/icon32.png"/>
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css" type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://mozapps/skin/aboutProfiles.css" type="text/css" />
|
||||
<script type="application/javascript;version=1.7" src="chrome://global/content/aboutProfiles.js" />
|
||||
</head>
|
||||
<body id="body">
|
||||
<div id="action-box">
|
||||
<h3>&aboutProfiles.restart.title;</h3>
|
||||
<button id="restart-in-safe-mode-button">&aboutProfiles.restart.inSafeMode;</button>
|
||||
<button id="restart-button">&aboutProfiles.restart.normal;</button>
|
||||
</div>
|
||||
|
||||
<h1>&aboutProfiles.title;</h1>
|
||||
<div class="page-subtitle">&aboutProfiles.subtitle;</div>
|
||||
|
||||
<div>
|
||||
<button id="create-button">&aboutProfiles.create;</button>
|
||||
</div>
|
||||
|
||||
<div id="profiles" class="tab"></div>
|
||||
</body>
|
||||
</html>
|
||||
59
toolkit/content/aboutRights-unbranded.xhtml
Normal file
59
toolkit/content/aboutRights-unbranded.xhtml
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % aboutRightsDTD SYSTEM "chrome://global/locale/aboutRights.dtd">
|
||||
%aboutRightsDTD;
|
||||
]>
|
||||
|
||||
<!-- 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>&rights.pagetitle;</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body id="your-rights" dir="&rights.locale-direction;" class="aboutPageWideContainer">
|
||||
|
||||
<h1>&rights.intro-header;</h1>
|
||||
|
||||
<p>&rights.intro;</p>
|
||||
|
||||
<ul>
|
||||
<li>&rights.intro-point1a;<a href="http://www.mozilla.org/MPL/">&rights.intro-point1b;</a>&rights.intro-point1c;</li>
|
||||
<!-- Point 2 discusses Mozilla trademarks, and isn't needed when the build is unbranded.
|
||||
- Point 3 discusses privacy policy, unbranded builds get a placeholder (for the vendor to replace)
|
||||
- Point 4 discusses web service terms, unbranded builds gets a placeholder (for the vendor to replace) -->
|
||||
<li>&rights.intro-point3-unbranded;</li>
|
||||
<li>&rights.intro-point4a-unbranded;<a href="about:rights#webservices" onclick="showServices();">&rights.intro-point4b-unbranded;</a>&rights.intro-point4c-unbranded;</li>
|
||||
</ul>
|
||||
|
||||
<div id="webservices-container">
|
||||
<a name="webservices"/>
|
||||
<h3>&rights2.webservices-header;</h3>
|
||||
|
||||
<p>&rights.webservices-unbranded;</p>
|
||||
|
||||
<ol>
|
||||
<!-- Terms only apply to official builds, unbranded builds get a placeholder. -->
|
||||
<li>&rights.webservices-term1-unbranded;</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<script type="application/javascript"><![CDATA[
|
||||
var servicesDiv = document.getElementById("webservices-container");
|
||||
servicesDiv.style.display = "none";
|
||||
|
||||
function showServices() {
|
||||
servicesDiv.style.display = "";
|
||||
}
|
||||
]]></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
96
toolkit/content/aboutRights.xhtml
Normal file
96
toolkit/content/aboutRights.xhtml
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
<?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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%brandDTD;
|
||||
<!ENTITY % securityPrefsDTD SYSTEM "chrome://browser/locale/preferences/security.dtd">
|
||||
%securityPrefsDTD;
|
||||
<!ENTITY % aboutRightsDTD SYSTEM "chrome://global/locale/aboutRights.dtd">
|
||||
%aboutRightsDTD;
|
||||
]>
|
||||
|
||||
<!-- 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>&rights.pagetitle;</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css"/>
|
||||
</head>
|
||||
|
||||
<body id="your-rights" dir="&rights.locale-direction;" class="aboutPageWideContainer">
|
||||
|
||||
<h1>&rights.intro-header;</h1>
|
||||
|
||||
<p>&rights.intro;</p>
|
||||
|
||||
<ul>
|
||||
<li>&rights.intro-point1a;<a href="http://www.mozilla.org/MPL/">&rights.intro-point1b;</a>&rights.intro-point1c;</li>
|
||||
<!-- Point 2 discusses Mozilla trademarks, and isn't needed when the build is unbranded.
|
||||
- Point 3 discusses privacy policy, unbranded builds get a placeholder (for the vendor to replace)
|
||||
- Point 4 discusses web service terms, unbranded builds gets a placeholder (for the vendor to replace) -->
|
||||
<li>&rights.intro-point2-a;<a href="http://www.mozilla.org/foundation/trademarks/policy.html">&rights.intro-point2-b;</a>&rights.intro-point2-c;</li>
|
||||
<li>&rights.intro-point2.5;</li>
|
||||
<li>&rights2.intro-point3a;<a href="https://www.mozilla.org/legal/privacy/firefox.html">&rights2.intro-point3b;</a>&rights.intro-point3c;</li>
|
||||
<li>&rights2.intro-point4a;<a href="about:rights#webservices" onclick="showServices();">&rights.intro-point4b;</a>&rights.intro-point4c;</li>
|
||||
<li>&rights.intro-point5;</li>
|
||||
</ul>
|
||||
|
||||
<div id="webservices-container">
|
||||
<a name="webservices"/>
|
||||
<h3>&rights2.webservices-header;</h3>
|
||||
|
||||
<p>&rights2.webservices-a;<a href="about:rights#disabling-webservices" onclick="showDisablingServices();">&rights2.webservices-b;</a>&rights3.webservices-c;</p>
|
||||
|
||||
<div id="disabling-webservices-container" style="margin-left:40px;">
|
||||
<a name="disabling-webservices"/>
|
||||
<p><strong>&rights.safebrowsing-a;</strong>&rights.safebrowsing-b;</p>
|
||||
<ul>
|
||||
<li>&rights.safebrowsing-term1;</li>
|
||||
<li>&rights.safebrowsing-term2;</li>
|
||||
<li>&rights2.safebrowsing-term3;</li>
|
||||
<li>&rights.safebrowsing-term4;</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>&rights.locationawarebrowsing-a;</strong>&rights.locationawarebrowsing-b;</p>
|
||||
<ul>
|
||||
<li>&rights.locationawarebrowsing-term1a;<code>&rights.locationawarebrowsing-term1b;</code></li>
|
||||
<li>&rights.locationawarebrowsing-term2;</li>
|
||||
<li>&rights.locationawarebrowsing-term3;</li>
|
||||
<li>&rights.locationawarebrowsing-term4;</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ol>
|
||||
<!-- Terms only apply to official builds, unbranded builds get a placeholder. -->
|
||||
<li>&rights2.webservices-term1;</li>
|
||||
<li>&rights.webservices-term2;</li>
|
||||
<li>&rights2.webservices-term3;</li>
|
||||
<li><strong>&rights.webservices-term4;</strong></li>
|
||||
<li><strong>&rights.webservices-term5;</strong></li>
|
||||
<li>&rights.webservices-term6;</li>
|
||||
<li>&rights.webservices-term7;</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<script type="application/javascript"><![CDATA[
|
||||
var servicesDiv = document.getElementById("webservices-container");
|
||||
servicesDiv.style.display = "none";
|
||||
|
||||
function showServices() {
|
||||
servicesDiv.style.display = "";
|
||||
}
|
||||
|
||||
var disablingServicesDiv = document.getElementById("disabling-webservices-container");
|
||||
disablingServicesDiv.style.display = "none";
|
||||
|
||||
function showDisablingServices() {
|
||||
disablingServicesDiv.style.display = "";
|
||||
}
|
||||
]]></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
184
toolkit/content/aboutServiceWorkers.js
Normal file
184
toolkit/content/aboutServiceWorkers.js
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* 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';
|
||||
|
||||
var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
|
||||
const bundle = Services.strings.createBundle(
|
||||
"chrome://global/locale/aboutServiceWorkers.properties");
|
||||
|
||||
const brandBundle = Services.strings.createBundle(
|
||||
"chrome://branding/locale/brand.properties");
|
||||
|
||||
var gSWM;
|
||||
var gSWCount = 0;
|
||||
|
||||
function init() {
|
||||
let enabled = Services.prefs.getBoolPref("dom.serviceWorkers.enabled");
|
||||
if (!enabled) {
|
||||
let div = document.getElementById("warning_not_enabled");
|
||||
div.classList.add("active");
|
||||
return;
|
||||
}
|
||||
|
||||
gSWM = Cc["@mozilla.org/serviceworkers/manager;1"]
|
||||
.getService(Ci.nsIServiceWorkerManager);
|
||||
if (!gSWM) {
|
||||
dump("AboutServiceWorkers: Failed to get the ServiceWorkerManager service!\n");
|
||||
return;
|
||||
}
|
||||
|
||||
let data = gSWM.getAllRegistrations();
|
||||
if (!data) {
|
||||
dump("AboutServiceWorkers: Failed to retrieve the registrations.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
let length = data.length;
|
||||
if (!length) {
|
||||
let div = document.getElementById("warning_no_serviceworkers");
|
||||
div.classList.add("active");
|
||||
return;
|
||||
}
|
||||
|
||||
let ps = undefined;
|
||||
try {
|
||||
ps = Cc["@mozilla.org/push/Service;1"]
|
||||
.getService(Ci.nsIPushService);
|
||||
} catch (e) {
|
||||
dump("Could not acquire PushService\n");
|
||||
}
|
||||
|
||||
for (let i = 0; i < length; ++i) {
|
||||
let info = data.queryElementAt(i, Ci.nsIServiceWorkerRegistrationInfo);
|
||||
if (!info) {
|
||||
dump("AboutServiceWorkers: Invalid nsIServiceWorkerRegistrationInfo interface.\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
display(info, ps);
|
||||
}
|
||||
}
|
||||
|
||||
function display(info, pushService) {
|
||||
let parent = document.getElementById("serviceworkers");
|
||||
|
||||
let div = document.createElement('div');
|
||||
parent.appendChild(div);
|
||||
|
||||
let title = document.createElement('h2');
|
||||
let titleStr = bundle.formatStringFromName('title', [info.principal.origin], 1);
|
||||
title.appendChild(document.createTextNode(titleStr));
|
||||
div.appendChild(title);
|
||||
|
||||
if (info.principal.appId) {
|
||||
let b2gtitle = document.createElement('h3');
|
||||
let trueFalse = bundle.GetStringFromName(info.principal.isInIsolatedMozBrowserElement ? 'true' : 'false');
|
||||
|
||||
let b2gtitleStr =
|
||||
bundle.formatStringFromName('b2gtitle', [ brandBundle.getString("brandShortName"),
|
||||
info.principal.appId,
|
||||
trueFalse], 2);
|
||||
b2gtitle.appendChild(document.createTextNode(b2gtitleStr));
|
||||
div.appendChild(b2gtitle);
|
||||
}
|
||||
|
||||
let list = document.createElement('ul');
|
||||
div.appendChild(list);
|
||||
|
||||
function createItem(title, value, makeLink) {
|
||||
let item = document.createElement('li');
|
||||
list.appendChild(item);
|
||||
|
||||
let bold = document.createElement('strong');
|
||||
bold.appendChild(document.createTextNode(title + " "));
|
||||
item.appendChild(bold);
|
||||
|
||||
let textNode = document.createTextNode(value);
|
||||
|
||||
if (makeLink) {
|
||||
let link = document.createElement("a");
|
||||
link.href = value;
|
||||
link.target = "_blank";
|
||||
link.appendChild(textNode);
|
||||
item.appendChild(link);
|
||||
} else {
|
||||
item.appendChild(textNode);
|
||||
}
|
||||
|
||||
return textNode;
|
||||
}
|
||||
|
||||
createItem(bundle.GetStringFromName('scope'), info.scope);
|
||||
createItem(bundle.GetStringFromName('scriptSpec'), info.scriptSpec, true);
|
||||
let currentWorkerURL = info.activeWorker ? info.activeWorker.scriptSpec : "";
|
||||
createItem(bundle.GetStringFromName('currentWorkerURL'), currentWorkerURL, true);
|
||||
let activeCacheName = info.activeWorker ? info.activeWorker.cacheName : "";
|
||||
createItem(bundle.GetStringFromName('activeCacheName'), activeCacheName);
|
||||
let waitingCacheName = info.waitingWorker ? info.waitingWorker.cacheName : "";
|
||||
createItem(bundle.GetStringFromName('waitingCacheName'), waitingCacheName);
|
||||
|
||||
let pushItem = createItem(bundle.GetStringFromName('pushEndpoint'), bundle.GetStringFromName('waiting'));
|
||||
if (pushService) {
|
||||
pushService.getSubscription(info.scope, info.principal, (status, pushRecord) => {
|
||||
if (Components.isSuccessCode(status)) {
|
||||
pushItem.data = JSON.stringify(pushRecord);
|
||||
} else {
|
||||
dump("about:serviceworkers - retrieving push registration failed\n");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let updateButton = document.createElement("button");
|
||||
updateButton.appendChild(document.createTextNode(bundle.GetStringFromName('update')));
|
||||
updateButton.onclick = function() {
|
||||
gSWM.propagateSoftUpdate(info.principal.originAttributes, info.scope);
|
||||
};
|
||||
div.appendChild(updateButton);
|
||||
|
||||
let unregisterButton = document.createElement("button");
|
||||
unregisterButton.appendChild(document.createTextNode(bundle.GetStringFromName('unregister')));
|
||||
div.appendChild(unregisterButton);
|
||||
|
||||
let loadingMessage = document.createElement('span');
|
||||
loadingMessage.appendChild(document.createTextNode(bundle.GetStringFromName('waiting')));
|
||||
loadingMessage.classList.add('inactive');
|
||||
div.appendChild(loadingMessage);
|
||||
|
||||
unregisterButton.onclick = function() {
|
||||
let cb = {
|
||||
unregisterSucceeded: function() {
|
||||
parent.removeChild(div);
|
||||
|
||||
if (!--gSWCount) {
|
||||
let div = document.getElementById("warning_no_serviceworkers");
|
||||
div.classList.add("active");
|
||||
}
|
||||
},
|
||||
|
||||
unregisterFailed: function() {
|
||||
alert(bundle.GetStringFromName('unregisterError'));
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIServiceWorkerUnregisterCallback])
|
||||
};
|
||||
|
||||
loadingMessage.classList.remove('inactive');
|
||||
gSWM.propagateUnregister(info.principal, cb, info.scope);
|
||||
};
|
||||
|
||||
let sep = document.createElement('hr');
|
||||
div.appendChild(sep);
|
||||
|
||||
++gSWCount;
|
||||
}
|
||||
|
||||
window.addEventListener("DOMContentLoaded", function load() {
|
||||
window.removeEventListener("DOMContentLoaded", load);
|
||||
init();
|
||||
});
|
||||
34
toolkit/content/aboutServiceWorkers.xhtml
Normal file
34
toolkit/content/aboutServiceWorkers.xhtml
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<!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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
|
||||
<!ENTITY % serviceworkersDTD SYSTEM "chrome://global/locale/aboutServiceWorkers.dtd"> %serviceworkersDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&aboutServiceWorkers.title;</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css" />
|
||||
<link rel="stylesheet" href="chrome://mozapps/skin/aboutServiceWorkers.css" type="text/css" />
|
||||
<script type="application/javascript;version=1.7" src="chrome://global/content/aboutServiceWorkers.js" />
|
||||
</head>
|
||||
<body id="body">
|
||||
<div id="warning_not_enabled" class="warningBackground">
|
||||
<div class="warningMessage">&aboutServiceWorkers.warning_not_enabled;</div>
|
||||
</div>
|
||||
|
||||
<div id="warning_no_serviceworkers" class="warningBackground">
|
||||
<div class="warningMessage">&aboutServiceWorkers.warning_no_serviceworkers;</div>
|
||||
</div>
|
||||
|
||||
<div id="serviceworkers" class="tab active">
|
||||
<h1>&aboutServiceWorkers.maintitle;</h1>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
1003
toolkit/content/aboutSupport.js
Normal file
1003
toolkit/content/aboutSupport.js
Normal file
File diff suppressed because it is too large
Load diff
555
toolkit/content/aboutSupport.xhtml
Normal file
555
toolkit/content/aboutSupport.xhtml
Normal file
|
|
@ -0,0 +1,555 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
<!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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
|
||||
<!ENTITY % aboutSupportDTD SYSTEM "chrome://global/locale/aboutSupport.dtd"> %aboutSupportDTD;
|
||||
<!ENTITY % resetProfileDTD SYSTEM "chrome://global/locale/resetProfile.dtd"> %resetProfileDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&aboutSupport.pageTitle;</title>
|
||||
|
||||
<link rel="icon" type="image/png" id="favicon"
|
||||
href="chrome://branding/content/icon32.png"/>
|
||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css"
|
||||
type="text/css"/>
|
||||
<link rel="stylesheet" href="chrome://global/skin/aboutSupport.css"
|
||||
type="text/css"/>
|
||||
|
||||
<script type="application/javascript;version=1.7"
|
||||
src="chrome://global/content/aboutSupport.js"/>
|
||||
<script type="application/javascript;version=1.7"
|
||||
src="chrome://global/content/resetProfile.js"/>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
|
||||
<div id="action-box">
|
||||
<div id="reset-box">
|
||||
<h3>&refreshProfile.title;</h3>
|
||||
<button id="reset-box-button">
|
||||
&refreshProfile.button.label;
|
||||
</button>
|
||||
</div>
|
||||
<div id="safe-mode-box">
|
||||
<h3>&aboutSupport.safeModeTitle;</h3>
|
||||
<button id="restart-in-safe-mode-button">
|
||||
&aboutSupport.restartInSafeMode.label;
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<h1>
|
||||
&aboutSupport.pageTitle;
|
||||
</h1>
|
||||
|
||||
<div class="page-subtitle">
|
||||
&aboutSupport.pageSubtitle;
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="copy-raw-data-to-clipboard">
|
||||
&aboutSupport.copyRawDataToClipboard.label;
|
||||
</button>
|
||||
<button id="copy-to-clipboard">
|
||||
&aboutSupport.copyTextToClipboard.label;
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div id="contents">
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.appBasicsTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsName;
|
||||
</th>
|
||||
|
||||
<td id="application-box">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsVersion;
|
||||
</th>
|
||||
|
||||
<td id="version-box">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsBuildID;
|
||||
</th>
|
||||
<td id="buildid-box"></td>
|
||||
</tr>
|
||||
|
||||
#ifndef ANDROID
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsUpdateHistory;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<button id="show-update-history-button">
|
||||
&aboutSupport.appBasicsShowUpdateHistory;
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_UPDATER
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsUpdateChannel;
|
||||
</th>
|
||||
<td id="updatechannel-box"></td>
|
||||
</tr>
|
||||
#endif
|
||||
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsUserAgent;
|
||||
</th>
|
||||
|
||||
<td id="useragent-box">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsOS;
|
||||
</th>
|
||||
|
||||
<td id="os-box">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr id="profile-row" class="no-copy">
|
||||
<th class="column">
|
||||
#ifdef XP_WIN
|
||||
&aboutSupport.appBasicsProfileDirWinMac;
|
||||
#else
|
||||
#ifdef XP_MACOSX
|
||||
&aboutSupport.appBasicsProfileDirWinMac;
|
||||
#else
|
||||
&aboutSupport.appBasicsProfileDir;
|
||||
#endif
|
||||
#endif
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<button id="profile-dir-button">
|
||||
#ifdef XP_WIN
|
||||
&aboutSupport.showWin2.label;
|
||||
#else
|
||||
#ifdef XP_MACOSX
|
||||
&aboutSupport.showMac.label;
|
||||
#else
|
||||
&aboutSupport.showDir.label;
|
||||
#endif
|
||||
#endif
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsEnabledPlugins;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<a href="about:plugins">about:plugins</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsBuildConfig;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<a href="about:buildconfig">about:buildconfig</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsMemoryUse;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<a href="about:memory">about:memory</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsPerformance;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<a href="about:performance">about:performance</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsServiceWorkers;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<a href="about:serviceworkers">about:serviceworkers</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsMultiProcessSupport;
|
||||
</th>
|
||||
|
||||
<td id="multiprocess-box">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsSafeMode;
|
||||
</th>
|
||||
|
||||
<td id="safemode-box">
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
#ifndef ANDROID
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.appBasicsProfiles;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<a href="about:profiles">about:profiles</a>
|
||||
</td>
|
||||
</tr>
|
||||
#endif
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
#ifdef MOZ_CRASHREPORTER
|
||||
|
||||
<h2 class="major-section" id="crashes-title">
|
||||
&aboutSupport.crashes.title;
|
||||
</h2>
|
||||
|
||||
<table id="crashes-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
&aboutSupport.crashes.id;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.crashes.sendDate;
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="crashes-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
<p id="crashes-allReports" class="hidden no-copy">
|
||||
<a href="about:crashes" id="crashes-allReportsWithPending" class="block">&aboutSupport.crashes.allReports;</a>
|
||||
</p>
|
||||
<p id="crashes-noConfig" class="hidden no-copy">&aboutSupport.crashes.noConfig;</p>
|
||||
|
||||
#endif
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.extensionsTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
&aboutSupport.extensionName;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.extensionVersion;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.extensionEnabled;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.extensionId;
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="extensions-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.graphicsTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<tbody id="graphics-features-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsFeaturesTitle;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-tbody">
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-gpu-1-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsGPU1Title;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-gpu-2-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsGPU2Title;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-diagnostics-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsDiagnosticsTitle;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-decisions-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsDecisionLogTitle;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-crashguards-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsCrashGuardsTitle;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-workarounds-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsWorkaroundsTitle;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
<tbody id="graphics-failures-tbody">
|
||||
<tr>
|
||||
<th colspan="2" class="title-column">
|
||||
&aboutSupport.graphicsFailureLogTitle;
|
||||
</th>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.modifiedKeyPrefsTitle;
|
||||
</h2>
|
||||
|
||||
<table class="prefs-table">
|
||||
<thead class="no-copy">
|
||||
<th class="name">
|
||||
&aboutSupport.modifiedPrefsName;
|
||||
</th>
|
||||
|
||||
<th class="value">
|
||||
&aboutSupport.modifiedPrefsValue;
|
||||
</th>
|
||||
</thead>
|
||||
|
||||
<tbody id="prefs-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<section id="prefs-user-js-section" class="hidden no-copy">
|
||||
<h3>&aboutSupport.userJSTitle;</h3>
|
||||
<p>&aboutSupport.userJSDescription;</p>
|
||||
</section>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.lockedKeyPrefsTitle;
|
||||
</h2>
|
||||
|
||||
<table class="prefs-table">
|
||||
<thead class="no-copy">
|
||||
<th class="name">
|
||||
&aboutSupport.lockedPrefsName;
|
||||
</th>
|
||||
|
||||
<th class="value">
|
||||
&aboutSupport.lockedPrefsValue;
|
||||
</th>
|
||||
</thead>
|
||||
|
||||
<tbody id="locked-prefs-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.placeDatabaseTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<tr class="no-copy">
|
||||
<th class="column">
|
||||
&aboutSupport.placeDatabaseIntegrity;
|
||||
</th>
|
||||
|
||||
<td>
|
||||
<button id="verify-place-integrity-button">
|
||||
&aboutSupport.placeDatabaseVerifyIntegrity;
|
||||
</button>
|
||||
<pre id="verify-place-result" class="hidden no-copy"></pre>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.jsTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.jsIncrementalGC;
|
||||
</th>
|
||||
|
||||
<td id="javascript-incremental-gc">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.a11yTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.a11yActivated;
|
||||
</th>
|
||||
|
||||
<td id="a11y-activated">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="column">
|
||||
&aboutSupport.a11yForceDisabled;
|
||||
</th>
|
||||
|
||||
<td id="a11y-force-disabled">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.libraryVersionsTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<tbody id="libversions-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
<h2 class="major-section">
|
||||
&aboutSupport.experimentsTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
&aboutSupport.experimentName;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.experimentId;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.experimentDescription;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.experimentActive;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.experimentEndDate;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.experimentHomepage;
|
||||
</th>
|
||||
<th>
|
||||
&aboutSupport.experimentBranch;
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="experiments-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
<!-- - - - - - - - - - - - - - - - - - - - - -->
|
||||
|
||||
#if defined(MOZ_SANDBOX)
|
||||
<h2 class="major-section" id="sandbox">
|
||||
&aboutSupport.sandboxTitle;
|
||||
</h2>
|
||||
|
||||
<table>
|
||||
<tbody id="sandbox-tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
#endif
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
271
toolkit/content/aboutTelemetry.css
Normal file
271
toolkit/content/aboutTelemetry.css
Normal file
|
|
@ -0,0 +1,271 @@
|
|||
/* 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/. */
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
html {
|
||||
background-color: -moz-Dialog;
|
||||
color: -moz-DialogText;
|
||||
font: message-box;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: medium;
|
||||
}
|
||||
|
||||
#page-description {
|
||||
border: 1px solid threedshadow;
|
||||
margin: 0px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#settings {
|
||||
border: 1px solid lightgrey;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.description-enabled,
|
||||
.description-disabled {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
.description-enabled > span {
|
||||
color: green;
|
||||
}
|
||||
|
||||
.description-disabled > span {
|
||||
color: red;
|
||||
}
|
||||
|
||||
#ping-picker {
|
||||
margin-top: 10px;
|
||||
border: 1px solid lightgrey;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
#ping-source-picker {
|
||||
margin-left: 5px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.data-section,
|
||||
.data-subsection {
|
||||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
border-top: 1px solid threedshadow;
|
||||
border-bottom: 1px solid threedshadow;
|
||||
margin: 0px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.data-section:not(.has-data),
|
||||
.data-subsection:not(.has-subdata) {
|
||||
color: gray;
|
||||
}
|
||||
|
||||
|
||||
.section-name {
|
||||
font-size: x-large;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.has-data .section-name {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
|
||||
.toggle-caption {
|
||||
font-style: italic;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.data-section:not(.has-data) .toggle-caption,
|
||||
.data-subsection:not(.has-subdata) .toggle-caption {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.empty-caption {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.has-data .empty-caption,
|
||||
.has-subdata .empty-caption {
|
||||
display: none; /* invisible when has-data */
|
||||
}
|
||||
|
||||
.data,
|
||||
.subdata {
|
||||
margin: 15px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.has-data.expanded .data,
|
||||
.has-subdata.expanded .subdata {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.stack-title {
|
||||
font-size: medium;
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#histograms, #addon-histograms, #thread-hang-stats>div {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.histogram {
|
||||
float: left;
|
||||
border: 1px solid gray;
|
||||
white-space: nowrap;
|
||||
padding: 10px;
|
||||
position: relative; /* required for position:absolute of the contained .copy-node */
|
||||
}
|
||||
|
||||
body[dir="rtl"] .histogram {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.histogram-title {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.keyed-histogram {
|
||||
white-space: nowrap;
|
||||
padding: 15px;
|
||||
position: relative; /* required for position:absolute of the contained .copy-node */
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.keyed-histogram-title {
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
margin: 10px;
|
||||
font-weight: bold;
|
||||
font-size: 120%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
.bar {
|
||||
width: 2em;
|
||||
margin: 2px;
|
||||
text-align: center;
|
||||
float: left;
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
body[dir="rtl"] .bar {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.bar-inner {
|
||||
background-color: DeepSkyBlue;
|
||||
border: 1px solid #0000b0;
|
||||
}
|
||||
|
||||
th {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
body[dir="rtl"] th {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
caption {
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
body[dir="rtl"] caption {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.copy-node {
|
||||
visibility: hidden;
|
||||
position: absolute;
|
||||
bottom: 1px;
|
||||
right: 1px;
|
||||
}
|
||||
|
||||
body[dir="rtl"] .copy-node {
|
||||
left: 1px;
|
||||
}
|
||||
|
||||
.histogram:hover .copy-node {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
.statebox {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
.filter-ui {
|
||||
padding-inline-start: 10em;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.has-data.expanded .filter-ui {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.processes-ui {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.has-data.expanded .processes-ui {
|
||||
display: initial;
|
||||
}
|
||||
|
||||
.filter-blocked {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#raw-ping-data-section {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color:-moz-Dialog;
|
||||
}
|
||||
|
||||
#raw-ping-data {
|
||||
background-color:white;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#hide-raw-ping {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
background-color:#d8d8d8;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
/* addon subsection style */
|
||||
.addon-caption {
|
||||
font-size: larger;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
.process-picker {
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
2175
toolkit/content/aboutTelemetry.js
Normal file
2175
toolkit/content/aboutTelemetry.js
Normal file
File diff suppressed because it is too large
Load diff
290
toolkit/content/aboutTelemetry.xhtml
Normal file
290
toolkit/content/aboutTelemetry.xhtml
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!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 % brandDTD SYSTEM "chrome://branding/locale/brand.dtd"> %brandDTD;
|
||||
<!ENTITY % aboutTelemetryDTD SYSTEM "chrome://global/locale/aboutTelemetry.dtd"> %aboutTelemetryDTD;
|
||||
]>
|
||||
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<title>&aboutTelemetry.pageTitle;</title>
|
||||
|
||||
<link rel="stylesheet" href="chrome://global/content/aboutTelemetry.css"
|
||||
type="text/css"/>
|
||||
|
||||
<script type="application/javascript;version=1.7"
|
||||
src="chrome://global/content/aboutTelemetry.js"/>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
|
||||
<header id="page-description">
|
||||
<h1>&aboutTelemetry.pageTitle;</h1>
|
||||
|
||||
<h2 id="page-subtitle"></h2>
|
||||
|
||||
<table id="settings">
|
||||
<tr>
|
||||
<td>
|
||||
<p id="description-upload-enabled" class="description-enabled">&aboutTelemetry.uploadEnabled;</p>
|
||||
<p id="description-upload-disabled" class="description-disabled">&aboutTelemetry.uploadDisabled;</p>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" class="change-data-choices-link">&aboutTelemetry.changeDataChoices;</a>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p id="description-extended-recording-enabled" class="description-enabled">&aboutTelemetry.extendedRecordingEnabled;</p>
|
||||
<p id="description-extended-recording-disabled" class="description-disabled">&aboutTelemetry.extendedRecordingDisabled;</p>
|
||||
</td>
|
||||
<td>
|
||||
<a href="" class="change-data-choices-link">&aboutTelemetry.changeDataChoices;</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div id="ping-picker">
|
||||
<div id="ping-source-picker">
|
||||
&aboutTelemetry.pingDataSource;<br/>
|
||||
<input type="radio" id="ping-source-current" name="choose-ping-source" value="current" checked="checked" />
|
||||
&aboutTelemetry.showCurrentPingData;<br />
|
||||
<input type="radio" id="ping-source-archive" name="choose-ping-source" value="archive" />
|
||||
&aboutTelemetry.showArchivedPingData;<br />
|
||||
</div>
|
||||
<div id="ping-source-picker">
|
||||
&aboutTelemetry.pingDataDisplay;<br/>
|
||||
<input type="radio" id="ping-source-structured" name="choose-ping-display" value="structured" checked="checked" />
|
||||
&aboutTelemetry.structured;<br />
|
||||
<input type="radio" id="ping-source-raw" name="choose-ping-display" value="raw" />
|
||||
&aboutTelemetry.raw;<br />
|
||||
</div>
|
||||
<div id="current-ping-picker">
|
||||
<input id="show-subsession-data" type="checkbox" checked="checked" />&aboutTelemetry.showSubsessionData;
|
||||
</div>
|
||||
<div id="archived-ping-picker" class="hidden">
|
||||
&aboutTelemetry.choosePing;<br />
|
||||
<button id="newer-ping" type="button">&aboutTelemetry.showNewerPing;</button>
|
||||
<button id="older-ping" type="button">&aboutTelemetry.showOlderPing;</button><br />
|
||||
<table>
|
||||
<tr>
|
||||
<th>&aboutTelemetry.archiveWeekHeader;</th>
|
||||
<th>&aboutTelemetry.archivePingHeader;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<select id="choose-ping-week">
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<select id="choose-ping-id">
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<table>
|
||||
<tr>
|
||||
<th>&aboutTelemetry.payloadChoiceHeader;</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<select id="choose-payload">
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div id="raw-ping-data-section" class="hidden">
|
||||
<pre id="raw-ping-data"></pre>
|
||||
</div>
|
||||
|
||||
<div id="structured-ping-data-section">
|
||||
<section id="general-data-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.generalDataSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="general-data" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="environment-data-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.environmentDataSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="environment-data" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="session-info-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.sessionInfoSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="session-info" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="scalars-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.scalarsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="scalars" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="keyed-scalars-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.keyedScalarsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="keyed-scalars" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="histograms-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.histogramsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<span class="filter-ui">
|
||||
&aboutTelemetry.filterText; <input type="text" class="filter" id="histograms-filter" target_id="histograms"/>
|
||||
</span>
|
||||
<div class="processes-ui">
|
||||
<select id="histograms-processes" class="process-picker"></select>
|
||||
</div>
|
||||
<div id="histograms" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="keyed-histograms-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.keyedHistogramsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div class="processes-ui">
|
||||
<select id="keyed-histograms-processes" class="process-picker"></select>
|
||||
</div>
|
||||
<div id="keyed-histograms" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="events-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">Events</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="events" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="simple-measurements-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.simpleMeasurementsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="simple-measurements" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="telemetry-log-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.telemetryLogSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="telemetry-log" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="slow-sql-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.slowSqlSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="slow-sql-tables" class="data">
|
||||
<p id="sql-warning" class="hidden">&aboutTelemetry.fullSqlWarning;</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="chrome-hangs-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.chromeHangsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="chrome-hangs" class="data">
|
||||
<a id="chrome-hangs-fetch-symbols" href="#">&aboutTelemetry.fetchSymbols;</a>
|
||||
<a id="chrome-hangs-hide-symbols" class="hidden" href="#">&aboutTelemetry.hideSymbols;</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<div id="chrome-hangs-data">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="thread-hang-stats-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.threadHangStatsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="thread-hang-stats" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="late-writes-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.lateWritesSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="late-writes" class="data">
|
||||
<a id="late-writes-fetch-symbols" href="#">&aboutTelemetry.fetchSymbols;</a>
|
||||
<a id="late-writes-hide-symbols" class="hidden" href="#">&aboutTelemetry.hideSymbols;</a>
|
||||
<br/>
|
||||
<br/>
|
||||
<div id="late-writes-data">
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="addon-details-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.addonDetailsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="addon-details" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="addon-histograms-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.addonHistogramsSection;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="addon-histograms" class="data">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="raw-payload-section" class="data-section">
|
||||
<input type="checkbox" class="statebox"/>
|
||||
<h1 class="section-name">&aboutTelemetry.rawPayload;</h1>
|
||||
<span class="toggle-caption">&aboutTelemetry.toggle;</span>
|
||||
<span class="empty-caption">&aboutTelemetry.emptySection;</span>
|
||||
<div id="raw-payload-data" class="data">
|
||||
<pre id="raw-payload-data-pre"></pre>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
97
toolkit/content/aboutwebrtc/aboutWebrtc.css
Normal file
97
toolkit/content/aboutwebrtc/aboutWebrtc.css
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
html {
|
||||
background-color: #edeceb;
|
||||
font: message-box;
|
||||
}
|
||||
|
||||
.controls {
|
||||
font-size: 1.1em;
|
||||
display: inline-block;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.control {
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.control > button {
|
||||
margin: 0 0.25em;
|
||||
}
|
||||
|
||||
.message > p {
|
||||
margin: 0.5em 0.5em;
|
||||
}
|
||||
|
||||
.log p {
|
||||
font-family: monospace;
|
||||
padding-left: 2em;
|
||||
text-indent: -2em;
|
||||
margin-top: 2px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
#content > div {
|
||||
padding: 1em 2em;
|
||||
margin: 1em 0;
|
||||
border: 1px solid #afaca9;
|
||||
border-radius: 10px;
|
||||
background: none repeat scroll 0% 0% #fff;
|
||||
}
|
||||
|
||||
.section-heading *
|
||||
{
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.section-heading > button {
|
||||
margin-left: 1em;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.peer-connection > h3
|
||||
{
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.peer-connection table {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.peer-connection table th,
|
||||
.peer-connection table td {
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
.peer-connection table tr:nth-child(even) {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.section-ctrl {
|
||||
margin: 1em 1.5em;
|
||||
}
|
||||
|
||||
div.fold-trigger {
|
||||
color: blue;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media screen {
|
||||
.fold-closed {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media print {
|
||||
.no-print {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
21
toolkit/content/aboutwebrtc/aboutWebrtc.html
Normal file
21
toolkit/content/aboutwebrtc/aboutWebrtc.html
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE HTML>
|
||||
|
||||
<!-- 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>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>about:webrtc</title>
|
||||
<link rel="stylesheet" type="text/css" media="all"
|
||||
href="chrome://global/content/aboutwebrtc/aboutWebrtc.css"/>
|
||||
<script type="text/javascript;version=1.8"
|
||||
src="chrome://global/content/aboutwebrtc/aboutWebrtc.js"
|
||||
defer="defer"></script>
|
||||
</head>
|
||||
<body id="body" onload="onLoad()">
|
||||
<div id="controls" class="no-print"></div>
|
||||
<div id="content"></div>
|
||||
</body>
|
||||
</html>
|
||||
841
toolkit/content/aboutwebrtc/aboutWebrtc.js
Normal file
841
toolkit/content/aboutwebrtc/aboutWebrtc.js
Normal file
|
|
@ -0,0 +1,841 @@
|
|||
/* 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 WebrtcGlobalInformation, document */
|
||||
|
||||
var Cu = Components.utils;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "FilePicker",
|
||||
"@mozilla.org/filepicker;1", "nsIFilePicker");
|
||||
XPCOMUtils.defineLazyGetter(this, "strings", () => {
|
||||
return Services.strings.createBundle("chrome://global/locale/aboutWebrtc.properties");
|
||||
});
|
||||
|
||||
const getString = strings.GetStringFromName;
|
||||
const formatString = strings.formatStringFromName;
|
||||
|
||||
const LOGFILE_NAME_DEFAULT = "aboutWebrtc.html";
|
||||
const WEBRTC_TRACE_ALL = 65535;
|
||||
|
||||
function getStats() {
|
||||
return new Promise(resolve =>
|
||||
WebrtcGlobalInformation.getAllStats(stats => resolve(stats)));
|
||||
}
|
||||
|
||||
function getLog() {
|
||||
return new Promise(resolve =>
|
||||
WebrtcGlobalInformation.getLogging("", log => resolve(log)));
|
||||
}
|
||||
|
||||
// Begin initial data queries as page loads. Store returned Promises for
|
||||
// later use.
|
||||
var reportsRetrieved = getStats();
|
||||
var logRetrieved = getLog();
|
||||
|
||||
function onLoad() {
|
||||
document.title = getString("document_title");
|
||||
let controls = document.querySelector("#controls");
|
||||
if (controls) {
|
||||
let set = ControlSet.render();
|
||||
ControlSet.add(new SavePage());
|
||||
ControlSet.add(new DebugMode());
|
||||
ControlSet.add(new AecLogging());
|
||||
controls.appendChild(set);
|
||||
}
|
||||
|
||||
let contentElem = document.querySelector("#content");
|
||||
if (!contentElem) {
|
||||
return;
|
||||
}
|
||||
|
||||
let contentInit = function(data) {
|
||||
AboutWebRTC.init(onClearStats, onClearLog);
|
||||
AboutWebRTC.render(contentElem, data);
|
||||
};
|
||||
|
||||
Promise.all([reportsRetrieved, logRetrieved])
|
||||
.then(([stats, log]) => contentInit({reports: stats.reports, log: log}))
|
||||
.catch(error => contentInit({error: error}));
|
||||
}
|
||||
|
||||
function onClearLog() {
|
||||
WebrtcGlobalInformation.clearLogging();
|
||||
getLog()
|
||||
.then(log => AboutWebRTC.refresh({log: log}))
|
||||
.catch(error => AboutWebRTC.refresh({logError: error}));
|
||||
}
|
||||
|
||||
function onClearStats() {
|
||||
WebrtcGlobalInformation.clearAllStats();
|
||||
getStats()
|
||||
.then(stats => AboutWebRTC.refresh({reports: stats.reports}))
|
||||
.catch(error => AboutWebRTC.refresh({reportError: error}));
|
||||
}
|
||||
|
||||
var ControlSet = {
|
||||
render: function() {
|
||||
let controls = document.createElement("div");
|
||||
let control = document.createElement("div");
|
||||
let message = document.createElement("div");
|
||||
|
||||
controls.className = "controls";
|
||||
control.className = "control";
|
||||
message.className = "message";
|
||||
controls.appendChild(control);
|
||||
controls.appendChild(message);
|
||||
|
||||
this.controlSection = control;
|
||||
this.messageSection = message;
|
||||
return controls;
|
||||
},
|
||||
|
||||
add: function(controlObj) {
|
||||
let [controlElem, messageElem] = controlObj.render();
|
||||
this.controlSection.appendChild(controlElem);
|
||||
this.messageSection.appendChild(messageElem);
|
||||
}
|
||||
};
|
||||
|
||||
function Control() {
|
||||
this._label = null;
|
||||
this._message = null;
|
||||
this._messageHeader = null;
|
||||
}
|
||||
|
||||
Control.prototype = {
|
||||
render: function () {
|
||||
let controlElem = document.createElement("button");
|
||||
let messageElem = document.createElement("p");
|
||||
|
||||
this.ctrl = controlElem;
|
||||
controlElem.onclick = this.onClick.bind(this);
|
||||
this.msg = messageElem;
|
||||
this.update();
|
||||
|
||||
return [controlElem, messageElem];
|
||||
},
|
||||
|
||||
set label(val) {
|
||||
return this._labelVal = val || "\xA0";
|
||||
},
|
||||
|
||||
get label() {
|
||||
return this._labelVal;
|
||||
},
|
||||
|
||||
set message(val) {
|
||||
return this._messageVal = val;
|
||||
},
|
||||
|
||||
get message() {
|
||||
return this._messageVal;
|
||||
},
|
||||
|
||||
update: function() {
|
||||
this.ctrl.textContent = this._label;
|
||||
|
||||
if (this._message) {
|
||||
this.msg.innerHTML =
|
||||
`<span class="info-label">${this._messageHeader}:</span> ${this._message}`;
|
||||
} else {
|
||||
this.msg.innerHTML = null;
|
||||
}
|
||||
},
|
||||
|
||||
onClick: function(event) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
function SavePage() {
|
||||
Control.call(this);
|
||||
this._messageHeader = getString("save_page_label");
|
||||
this._label = getString("save_page_label");
|
||||
}
|
||||
|
||||
SavePage.prototype = Object.create(Control.prototype);
|
||||
SavePage.prototype.constructor = SavePage;
|
||||
|
||||
SavePage.prototype.onClick = function() {
|
||||
let content = document.querySelector("#content");
|
||||
|
||||
if (!content)
|
||||
return;
|
||||
|
||||
FoldEffect.expandAll();
|
||||
FilePicker.init(window, getString("save_page_dialog_title"), FilePicker.modeSave);
|
||||
FilePicker.defaultString = LOGFILE_NAME_DEFAULT;
|
||||
let rv = FilePicker.show();
|
||||
|
||||
if (rv == FilePicker.returnOK || rv == FilePicker.returnReplace) {
|
||||
let fout = FileUtils.openAtomicFileOutputStream(
|
||||
FilePicker.file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE);
|
||||
|
||||
let nodes = content.querySelectorAll(".no-print");
|
||||
let noPrintList = [];
|
||||
for (let node of nodes) {
|
||||
noPrintList.push(node);
|
||||
node.style.setProperty("display", "none");
|
||||
}
|
||||
|
||||
fout.write(content.outerHTML, content.outerHTML.length);
|
||||
FileUtils.closeAtomicFileOutputStream(fout);
|
||||
|
||||
for (let node of noPrintList) {
|
||||
node.style.removeProperty("display");
|
||||
}
|
||||
|
||||
this._message = formatString("save_page_msg", [FilePicker.file.path], 1);
|
||||
this.update();
|
||||
}
|
||||
};
|
||||
|
||||
function DebugMode() {
|
||||
Control.call(this);
|
||||
this._messageHeader = getString("debug_mode_msg_label");
|
||||
|
||||
if (WebrtcGlobalInformation.debugLevel > 0) {
|
||||
this.onState();
|
||||
} else {
|
||||
this._label = getString("debug_mode_off_state_label");
|
||||
this._message = null;
|
||||
}
|
||||
}
|
||||
|
||||
DebugMode.prototype = Object.create(Control.prototype);
|
||||
DebugMode.prototype.constructor = DebugMode;
|
||||
|
||||
DebugMode.prototype.onState = function() {
|
||||
this._label = getString("debug_mode_on_state_label");
|
||||
try {
|
||||
let file = Services.prefs.getCharPref("media.webrtc.debug.log_file");
|
||||
this._message = formatString("debug_mode_on_state_msg", [file], 1);
|
||||
} catch (e) {
|
||||
this._message = null;
|
||||
}
|
||||
};
|
||||
|
||||
DebugMode.prototype.offState = function() {
|
||||
this._label = getString("debug_mode_off_state_label");
|
||||
try {
|
||||
let file = Services.prefs.getCharPref("media.webrtc.debug.log_file");
|
||||
this._message = formatString("debug_mode_off_state_msg", [file], 1);
|
||||
} catch (e) {
|
||||
this._message = null;
|
||||
}
|
||||
};
|
||||
|
||||
DebugMode.prototype.onClick = function() {
|
||||
if (WebrtcGlobalInformation.debugLevel > 0) {
|
||||
WebrtcGlobalInformation.debugLevel = 0;
|
||||
this.offState();
|
||||
} else {
|
||||
WebrtcGlobalInformation.debugLevel = WEBRTC_TRACE_ALL;
|
||||
this.onState();
|
||||
}
|
||||
|
||||
this.update();
|
||||
};
|
||||
|
||||
function AecLogging() {
|
||||
Control.call(this);
|
||||
this._messageHeader = getString("aec_logging_msg_label");
|
||||
|
||||
if (WebrtcGlobalInformation.aecDebug) {
|
||||
this.onState();
|
||||
} else {
|
||||
this._label = getString("aec_logging_off_state_label");
|
||||
this._message = null;
|
||||
}
|
||||
}
|
||||
|
||||
AecLogging.prototype = Object.create(Control.prototype);
|
||||
AecLogging.prototype.constructor = AecLogging;
|
||||
|
||||
AecLogging.prototype.offState = function () {
|
||||
this._label = getString("aec_logging_off_state_label");
|
||||
try {
|
||||
let file = Services.prefs.getCharPref("media.webrtc.debug.aec_log_dir");
|
||||
this._message = formatString("aec_logging_off_state_msg", [file], 1);
|
||||
} catch (e) {
|
||||
this._message = null;
|
||||
}
|
||||
};
|
||||
|
||||
AecLogging.prototype.onState = function () {
|
||||
this._label = getString("aec_logging_on_state_label");
|
||||
try {
|
||||
let file = Services.prefs.getCharPref("media.webrtc.debug.aec_log_dir");
|
||||
this._message = getString("aec_logging_on_state_msg");
|
||||
} catch (e) {
|
||||
this._message = null;
|
||||
}
|
||||
};
|
||||
|
||||
AecLogging.prototype.onClick = function () {
|
||||
if (WebrtcGlobalInformation.aecDebug) {
|
||||
WebrtcGlobalInformation.aecDebug = false;
|
||||
this.offState();
|
||||
} else {
|
||||
WebrtcGlobalInformation.aecDebug = true;
|
||||
this.onState();
|
||||
}
|
||||
this.update();
|
||||
};
|
||||
|
||||
var AboutWebRTC = {
|
||||
_reports: [],
|
||||
_log: [],
|
||||
|
||||
init: function(onClearStats, onClearLog) {
|
||||
this._onClearStats = onClearStats;
|
||||
this._onClearLog = onClearLog;
|
||||
},
|
||||
|
||||
render: function(parent, data) {
|
||||
this._content = parent;
|
||||
this._setData(data);
|
||||
|
||||
if (data.error) {
|
||||
let msg = document.createElement("h3");
|
||||
msg.textContent = getString("cannot_retrieve_log");
|
||||
parent.appendChild(msg);
|
||||
msg = document.createElement("p");
|
||||
msg.innerHTML = `${data.error.name}: ${data.error.message}`;
|
||||
parent.appendChild(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this._peerConnections = this.renderPeerConnections();
|
||||
this._connectionLog = this.renderConnectionLog();
|
||||
this._content.appendChild(this._peerConnections);
|
||||
this._content.appendChild(this._connectionLog);
|
||||
},
|
||||
|
||||
_setData: function(data) {
|
||||
if (data.reports) {
|
||||
this._reports = data.reports;
|
||||
}
|
||||
|
||||
if (data.log) {
|
||||
this._log = data.log;
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function(data) {
|
||||
this._setData(data);
|
||||
let pc = this._peerConnections;
|
||||
this._peerConnections = this.renderPeerConnections();
|
||||
let log = this._connectionLog;
|
||||
this._connectionLog = this.renderConnectionLog();
|
||||
this._content.replaceChild(this._peerConnections, pc);
|
||||
this._content.replaceChild(this._connectionLog, log);
|
||||
},
|
||||
|
||||
renderPeerConnections: function() {
|
||||
let connections = document.createElement("div");
|
||||
connections.className = "stats";
|
||||
|
||||
let heading = document.createElement("span");
|
||||
heading.className = "section-heading";
|
||||
let elem = document.createElement("h3");
|
||||
elem.textContent = getString("stats_heading");
|
||||
heading.appendChild(elem);
|
||||
|
||||
elem = document.createElement("button");
|
||||
elem.textContent = "Clear History";
|
||||
elem.className = "no-print";
|
||||
elem.onclick = this._onClearStats;
|
||||
heading.appendChild(elem);
|
||||
connections.appendChild(heading);
|
||||
|
||||
if (!this._reports || !this._reports.length) {
|
||||
return connections;
|
||||
}
|
||||
|
||||
let reports = [...this._reports];
|
||||
reports.sort((a, b) => b.timestamp - a.timestamp);
|
||||
for (let report of reports) {
|
||||
let peerConnection = new PeerConnection(report);
|
||||
connections.appendChild(peerConnection.render());
|
||||
}
|
||||
|
||||
return connections;
|
||||
},
|
||||
|
||||
renderConnectionLog: function() {
|
||||
let content = document.createElement("div");
|
||||
content.className = "log";
|
||||
|
||||
let heading = document.createElement("span");
|
||||
heading.className = "section-heading";
|
||||
let elem = document.createElement("h3");
|
||||
elem.textContent = getString("log_heading");
|
||||
heading.appendChild(elem);
|
||||
elem = document.createElement("button");
|
||||
elem.textContent = "Clear Log";
|
||||
elem.className = "no-print";
|
||||
elem.onclick = this._onClearLog;
|
||||
heading.appendChild(elem);
|
||||
content.appendChild(heading);
|
||||
|
||||
if (!this._log || !this._log.length) {
|
||||
return content;
|
||||
}
|
||||
|
||||
let div = document.createElement("div");
|
||||
let sectionCtrl = document.createElement("div");
|
||||
sectionCtrl.className = "section-ctrl no-print";
|
||||
let foldEffect = new FoldEffect(div, {
|
||||
showMsg: getString("log_show_msg"),
|
||||
hideMsg: getString("log_hide_msg")
|
||||
});
|
||||
sectionCtrl.appendChild(foldEffect.render());
|
||||
content.appendChild(sectionCtrl);
|
||||
|
||||
for (let line of this._log) {
|
||||
elem = document.createElement("p");
|
||||
elem.textContent = line;
|
||||
div.appendChild(elem);
|
||||
}
|
||||
|
||||
content.appendChild(div);
|
||||
return content;
|
||||
}
|
||||
};
|
||||
|
||||
function PeerConnection(report) {
|
||||
this._report = report;
|
||||
}
|
||||
|
||||
PeerConnection.prototype = {
|
||||
render: function() {
|
||||
let pc = document.createElement("div");
|
||||
pc.className = "peer-connection";
|
||||
pc.appendChild(this.renderHeading());
|
||||
|
||||
let div = document.createElement("div");
|
||||
let sectionCtrl = document.createElement("div");
|
||||
sectionCtrl.className = "section-ctrl no-print";
|
||||
let foldEffect = new FoldEffect(div);
|
||||
sectionCtrl.appendChild(foldEffect.render());
|
||||
pc.appendChild(sectionCtrl);
|
||||
|
||||
div.appendChild(this.renderDesc());
|
||||
div.appendChild(new ICEStats(this._report).render());
|
||||
div.appendChild(new SDPStats(this._report).render());
|
||||
div.appendChild(new RTPStats(this._report).render());
|
||||
|
||||
pc.appendChild(div);
|
||||
return pc;
|
||||
},
|
||||
|
||||
renderHeading: function () {
|
||||
let pcInfo = this.getPCInfo(this._report);
|
||||
let heading = document.createElement("h3");
|
||||
let now = new Date(this._report.timestamp).toTimeString();
|
||||
heading.textContent =
|
||||
`[ ${pcInfo.id} ] ${pcInfo.url} ${pcInfo.closed ? `(${getString("connection_closed")})` : ""} ${now}`;
|
||||
return heading;
|
||||
},
|
||||
|
||||
renderDesc: function() {
|
||||
let info = document.createElement("div");
|
||||
let label = document.createElement("span");
|
||||
let body = document.createElement("span");
|
||||
|
||||
label.className = "info-label";
|
||||
label.textContent = `${getString("peer_connection_id_label")}: `;
|
||||
info.appendChild(label);
|
||||
|
||||
body.className = "info-body";
|
||||
body.textContent = this._report.pcid;
|
||||
info.appendChild(body);
|
||||
|
||||
return info;
|
||||
},
|
||||
|
||||
getPCInfo: function(report) {
|
||||
return {
|
||||
id: report.pcid.match(/id=(\S+)/)[1],
|
||||
url: report.pcid.match(/url=([^)]+)/)[1],
|
||||
closed: report.closed
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
function SDPStats(report) {
|
||||
this._report = report;
|
||||
}
|
||||
|
||||
SDPStats.prototype = {
|
||||
render: function() {
|
||||
let div = document.createElement("div");
|
||||
let elem = document.createElement("h4");
|
||||
|
||||
elem.textContent = getString("sdp_heading");
|
||||
div.appendChild(elem);
|
||||
|
||||
elem = document.createElement("h5");
|
||||
elem.textContent = getString("local_sdp_heading");
|
||||
div.appendChild(elem);
|
||||
|
||||
elem = document.createElement("pre");
|
||||
elem.textContent = this._report.localSdp;
|
||||
div.appendChild(elem);
|
||||
|
||||
elem = document.createElement("h5");
|
||||
elem.textContent = getString("remote_sdp_heading");
|
||||
div.appendChild(elem);
|
||||
|
||||
elem = document.createElement("pre");
|
||||
elem.textContent = this._report.remoteSdp;
|
||||
div.appendChild(elem);
|
||||
|
||||
return div;
|
||||
}
|
||||
};
|
||||
|
||||
function RTPStats(report) {
|
||||
this._report = report;
|
||||
this._stats = [];
|
||||
}
|
||||
|
||||
RTPStats.prototype = {
|
||||
render: function() {
|
||||
let div = document.createElement("div");
|
||||
let heading = document.createElement("h4");
|
||||
|
||||
heading.textContent = getString("rtp_stats_heading");
|
||||
div.appendChild(heading);
|
||||
|
||||
this.generateRTPStats();
|
||||
|
||||
for (let statSet of this._stats) {
|
||||
div.appendChild(this.renderRTPStatSet(statSet));
|
||||
}
|
||||
|
||||
return div;
|
||||
},
|
||||
|
||||
generateRTPStats: function() {
|
||||
let remoteRtpStats = {};
|
||||
let rtpStats = [].concat((this._report.inboundRTPStreamStats || []),
|
||||
(this._report.outboundRTPStreamStats || []));
|
||||
|
||||
// Generate an id-to-streamStat index for each streamStat that is marked
|
||||
// as a remote. This will be used next to link the remote to its local side.
|
||||
for (let stats of rtpStats) {
|
||||
if (stats.isRemote) {
|
||||
remoteRtpStats[stats.id] = stats;
|
||||
}
|
||||
}
|
||||
|
||||
// If a streamStat has a remoteId attribute, create a remoteRtpStats
|
||||
// attribute that references the remote streamStat entry directly.
|
||||
// That is, the index generated above is merged into the returned list.
|
||||
for (let stats of rtpStats) {
|
||||
if (stats.remoteId) {
|
||||
stats.remoteRtpStats = remoteRtpStats[stats.remoteId];
|
||||
}
|
||||
}
|
||||
|
||||
this._stats = rtpStats;
|
||||
},
|
||||
|
||||
renderAvStats: function(stats) {
|
||||
let statsString = "";
|
||||
|
||||
if (stats.mozAvSyncDelay) {
|
||||
statsString += `${getString("av_sync_label")}: ${stats.mozAvSyncDelay} ms `;
|
||||
}
|
||||
if (stats.mozJitterBufferDelay) {
|
||||
statsString += `${getString("jitter_buffer_delay_label")}: ${stats.mozJitterBufferDelay} ms`;
|
||||
}
|
||||
|
||||
let line = document.createElement("p");
|
||||
line.textContent = statsString;
|
||||
return line;
|
||||
},
|
||||
|
||||
renderCoderStats: function(stats) {
|
||||
let statsString = "";
|
||||
let label;
|
||||
|
||||
if (stats.bitrateMean) {
|
||||
statsString += ` ${getString("avg_bitrate_label")}: ${(stats.bitrateMean / 1000000).toFixed(2)} Mbps`;
|
||||
if (stats.bitrateStdDev) {
|
||||
statsString += ` (${(stats.bitrateStdDev / 1000000).toFixed(2)} SD)`;
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.framerateMean) {
|
||||
statsString += ` ${getString("avg_framerate_label")}: ${(stats.framerateMean).toFixed(2)} fps`;
|
||||
if (stats.framerateStdDev) {
|
||||
statsString += ` (${stats.framerateStdDev.toFixed(2)} SD)`;
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.droppedFrames) {
|
||||
statsString += ` ${getString("dropped_frames_label")}: ${stats.droppedFrames}`;
|
||||
}
|
||||
if (stats.discardedPackets) {
|
||||
statsString += ` ${getString("discarded_packets_label")}: ${stats.discardedPackets}`;
|
||||
}
|
||||
|
||||
if (statsString) {
|
||||
label = (stats.packetsReceived ? ` ${getString("decoder_label")}:` : ` ${getString("encoder_label")}:`);
|
||||
statsString = label + statsString;
|
||||
}
|
||||
|
||||
let line = document.createElement("p");
|
||||
line.textContent = statsString;
|
||||
return line;
|
||||
},
|
||||
|
||||
renderTransportStats: function(stats, typeLabel) {
|
||||
let time = new Date(stats.timestamp).toTimeString();
|
||||
let statsString = `${typeLabel}: ${time} ${stats.type} SSRC: ${stats.ssrc}`;
|
||||
|
||||
if (stats.packetsReceived) {
|
||||
statsString += ` ${getString("received_label")}: ${stats.packetsReceived} ${getString("packets")}`;
|
||||
|
||||
if (stats.bytesReceived) {
|
||||
statsString += ` (${(stats.bytesReceived / 1024).toFixed(2)} Kb)`;
|
||||
}
|
||||
|
||||
statsString += ` ${getString("lost_label")}: ${stats.packetsLost} ${getString("jitter_label")}: ${stats.jitter}`;
|
||||
|
||||
if (stats.mozRtt) {
|
||||
statsString += ` RTT: ${stats.mozRtt} ms`;
|
||||
}
|
||||
} else if (stats.packetsSent) {
|
||||
statsString += ` ${getString("sent_label")}: ${stats.packetsSent} ${getString("packets")}`;
|
||||
if (stats.bytesSent) {
|
||||
statsString += ` (${(stats.bytesSent / 1024).toFixed(2)} Kb)`;
|
||||
}
|
||||
}
|
||||
|
||||
let line = document.createElement("p");
|
||||
line.textContent = statsString;
|
||||
return line;
|
||||
},
|
||||
|
||||
renderRTPStatSet: function(stats) {
|
||||
let div = document.createElement("div");
|
||||
let heading = document.createElement("h5");
|
||||
|
||||
heading.textContent = stats.id;
|
||||
div.appendChild(heading);
|
||||
|
||||
if (stats.MozAvSyncDelay || stats.mozJitterBufferDelay) {
|
||||
div.appendChild(this.renderAvStats(stats));
|
||||
}
|
||||
|
||||
div.appendChild(this.renderCoderStats(stats));
|
||||
div.appendChild(this.renderTransportStats(stats, getString("typeLocal")));
|
||||
|
||||
if (stats.remoteId && stats.remoteRtpStats) {
|
||||
div.appendChild(this.renderTransportStats(stats.remoteRtpStats, getString("typeRemote")));
|
||||
}
|
||||
|
||||
return div;
|
||||
},
|
||||
};
|
||||
|
||||
function ICEStats(report) {
|
||||
this._report = report;
|
||||
}
|
||||
|
||||
ICEStats.prototype = {
|
||||
render: function() {
|
||||
let tbody = [];
|
||||
for (let stat of this.generateICEStats()) {
|
||||
tbody.push([
|
||||
stat.localcandidate || "",
|
||||
stat.remotecandidate || "",
|
||||
stat.state || "",
|
||||
stat.priority || "",
|
||||
stat.nominated || "",
|
||||
stat.selected || ""
|
||||
]);
|
||||
}
|
||||
|
||||
let statsTable = new SimpleTable(
|
||||
[getString("local_candidate"), getString("remote_candidate"), getString("ice_state"),
|
||||
getString("priority"), getString("nominated"), getString("selected")],
|
||||
tbody);
|
||||
|
||||
let div = document.createElement("div");
|
||||
let heading = document.createElement("h4");
|
||||
|
||||
heading.textContent = getString("ice_stats_heading");
|
||||
div.appendChild(heading);
|
||||
div.appendChild(statsTable.render());
|
||||
|
||||
return div;
|
||||
},
|
||||
|
||||
generateICEStats: function() {
|
||||
// Create an index based on candidate ID for each element in the
|
||||
// iceCandidateStats array.
|
||||
let candidates = new Map();
|
||||
|
||||
for (let candidate of this._report.iceCandidateStats) {
|
||||
candidates.set(candidate.id, candidate);
|
||||
}
|
||||
|
||||
// A component may have a remote or local candidate address or both.
|
||||
// Combine those with both; these will be the peer candidates.
|
||||
let matched = {};
|
||||
let stats = [];
|
||||
let stat;
|
||||
|
||||
for (let pair of this._report.iceCandidatePairStats) {
|
||||
let local = candidates.get(pair.localCandidateId);
|
||||
let remote = candidates.get(pair.remoteCandidateId);
|
||||
|
||||
if (local) {
|
||||
stat = {
|
||||
localcandidate: this.candidateToString(local),
|
||||
state: pair.state,
|
||||
priority: pair.priority,
|
||||
nominated: pair.nominated,
|
||||
selected: pair.selected
|
||||
};
|
||||
matched[local.id] = true;
|
||||
|
||||
if (remote) {
|
||||
stat.remotecandidate = this.candidateToString(remote);
|
||||
matched[remote.id] = true;
|
||||
}
|
||||
stats.push(stat);
|
||||
}
|
||||
}
|
||||
|
||||
for (let c of candidates.values()) {
|
||||
if (matched[c.id])
|
||||
continue;
|
||||
|
||||
stat = {};
|
||||
stat[c.type] = this.candidateToString(c);
|
||||
stats.push(stat);
|
||||
}
|
||||
|
||||
return stats.sort((a, b) => (b.priority || 0) - (a.priority || 0));
|
||||
},
|
||||
|
||||
candidateToString: function(c) {
|
||||
if (!c) {
|
||||
return "*";
|
||||
}
|
||||
|
||||
var type = c.candidateType;
|
||||
|
||||
if (c.type == "localcandidate" && c.candidateType == "relayed") {
|
||||
type = `${c.candidateType}-${c.mozLocalTransport}`;
|
||||
}
|
||||
|
||||
return `${c.ipAddress}:${c.portNumber}/${c.transport}(${type})`;
|
||||
}
|
||||
};
|
||||
|
||||
function SimpleTable(heading, data) {
|
||||
this._heading = heading || [];
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
SimpleTable.prototype = {
|
||||
renderRow: function(list) {
|
||||
let row = document.createElement("tr");
|
||||
|
||||
for (let elem of list) {
|
||||
let cell = document.createElement("td");
|
||||
cell.textContent = elem;
|
||||
row.appendChild(cell);
|
||||
}
|
||||
|
||||
return row;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let table = document.createElement("table");
|
||||
|
||||
if (this._heading) {
|
||||
table.appendChild(this.renderRow(this._heading));
|
||||
}
|
||||
|
||||
for (let row of this._data) {
|
||||
table.appendChild(this.renderRow(row));
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
};
|
||||
|
||||
function FoldEffect(targetElem, options = {}) {
|
||||
if (targetElem) {
|
||||
this._showMsg = "\u25BC " + (options.showMsg || getString("fold_show_msg"));
|
||||
this._showHint = options.showHint || getString("fold_show_hint");
|
||||
this._hideMsg = "\u25B2 " + (options.hideMsg || getString("fold_hide_msg"));
|
||||
this._hideHint = options.hideHint || getString("fold_hide_hint");
|
||||
this._target = targetElem;
|
||||
}
|
||||
}
|
||||
|
||||
FoldEffect.prototype = {
|
||||
render: function() {
|
||||
this._target.classList.add("fold-target");
|
||||
|
||||
let ctrl = document.createElement("div");
|
||||
this._trigger = ctrl;
|
||||
ctrl.className = "fold-trigger";
|
||||
ctrl.addEventListener("click", this.onClick.bind(this));
|
||||
this.close();
|
||||
|
||||
FoldEffect._sections.push(this);
|
||||
return ctrl;
|
||||
},
|
||||
|
||||
onClick: function() {
|
||||
if (this._target.classList.contains("fold-closed")) {
|
||||
this.open();
|
||||
} else {
|
||||
this.close();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
|
||||
open: function() {
|
||||
this._target.classList.remove("fold-closed");
|
||||
this._trigger.setAttribute("title", this._hideHint);
|
||||
this._trigger.textContent = this._hideMsg;
|
||||
},
|
||||
|
||||
close: function() {
|
||||
this._target.classList.add("fold-closed");
|
||||
this._trigger.setAttribute("title", this._showHint);
|
||||
this._trigger.textContent = this._showMsg;
|
||||
}
|
||||
};
|
||||
|
||||
FoldEffect._sections = [];
|
||||
|
||||
FoldEffect.expandAll = function() {
|
||||
for (let section of this._sections) {
|
||||
section.open();
|
||||
}
|
||||
};
|
||||
|
||||
FoldEffect.collapseAll = function() {
|
||||
for (let section of this._sections) {
|
||||
section.close();
|
||||
}
|
||||
};
|
||||
40
toolkit/content/autocomplete.css
Normal file
40
toolkit/content/autocomplete.css
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* 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/. */
|
||||
|
||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
|
||||
@namespace html url("http://www.w3.org/1999/xhtml");
|
||||
|
||||
/* Apply crisp rendering for favicons at exactly 2dppx resolution */
|
||||
@media (resolution: 2dppx) {
|
||||
.ac-site-icon {
|
||||
image-rendering: -moz-crisp-edges;
|
||||
}
|
||||
}
|
||||
|
||||
richlistitem {
|
||||
-moz-box-orient: horizontal;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.ac-title-text,
|
||||
.ac-tags-text,
|
||||
.ac-url-text,
|
||||
.ac-action-text {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ac-tags[empty] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ac-action[actiontype=searchengine]:not([selected]),
|
||||
.ac-separator[actiontype=searchengine]:not([selected]) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.ac-separator[type=keyword] {
|
||||
display: none;
|
||||
}
|
||||
625
toolkit/content/browser-child.js
Normal file
625
toolkit/content/browser-child.js
Normal file
|
|
@ -0,0 +1,625 @@
|
|||
/* 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/. */
|
||||
|
||||
var Cc = Components.classes;
|
||||
var Ci = Components.interfaces;
|
||||
var Cu = Components.utils;
|
||||
var Cr = Components.results;
|
||||
|
||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||
Cu.import("resource://gre/modules/BrowserUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
Cu.import("resource://gre/modules/RemoteAddonsChild.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PageThumbUtils",
|
||||
"resource://gre/modules/PageThumbUtils.jsm");
|
||||
|
||||
if (AppConstants.MOZ_CRASHREPORTER) {
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
|
||||
"@mozilla.org/xre/app-info;1",
|
||||
"nsICrashReporter");
|
||||
}
|
||||
|
||||
function makeInputStream(aString) {
|
||||
let stream = Cc["@mozilla.org/io/string-input-stream;1"].
|
||||
createInstance(Ci.nsISupportsCString);
|
||||
stream.data = aString;
|
||||
return stream; // XPConnect will QI this to nsIInputStream for us.
|
||||
}
|
||||
|
||||
var WebProgressListener = {
|
||||
init: function() {
|
||||
this._filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
|
||||
.createInstance(Ci.nsIWebProgress);
|
||||
this._filter.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.addProgressListener(this._filter, Ci.nsIWebProgress.NOTIFY_ALL);
|
||||
},
|
||||
|
||||
uninit() {
|
||||
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebProgress);
|
||||
webProgress.removeProgressListener(this._filter);
|
||||
|
||||
this._filter.removeProgressListener(this);
|
||||
this._filter = null;
|
||||
},
|
||||
|
||||
_requestSpec: function (aRequest, aPropertyName) {
|
||||
if (!aRequest || !(aRequest instanceof Ci.nsIChannel))
|
||||
return null;
|
||||
return aRequest.QueryInterface(Ci.nsIChannel)[aPropertyName].spec;
|
||||
},
|
||||
|
||||
_setupJSON: function setupJSON(aWebProgress, aRequest) {
|
||||
let innerWindowID = null;
|
||||
if (aWebProgress) {
|
||||
let domWindowID = null;
|
||||
try {
|
||||
let utils = aWebProgress.DOMWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
domWindowID = utils.outerWindowID;
|
||||
innerWindowID = utils.currentInnerWindowID;
|
||||
} catch (e) {
|
||||
// If nsDocShell::Destroy has already been called, then we'll
|
||||
// get NS_NOINTERFACE when trying to get the DOM window.
|
||||
// If there is no current inner window, we'll get
|
||||
// NS_ERROR_NOT_AVAILABLE.
|
||||
}
|
||||
|
||||
aWebProgress = {
|
||||
isTopLevel: aWebProgress.isTopLevel,
|
||||
isLoadingDocument: aWebProgress.isLoadingDocument,
|
||||
loadType: aWebProgress.loadType,
|
||||
DOMWindowID: domWindowID
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
webProgress: aWebProgress || null,
|
||||
requestURI: this._requestSpec(aRequest, "URI"),
|
||||
originalRequestURI: this._requestSpec(aRequest, "originalURI"),
|
||||
documentContentType: content.document && content.document.contentType,
|
||||
innerWindowID,
|
||||
};
|
||||
},
|
||||
|
||||
_setupObjects: function setupObjects(aWebProgress, aRequest) {
|
||||
let domWindow;
|
||||
try {
|
||||
domWindow = aWebProgress && aWebProgress.DOMWindow;
|
||||
} catch (e) {
|
||||
// If nsDocShell::Destroy has already been called, then we'll
|
||||
// get NS_NOINTERFACE when trying to get the DOM window. Ignore
|
||||
// that here.
|
||||
domWindow = null;
|
||||
}
|
||||
|
||||
return {
|
||||
contentWindow: content,
|
||||
// DOMWindow is not necessarily the content-window with subframes.
|
||||
DOMWindow: domWindow,
|
||||
webProgress: aWebProgress,
|
||||
request: aRequest,
|
||||
};
|
||||
},
|
||||
|
||||
_send(name, data, objects) {
|
||||
if (RemoteAddonsChild.useSyncWebProgress) {
|
||||
sendRpcMessage(name, data, objects);
|
||||
} else {
|
||||
sendAsyncMessage(name, data, objects);
|
||||
}
|
||||
},
|
||||
|
||||
onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
|
||||
let json = this._setupJSON(aWebProgress, aRequest);
|
||||
let objects = this._setupObjects(aWebProgress, aRequest);
|
||||
|
||||
json.stateFlags = aStateFlags;
|
||||
json.status = aStatus;
|
||||
|
||||
// It's possible that this state change was triggered by
|
||||
// loading an internal error page, for which the parent
|
||||
// will want to know some details, so we'll update it with
|
||||
// the documentURI.
|
||||
if (aWebProgress && aWebProgress.isTopLevel) {
|
||||
json.documentURI = content.document.documentURIObject.spec;
|
||||
json.charset = content.document.characterSet;
|
||||
json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
|
||||
json.inLoadURI = WebNavigation.inLoadURI;
|
||||
}
|
||||
|
||||
this._send("Content:StateChange", json, objects);
|
||||
},
|
||||
|
||||
onProgressChange: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
|
||||
let json = this._setupJSON(aWebProgress, aRequest);
|
||||
let objects = this._setupObjects(aWebProgress, aRequest);
|
||||
|
||||
json.curSelf = aCurSelf;
|
||||
json.maxSelf = aMaxSelf;
|
||||
json.curTotal = aCurTotal;
|
||||
json.maxTotal = aMaxTotal;
|
||||
|
||||
this._send("Content:ProgressChange", json, objects);
|
||||
},
|
||||
|
||||
onProgressChange64: function onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal) {
|
||||
this.onProgressChange(aWebProgress, aRequest, aCurSelf, aMaxSelf, aCurTotal, aMaxTotal);
|
||||
},
|
||||
|
||||
onLocationChange: function onLocationChange(aWebProgress, aRequest, aLocationURI, aFlags) {
|
||||
let json = this._setupJSON(aWebProgress, aRequest);
|
||||
let objects = this._setupObjects(aWebProgress, aRequest);
|
||||
|
||||
json.location = aLocationURI ? aLocationURI.spec : "";
|
||||
json.flags = aFlags;
|
||||
|
||||
// These properties can change even for a sub-frame navigation.
|
||||
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
json.canGoBack = webNav.canGoBack;
|
||||
json.canGoForward = webNav.canGoForward;
|
||||
|
||||
if (aWebProgress && aWebProgress.isTopLevel) {
|
||||
json.documentURI = content.document.documentURIObject.spec;
|
||||
json.title = content.document.title;
|
||||
json.charset = content.document.characterSet;
|
||||
json.mayEnableCharacterEncodingMenu = docShell.mayEnableCharacterEncodingMenu;
|
||||
json.principal = content.document.nodePrincipal;
|
||||
json.synthetic = content.document.mozSyntheticDocument;
|
||||
json.inLoadURI = WebNavigation.inLoadURI;
|
||||
|
||||
if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
|
||||
let uri = aLocationURI.clone();
|
||||
try {
|
||||
// If the current URI contains a username/password, remove it.
|
||||
uri.userPass = "";
|
||||
} catch (ex) { /* Ignore failures on about: URIs. */ }
|
||||
CrashReporter.annotateCrashReport("URL", uri.spec);
|
||||
}
|
||||
}
|
||||
|
||||
this._send("Content:LocationChange", json, objects);
|
||||
},
|
||||
|
||||
onStatusChange: function onStatusChange(aWebProgress, aRequest, aStatus, aMessage) {
|
||||
let json = this._setupJSON(aWebProgress, aRequest);
|
||||
let objects = this._setupObjects(aWebProgress, aRequest);
|
||||
|
||||
json.status = aStatus;
|
||||
json.message = aMessage;
|
||||
|
||||
this._send("Content:StatusChange", json, objects);
|
||||
},
|
||||
|
||||
onSecurityChange: function onSecurityChange(aWebProgress, aRequest, aState) {
|
||||
let json = this._setupJSON(aWebProgress, aRequest);
|
||||
let objects = this._setupObjects(aWebProgress, aRequest);
|
||||
|
||||
json.state = aState;
|
||||
json.status = SecurityUI.getSSLStatusAsString();
|
||||
|
||||
this._send("Content:SecurityChange", json, objects);
|
||||
},
|
||||
|
||||
onRefreshAttempted: function onRefreshAttempted(aWebProgress, aURI, aDelay, aSameURI) {
|
||||
return true;
|
||||
},
|
||||
|
||||
sendLoadCallResult() {
|
||||
sendAsyncMessage("Content:LoadURIResult");
|
||||
},
|
||||
|
||||
QueryInterface: function QueryInterface(aIID) {
|
||||
if (aIID.equals(Ci.nsIWebProgressListener) ||
|
||||
aIID.equals(Ci.nsIWebProgressListener2) ||
|
||||
aIID.equals(Ci.nsISupportsWeakReference) ||
|
||||
aIID.equals(Ci.nsISupports)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
throw Components.results.NS_ERROR_NO_INTERFACE;
|
||||
}
|
||||
};
|
||||
|
||||
WebProgressListener.init();
|
||||
addEventListener("unload", () => {
|
||||
WebProgressListener.uninit();
|
||||
});
|
||||
|
||||
var WebNavigation = {
|
||||
init: function() {
|
||||
addMessageListener("WebNavigation:GoBack", this);
|
||||
addMessageListener("WebNavigation:GoForward", this);
|
||||
addMessageListener("WebNavigation:GotoIndex", this);
|
||||
addMessageListener("WebNavigation:LoadURI", this);
|
||||
addMessageListener("WebNavigation:SetOriginAttributes", this);
|
||||
addMessageListener("WebNavigation:Reload", this);
|
||||
addMessageListener("WebNavigation:Stop", this);
|
||||
},
|
||||
|
||||
get webNavigation() {
|
||||
return docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
},
|
||||
|
||||
_inLoadURI: false,
|
||||
|
||||
get inLoadURI() {
|
||||
return this._inLoadURI;
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
switch (message.name) {
|
||||
case "WebNavigation:GoBack":
|
||||
this.goBack();
|
||||
break;
|
||||
case "WebNavigation:GoForward":
|
||||
this.goForward();
|
||||
break;
|
||||
case "WebNavigation:GotoIndex":
|
||||
this.gotoIndex(message.data.index);
|
||||
break;
|
||||
case "WebNavigation:LoadURI":
|
||||
this.loadURI(message.data.uri, message.data.flags,
|
||||
message.data.referrer, message.data.referrerPolicy,
|
||||
message.data.postData, message.data.headers,
|
||||
message.data.baseURI);
|
||||
break;
|
||||
case "WebNavigation:SetOriginAttributes":
|
||||
this.setOriginAttributes(message.data.originAttributes);
|
||||
break;
|
||||
case "WebNavigation:Reload":
|
||||
this.reload(message.data.flags);
|
||||
break;
|
||||
case "WebNavigation:Stop":
|
||||
this.stop(message.data.flags);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_wrapURIChangeCall(fn) {
|
||||
this._inLoadURI = true;
|
||||
try {
|
||||
fn();
|
||||
} finally {
|
||||
this._inLoadURI = false;
|
||||
WebProgressListener.sendLoadCallResult();
|
||||
}
|
||||
},
|
||||
|
||||
goBack: function() {
|
||||
if (this.webNavigation.canGoBack) {
|
||||
this._wrapURIChangeCall(() => this.webNavigation.goBack());
|
||||
}
|
||||
},
|
||||
|
||||
goForward: function() {
|
||||
if (this.webNavigation.canGoForward) {
|
||||
this._wrapURIChangeCall(() => this.webNavigation.goForward());
|
||||
}
|
||||
},
|
||||
|
||||
gotoIndex: function(index) {
|
||||
this._wrapURIChangeCall(() => this.webNavigation.gotoIndex(index));
|
||||
},
|
||||
|
||||
loadURI: function(uri, flags, referrer, referrerPolicy, postData, headers, baseURI) {
|
||||
if (AppConstants.MOZ_CRASHREPORTER && CrashReporter.enabled) {
|
||||
let annotation = uri;
|
||||
try {
|
||||
let url = Services.io.newURI(uri, null, null);
|
||||
// If the current URI contains a username/password, remove it.
|
||||
url.userPass = "";
|
||||
annotation = url.spec;
|
||||
} catch (ex) { /* Ignore failures to parse and failures
|
||||
on about: URIs. */ }
|
||||
CrashReporter.annotateCrashReport("URL", annotation);
|
||||
}
|
||||
if (referrer)
|
||||
referrer = Services.io.newURI(referrer, null, null);
|
||||
if (postData)
|
||||
postData = makeInputStream(postData);
|
||||
if (headers)
|
||||
headers = makeInputStream(headers);
|
||||
if (baseURI)
|
||||
baseURI = Services.io.newURI(baseURI, null, null);
|
||||
this._wrapURIChangeCall(() => {
|
||||
return this.webNavigation.loadURIWithOptions(uri, flags, referrer, referrerPolicy,
|
||||
postData, headers, baseURI);
|
||||
});
|
||||
},
|
||||
|
||||
setOriginAttributes: function(originAttributes) {
|
||||
if (originAttributes) {
|
||||
this.webNavigation.setOriginAttributesBeforeLoading(originAttributes);
|
||||
}
|
||||
},
|
||||
|
||||
reload: function(flags) {
|
||||
this.webNavigation.reload(flags);
|
||||
},
|
||||
|
||||
stop: function(flags) {
|
||||
this.webNavigation.stop(flags);
|
||||
}
|
||||
};
|
||||
|
||||
WebNavigation.init();
|
||||
|
||||
var SecurityUI = {
|
||||
getSSLStatusAsString: function() {
|
||||
let status = docShell.securityUI.QueryInterface(Ci.nsISSLStatusProvider).SSLStatus;
|
||||
|
||||
if (status) {
|
||||
let helper = Cc["@mozilla.org/network/serialization-helper;1"]
|
||||
.getService(Ci.nsISerializationHelper);
|
||||
|
||||
status.QueryInterface(Ci.nsISerializable);
|
||||
return helper.serializeToString(status);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
var ControllerCommands = {
|
||||
init: function () {
|
||||
addMessageListener("ControllerCommands:Do", this);
|
||||
addMessageListener("ControllerCommands:DoWithParams", this);
|
||||
},
|
||||
|
||||
receiveMessage: function(message) {
|
||||
switch (message.name) {
|
||||
case "ControllerCommands:Do":
|
||||
if (docShell.isCommandEnabled(message.data))
|
||||
docShell.doCommand(message.data);
|
||||
break;
|
||||
|
||||
case "ControllerCommands:DoWithParams":
|
||||
var data = message.data;
|
||||
if (docShell.isCommandEnabled(data.cmd)) {
|
||||
var params = Cc["@mozilla.org/embedcomp/command-params;1"].
|
||||
createInstance(Ci.nsICommandParams);
|
||||
for (var name in data.params) {
|
||||
var value = data.params[name];
|
||||
if (value.type == "long") {
|
||||
params.setLongValue(name, parseInt(value.value));
|
||||
} else {
|
||||
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
}
|
||||
docShell.doCommandWithParams(data.cmd, params);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ControllerCommands.init()
|
||||
|
||||
addEventListener("DOMTitleChanged", function (aEvent) {
|
||||
let document = content.document;
|
||||
switch (aEvent.type) {
|
||||
case "DOMTitleChanged":
|
||||
if (!aEvent.isTrusted || aEvent.target.defaultView != content)
|
||||
return;
|
||||
|
||||
sendAsyncMessage("DOMTitleChanged", { title: document.title });
|
||||
break;
|
||||
}
|
||||
}, false);
|
||||
|
||||
addEventListener("DOMWindowClose", function (aEvent) {
|
||||
if (!aEvent.isTrusted)
|
||||
return;
|
||||
sendAsyncMessage("DOMWindowClose");
|
||||
}, false);
|
||||
|
||||
addEventListener("ImageContentLoaded", function (aEvent) {
|
||||
if (content.document instanceof Ci.nsIImageDocument) {
|
||||
let req = content.document.imageRequest;
|
||||
if (!req.image)
|
||||
return;
|
||||
sendAsyncMessage("ImageDocumentLoaded", { width: req.image.width,
|
||||
height: req.image.height });
|
||||
}
|
||||
}, false);
|
||||
|
||||
const ZoomManager = {
|
||||
get fullZoom() {
|
||||
return this._cache.fullZoom;
|
||||
},
|
||||
|
||||
get textZoom() {
|
||||
return this._cache.textZoom;
|
||||
},
|
||||
|
||||
set fullZoom(value) {
|
||||
this._cache.fullZoom = value;
|
||||
this._markupViewer.fullZoom = value;
|
||||
},
|
||||
|
||||
set textZoom(value) {
|
||||
this._cache.textZoom = value;
|
||||
this._markupViewer.textZoom = value;
|
||||
},
|
||||
|
||||
refreshFullZoom: function() {
|
||||
return this._refreshZoomValue('fullZoom');
|
||||
},
|
||||
|
||||
refreshTextZoom: function() {
|
||||
return this._refreshZoomValue('textZoom');
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieves specified zoom property value from markupViewer and refreshes
|
||||
* cache if needed.
|
||||
* @param valueName Either 'fullZoom' or 'textZoom'.
|
||||
* @returns Returns true if cached value was actually refreshed.
|
||||
* @private
|
||||
*/
|
||||
_refreshZoomValue: function(valueName) {
|
||||
let actualZoomValue = this._markupViewer[valueName];
|
||||
// Round to remove any floating-point error.
|
||||
actualZoomValue = Number(actualZoomValue.toFixed(2));
|
||||
if (actualZoomValue != this._cache[valueName]) {
|
||||
this._cache[valueName] = actualZoomValue;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
get _markupViewer() {
|
||||
return docShell.contentViewer;
|
||||
},
|
||||
|
||||
_cache: {
|
||||
fullZoom: NaN,
|
||||
textZoom: NaN
|
||||
}
|
||||
};
|
||||
|
||||
addMessageListener("FullZoom", function (aMessage) {
|
||||
ZoomManager.fullZoom = aMessage.data.value;
|
||||
});
|
||||
|
||||
addMessageListener("TextZoom", function (aMessage) {
|
||||
ZoomManager.textZoom = aMessage.data.value;
|
||||
});
|
||||
|
||||
addEventListener("FullZoomChange", function () {
|
||||
if (ZoomManager.refreshFullZoom()) {
|
||||
sendAsyncMessage("FullZoomChange", { value: ZoomManager.fullZoom });
|
||||
}
|
||||
}, false);
|
||||
|
||||
addEventListener("TextZoomChange", function (aEvent) {
|
||||
if (ZoomManager.refreshTextZoom()) {
|
||||
sendAsyncMessage("TextZoomChange", { value: ZoomManager.textZoom });
|
||||
}
|
||||
}, false);
|
||||
|
||||
addEventListener("ZoomChangeUsingMouseWheel", function () {
|
||||
sendAsyncMessage("ZoomChangeUsingMouseWheel", {});
|
||||
}, false);
|
||||
|
||||
addMessageListener("UpdateCharacterSet", function (aMessage) {
|
||||
docShell.charset = aMessage.data.value;
|
||||
docShell.gatherCharsetMenuTelemetry();
|
||||
});
|
||||
|
||||
/**
|
||||
* Remote thumbnail request handler for PageThumbs thumbnails.
|
||||
*/
|
||||
addMessageListener("Browser:Thumbnail:Request", function (aMessage) {
|
||||
let snapshot;
|
||||
let args = aMessage.data.additionalArgs;
|
||||
let fullScale = args ? args.fullScale : false;
|
||||
if (fullScale) {
|
||||
snapshot = PageThumbUtils.createSnapshotThumbnail(content, null, args);
|
||||
} else {
|
||||
let snapshotWidth = aMessage.data.canvasWidth;
|
||||
let snapshotHeight = aMessage.data.canvasHeight;
|
||||
snapshot =
|
||||
PageThumbUtils.createCanvas(content, snapshotWidth, snapshotHeight);
|
||||
PageThumbUtils.createSnapshotThumbnail(content, snapshot, args);
|
||||
}
|
||||
|
||||
snapshot.toBlob(function (aBlob) {
|
||||
sendAsyncMessage("Browser:Thumbnail:Response", {
|
||||
thumbnail: aBlob,
|
||||
id: aMessage.data.id
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Remote isSafeForCapture request handler for PageThumbs.
|
||||
*/
|
||||
addMessageListener("Browser:Thumbnail:CheckState", function (aMessage) {
|
||||
let result = PageThumbUtils.shouldStoreContentThumbnail(content, docShell);
|
||||
sendAsyncMessage("Browser:Thumbnail:CheckState:Response", {
|
||||
result: result
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Remote GetOriginalURL request handler for PageThumbs.
|
||||
*/
|
||||
addMessageListener("Browser:Thumbnail:GetOriginalURL", function (aMessage) {
|
||||
let channel = docShell.currentDocumentChannel;
|
||||
let channelError = PageThumbUtils.isChannelErrorResponse(channel);
|
||||
let originalURL;
|
||||
try {
|
||||
originalURL = channel.originalURI.spec;
|
||||
} catch (ex) {}
|
||||
sendAsyncMessage("Browser:Thumbnail:GetOriginalURL:Response", {
|
||||
channelError: channelError,
|
||||
originalURL: originalURL,
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Remote createAboutBlankContentViewer request handler.
|
||||
*/
|
||||
addMessageListener("Browser:CreateAboutBlank", function(aMessage) {
|
||||
if (!content.document || content.document.documentURI != "about:blank") {
|
||||
throw new Error("Can't create a content viewer unless on about:blank");
|
||||
}
|
||||
let principal = aMessage.data;
|
||||
principal = BrowserUtils.principalWithMatchingOA(principal, content.document.nodePrincipal);
|
||||
docShell.createAboutBlankContentViewer(principal);
|
||||
});
|
||||
|
||||
// The AddonsChild needs to be rooted so that it stays alive as long as
|
||||
// the tab.
|
||||
var AddonsChild = RemoteAddonsChild.init(this);
|
||||
if (AddonsChild) {
|
||||
addEventListener("unload", () => {
|
||||
RemoteAddonsChild.uninit(AddonsChild);
|
||||
});
|
||||
}
|
||||
|
||||
addMessageListener("NetworkPrioritizer:AdjustPriority", (msg) => {
|
||||
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let loadGroup = webNav.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup.QueryInterface(Ci.nsISupportsPriority);
|
||||
loadGroup.adjustPriority(msg.data.adjustment);
|
||||
});
|
||||
|
||||
addMessageListener("NetworkPrioritizer:SetPriority", (msg) => {
|
||||
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
|
||||
let loadGroup = webNav.QueryInterface(Ci.nsIDocumentLoader)
|
||||
.loadGroup.QueryInterface(Ci.nsISupportsPriority);
|
||||
loadGroup.priority = msg.data.priority;
|
||||
});
|
||||
|
||||
addMessageListener("InPermitUnload", msg => {
|
||||
let inPermitUnload = docShell.contentViewer && docShell.contentViewer.inPermitUnload;
|
||||
sendAsyncMessage("InPermitUnload", {id: msg.data.id, inPermitUnload});
|
||||
});
|
||||
|
||||
addMessageListener("PermitUnload", msg => {
|
||||
sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "start"});
|
||||
|
||||
let permitUnload = true;
|
||||
if (docShell && docShell.contentViewer) {
|
||||
permitUnload = docShell.contentViewer.permitUnload();
|
||||
}
|
||||
|
||||
sendAsyncMessage("PermitUnload", {id: msg.data.id, kind: "end", permitUnload});
|
||||
});
|
||||
|
||||
// We may not get any responses to Browser:Init if the browser element
|
||||
// is torn down too quickly.
|
||||
var outerWindowID = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
.outerWindowID;
|
||||
sendAsyncMessage("Browser:Init", {outerWindowID: outerWindowID});
|
||||
1762
toolkit/content/browser-content.js
Normal file
1762
toolkit/content/browser-content.js
Normal file
File diff suppressed because it is too large
Load diff
66
toolkit/content/buildconfig.html
Normal file
66
toolkit/content/buildconfig.html
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE html>
|
||||
# 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/.
|
||||
#
|
||||
#filter substitution
|
||||
#include @TOPOBJDIR@/source-repo.h
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width; user-scalable=false;">
|
||||
<title>about:buildconfig</title>
|
||||
<link rel="stylesheet" href="chrome://global/skin/about.css" type="text/css">
|
||||
<style type="text/css">
|
||||
th { text-align: start; }
|
||||
h2 { margin-top: 1.5em; }
|
||||
th, td { vertical-align: top; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="aboutPageWideContainer">
|
||||
<h1>about:buildconfig</h1>
|
||||
#ifdef MOZ_SOURCE_URL
|
||||
<h2>Source</h2>
|
||||
<p>Built from <a href="@MOZ_SOURCE_URL@">@MOZ_SOURCE_URL@</a></p>
|
||||
#endif
|
||||
<h2>Build platform</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>target</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@target@</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Build tools</h2>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Compiler</th>
|
||||
<th>Version</th>
|
||||
<th>Compiler flags</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@CC@</td>
|
||||
<td>@CC_VERSION@</td>
|
||||
<td>@CFLAGS@</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>@CXX@</td>
|
||||
<td>@CC_VERSION@</td>
|
||||
#ifndef BUILD_FASTER
|
||||
<td>@CXXFLAGS@ @CPPFLAGS@</td>
|
||||
#endif
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h2>Configure options</h2>
|
||||
<p>@MOZ_CONFIGURE_OPTIONS@</p>
|
||||
#ifdef ANDROID
|
||||
<h2>Package name</h2>
|
||||
<p>@ANDROID_PACKAGE_NAME@</p>
|
||||
#endif
|
||||
</body>
|
||||
</html>
|
||||
1330
toolkit/content/contentAreaUtils.js
Normal file
1330
toolkit/content/contentAreaUtils.js
Normal file
File diff suppressed because it is too large
Load diff
39
toolkit/content/customizeToolbar.css
Normal file
39
toolkit/content/customizeToolbar.css
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
/* 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/. */
|
||||
|
||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
|
||||
@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
|
||||
|
||||
#palette-box {
|
||||
overflow: auto;
|
||||
display: block;
|
||||
min-height: 3em;
|
||||
}
|
||||
|
||||
#palette-box > toolbarpaletteitem {
|
||||
width: 110px;
|
||||
height: 94px;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.toolbarpaletteitem-box {
|
||||
-moz-box-pack: center;
|
||||
-moz-box-flex: 1;
|
||||
width: 110px;
|
||||
max-width: 110px;
|
||||
}
|
||||
|
||||
toolbarpaletteitem > label {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#main-box > box {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Hide the toolbarbutton label because we replicate it on the wrapper */
|
||||
.toolbarbutton-text {
|
||||
display: none;
|
||||
}
|
||||
839
toolkit/content/customizeToolbar.js
Normal file
839
toolkit/content/customizeToolbar.js
Normal file
|
|
@ -0,0 +1,839 @@
|
|||
/* 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/. */
|
||||
|
||||
var gToolboxDocument = null;
|
||||
var gToolbox = null;
|
||||
var gCurrentDragOverItem = null;
|
||||
var gToolboxChanged = false;
|
||||
var gToolboxSheet = false;
|
||||
var gPaletteBox = null;
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
function onLoad()
|
||||
{
|
||||
if ("arguments" in window && window.arguments[0]) {
|
||||
InitWithToolbox(window.arguments[0]);
|
||||
repositionDialog(window);
|
||||
}
|
||||
else if (window.frameElement &&
|
||||
"toolbox" in window.frameElement) {
|
||||
gToolboxSheet = true;
|
||||
InitWithToolbox(window.frameElement.toolbox);
|
||||
repositionDialog(window.frameElement.panel);
|
||||
}
|
||||
}
|
||||
|
||||
function InitWithToolbox(aToolbox)
|
||||
{
|
||||
gToolbox = aToolbox;
|
||||
dispatchCustomizationEvent("beforecustomization");
|
||||
gToolboxDocument = gToolbox.ownerDocument;
|
||||
gToolbox.customizing = true;
|
||||
forEachCustomizableToolbar(function (toolbar) {
|
||||
toolbar.setAttribute("customizing", "true");
|
||||
});
|
||||
gPaletteBox = document.getElementById("palette-box");
|
||||
|
||||
var elts = getRootElements();
|
||||
for (let i=0; i < elts.length; i++) {
|
||||
elts[i].addEventListener("dragstart", onToolbarDragStart, true);
|
||||
elts[i].addEventListener("dragover", onToolbarDragOver, true);
|
||||
elts[i].addEventListener("dragexit", onToolbarDragExit, true);
|
||||
elts[i].addEventListener("drop", onToolbarDrop, true);
|
||||
}
|
||||
|
||||
initDialog();
|
||||
}
|
||||
|
||||
function onClose()
|
||||
{
|
||||
if (!gToolboxSheet)
|
||||
window.close();
|
||||
else
|
||||
finishToolbarCustomization();
|
||||
}
|
||||
|
||||
function onUnload()
|
||||
{
|
||||
if (!gToolboxSheet)
|
||||
finishToolbarCustomization();
|
||||
}
|
||||
|
||||
function finishToolbarCustomization()
|
||||
{
|
||||
removeToolboxListeners();
|
||||
unwrapToolbarItems();
|
||||
persistCurrentSets();
|
||||
gToolbox.customizing = false;
|
||||
forEachCustomizableToolbar(function (toolbar) {
|
||||
toolbar.removeAttribute("customizing");
|
||||
});
|
||||
|
||||
notifyParentComplete();
|
||||
}
|
||||
|
||||
function initDialog()
|
||||
{
|
||||
if (!gToolbox.toolbarset) {
|
||||
document.getElementById("newtoolbar").hidden = true;
|
||||
}
|
||||
|
||||
var mode = gToolbox.getAttribute("mode");
|
||||
document.getElementById("modelist").value = mode;
|
||||
var smallIconsCheckbox = document.getElementById("smallicons");
|
||||
smallIconsCheckbox.checked = gToolbox.getAttribute("iconsize") == "small";
|
||||
if (mode == "text")
|
||||
smallIconsCheckbox.disabled = true;
|
||||
|
||||
// Build up the palette of other items.
|
||||
buildPalette();
|
||||
|
||||
// Wrap all the items on the toolbar in toolbarpaletteitems.
|
||||
wrapToolbarItems();
|
||||
}
|
||||
|
||||
function repositionDialog(aWindow)
|
||||
{
|
||||
// Position the dialog touching the bottom of the toolbox and centered with
|
||||
// it.
|
||||
if (!aWindow)
|
||||
return;
|
||||
|
||||
var width;
|
||||
if (aWindow != window)
|
||||
width = aWindow.getBoundingClientRect().width;
|
||||
else if (document.documentElement.hasAttribute("width"))
|
||||
width = document.documentElement.getAttribute("width");
|
||||
else
|
||||
width = parseInt(document.documentElement.style.width);
|
||||
var screenX = gToolbox.boxObject.screenX
|
||||
+ ((gToolbox.boxObject.width - width) / 2);
|
||||
var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height;
|
||||
|
||||
aWindow.moveTo(screenX, screenY);
|
||||
}
|
||||
|
||||
function removeToolboxListeners()
|
||||
{
|
||||
var elts = getRootElements();
|
||||
for (let i=0; i < elts.length; i++) {
|
||||
elts[i].removeEventListener("dragstart", onToolbarDragStart, true);
|
||||
elts[i].removeEventListener("dragover", onToolbarDragOver, true);
|
||||
elts[i].removeEventListener("dragexit", onToolbarDragExit, true);
|
||||
elts[i].removeEventListener("drop", onToolbarDrop, true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke a callback on the toolbox to notify it that the dialog is done
|
||||
* and going away.
|
||||
*/
|
||||
function notifyParentComplete()
|
||||
{
|
||||
if ("customizeDone" in gToolbox)
|
||||
gToolbox.customizeDone(gToolboxChanged);
|
||||
dispatchCustomizationEvent("aftercustomization");
|
||||
}
|
||||
|
||||
function toolboxChanged(aType)
|
||||
{
|
||||
gToolboxChanged = true;
|
||||
if ("customizeChange" in gToolbox)
|
||||
gToolbox.customizeChange(aType);
|
||||
dispatchCustomizationEvent("customizationchange");
|
||||
}
|
||||
|
||||
function dispatchCustomizationEvent(aEventName) {
|
||||
var evt = document.createEvent("Events");
|
||||
evt.initEvent(aEventName, true, true);
|
||||
gToolbox.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist the current set of buttons in all customizable toolbars to
|
||||
* localstore.
|
||||
*/
|
||||
function persistCurrentSets()
|
||||
{
|
||||
if (!gToolboxChanged || gToolboxDocument.defaultView.closed)
|
||||
return;
|
||||
|
||||
var customCount = 0;
|
||||
forEachCustomizableToolbar(function (toolbar) {
|
||||
// Calculate currentset and store it in the attribute.
|
||||
var currentSet = toolbar.currentSet;
|
||||
toolbar.setAttribute("currentset", currentSet);
|
||||
|
||||
var customIndex = toolbar.hasAttribute("customindex");
|
||||
if (customIndex) {
|
||||
if (!toolbar.hasChildNodes()) {
|
||||
// Remove custom toolbars whose contents have been removed.
|
||||
gToolbox.removeChild(toolbar);
|
||||
} else if (gToolbox.toolbarset) {
|
||||
// Persist custom toolbar info on the <toolbarset/>
|
||||
gToolbox.toolbarset.setAttribute("toolbar"+(++customCount),
|
||||
toolbar.toolbarName + ":" + currentSet);
|
||||
gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
|
||||
}
|
||||
}
|
||||
|
||||
if (!customIndex) {
|
||||
// Persist the currentset attribute directly on hardcoded toolbars.
|
||||
gToolboxDocument.persist(toolbar.id, "currentset");
|
||||
}
|
||||
});
|
||||
|
||||
// Remove toolbarX attributes for removed toolbars.
|
||||
while (gToolbox.toolbarset && gToolbox.toolbarset.hasAttribute("toolbar"+(++customCount))) {
|
||||
gToolbox.toolbarset.removeAttribute("toolbar"+customCount);
|
||||
gToolboxDocument.persist(gToolbox.toolbarset.id, "toolbar"+customCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps all items in all customizable toolbars in a toolbox.
|
||||
*/
|
||||
function wrapToolbarItems()
|
||||
{
|
||||
forEachCustomizableToolbar(function (toolbar) {
|
||||
Array.forEach(toolbar.childNodes, function (item) {
|
||||
if (AppConstants.platform == "macosx") {
|
||||
if (item.firstChild && item.firstChild.localName == "menubar")
|
||||
return;
|
||||
}
|
||||
if (isToolbarItem(item)) {
|
||||
let wrapper = wrapToolbarItem(item);
|
||||
cleanupItemForToolbar(item, wrapper);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getRootElements()
|
||||
{
|
||||
return [gToolbox].concat(gToolbox.externalToolbars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps all items in all customizable toolbars in a toolbox.
|
||||
*/
|
||||
function unwrapToolbarItems()
|
||||
{
|
||||
let elts = getRootElements();
|
||||
for (let i=0; i < elts.length; i++) {
|
||||
let paletteItems = elts[i].getElementsByTagName("toolbarpaletteitem");
|
||||
let paletteItem;
|
||||
while ((paletteItem = paletteItems.item(0)) != null) {
|
||||
let toolbarItem = paletteItem.firstChild;
|
||||
restoreItemForToolbar(toolbarItem, paletteItem);
|
||||
paletteItem.parentNode.replaceChild(toolbarItem, paletteItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a wrapper that can be used to contain a toolbaritem and prevent
|
||||
* it from receiving UI events.
|
||||
*/
|
||||
function createWrapper(aId, aDocument)
|
||||
{
|
||||
var wrapper = aDocument.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"toolbarpaletteitem");
|
||||
|
||||
wrapper.id = "wrapper-"+aId;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an item that has been cloned from a template and adds
|
||||
* it to the end of the palette.
|
||||
*/
|
||||
function wrapPaletteItem(aPaletteItem)
|
||||
{
|
||||
var wrapper = createWrapper(aPaletteItem.id, document);
|
||||
|
||||
wrapper.appendChild(aPaletteItem);
|
||||
|
||||
// XXX We need to call this AFTER the palette item has been appended
|
||||
// to the wrapper or else we crash dropping certain buttons on the
|
||||
// palette due to removal of the command and disabled attributes - JRH
|
||||
cleanUpItemForPalette(aPaletteItem, wrapper);
|
||||
|
||||
gPaletteBox.appendChild(wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps an item that is currently on a toolbar and replaces the item
|
||||
* with the wrapper. This is not used when dropping items from the palette,
|
||||
* only when first starting the dialog and wrapping everything on the toolbars.
|
||||
*/
|
||||
function wrapToolbarItem(aToolbarItem)
|
||||
{
|
||||
var wrapper = createWrapper(aToolbarItem.id, gToolboxDocument);
|
||||
|
||||
wrapper.flex = aToolbarItem.flex;
|
||||
|
||||
aToolbarItem.parentNode.replaceChild(wrapper, aToolbarItem);
|
||||
|
||||
wrapper.appendChild(aToolbarItem);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of ids for the current set of items on each toolbar.
|
||||
*/
|
||||
function getCurrentItemIds()
|
||||
{
|
||||
var currentItems = {};
|
||||
forEachCustomizableToolbar(function (toolbar) {
|
||||
var child = toolbar.firstChild;
|
||||
while (child) {
|
||||
if (isToolbarItem(child))
|
||||
currentItems[child.id] = 1;
|
||||
child = child.nextSibling;
|
||||
}
|
||||
});
|
||||
return currentItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the palette of draggable items that are not yet in a toolbar.
|
||||
*/
|
||||
function buildPalette()
|
||||
{
|
||||
// Empty the palette first.
|
||||
while (gPaletteBox.lastChild)
|
||||
gPaletteBox.removeChild(gPaletteBox.lastChild);
|
||||
|
||||
// Add the toolbar separator item.
|
||||
var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"toolbarseparator");
|
||||
templateNode.id = "separator";
|
||||
wrapPaletteItem(templateNode);
|
||||
|
||||
// Add the toolbar spring item.
|
||||
templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"toolbarspring");
|
||||
templateNode.id = "spring";
|
||||
templateNode.flex = 1;
|
||||
wrapPaletteItem(templateNode);
|
||||
|
||||
// Add the toolbar spacer item.
|
||||
templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
|
||||
"toolbarspacer");
|
||||
templateNode.id = "spacer";
|
||||
templateNode.flex = 1;
|
||||
wrapPaletteItem(templateNode);
|
||||
|
||||
var currentItems = getCurrentItemIds();
|
||||
templateNode = gToolbox.palette.firstChild;
|
||||
while (templateNode) {
|
||||
// Check if the item is already in a toolbar before adding it to the palette.
|
||||
if (!(templateNode.id in currentItems)) {
|
||||
var paletteItem = document.importNode(templateNode, true);
|
||||
wrapPaletteItem(paletteItem);
|
||||
}
|
||||
|
||||
templateNode = templateNode.nextSibling;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that an item that has been cloned from a template
|
||||
* is stripped of any attributes that may adversely affect its
|
||||
* appearance in the palette.
|
||||
*/
|
||||
function cleanUpItemForPalette(aItem, aWrapper)
|
||||
{
|
||||
aWrapper.setAttribute("place", "palette");
|
||||
setWrapperType(aItem, aWrapper);
|
||||
|
||||
if (aItem.hasAttribute("title"))
|
||||
aWrapper.setAttribute("title", aItem.getAttribute("title"));
|
||||
else if (aItem.hasAttribute("label"))
|
||||
aWrapper.setAttribute("title", aItem.getAttribute("label"));
|
||||
else if (isSpecialItem(aItem)) {
|
||||
var stringBundle = document.getElementById("stringBundle");
|
||||
// Remove the common "toolbar" prefix to generate the string name.
|
||||
var title = stringBundle.getString(aItem.localName.slice(7) + "Title");
|
||||
aWrapper.setAttribute("title", title);
|
||||
}
|
||||
aWrapper.setAttribute("tooltiptext", aWrapper.getAttribute("title"));
|
||||
|
||||
// Remove attributes that screw up our appearance.
|
||||
aItem.removeAttribute("command");
|
||||
aItem.removeAttribute("observes");
|
||||
aItem.removeAttribute("type");
|
||||
aItem.removeAttribute("width");
|
||||
|
||||
Array.forEach(aWrapper.querySelectorAll("[disabled]"), function(aNode) {
|
||||
aNode.removeAttribute("disabled");
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that an item that has been cloned from a template
|
||||
* is stripped of all properties that may adversely affect its
|
||||
* appearance in the toolbar. Store critical properties on the
|
||||
* wrapper so they can be put back on the item when we're done.
|
||||
*/
|
||||
function cleanupItemForToolbar(aItem, aWrapper)
|
||||
{
|
||||
setWrapperType(aItem, aWrapper);
|
||||
aWrapper.setAttribute("place", "toolbar");
|
||||
|
||||
if (aItem.hasAttribute("command")) {
|
||||
aWrapper.setAttribute("itemcommand", aItem.getAttribute("command"));
|
||||
aItem.removeAttribute("command");
|
||||
}
|
||||
|
||||
if (aItem.checked) {
|
||||
aWrapper.setAttribute("itemchecked", "true");
|
||||
aItem.checked = false;
|
||||
}
|
||||
|
||||
if (aItem.disabled) {
|
||||
aWrapper.setAttribute("itemdisabled", "true");
|
||||
aItem.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore all the properties that we stripped off above.
|
||||
*/
|
||||
function restoreItemForToolbar(aItem, aWrapper)
|
||||
{
|
||||
if (aWrapper.hasAttribute("itemdisabled"))
|
||||
aItem.disabled = true;
|
||||
|
||||
if (aWrapper.hasAttribute("itemchecked"))
|
||||
aItem.checked = true;
|
||||
|
||||
if (aWrapper.hasAttribute("itemcommand")) {
|
||||
let commandID = aWrapper.getAttribute("itemcommand");
|
||||
aItem.setAttribute("command", commandID);
|
||||
|
||||
// XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
|
||||
let command = gToolboxDocument.getElementById(commandID);
|
||||
if (command && command.hasAttribute("disabled"))
|
||||
aItem.setAttribute("disabled", command.getAttribute("disabled"));
|
||||
}
|
||||
}
|
||||
|
||||
function setWrapperType(aItem, aWrapper)
|
||||
{
|
||||
if (aItem.localName == "toolbarseparator") {
|
||||
aWrapper.setAttribute("type", "separator");
|
||||
} else if (aItem.localName == "toolbarspring") {
|
||||
aWrapper.setAttribute("type", "spring");
|
||||
} else if (aItem.localName == "toolbarspacer") {
|
||||
aWrapper.setAttribute("type", "spacer");
|
||||
} else if (aItem.localName == "toolbaritem" && aItem.firstChild) {
|
||||
aWrapper.setAttribute("type", aItem.firstChild.localName);
|
||||
}
|
||||
}
|
||||
|
||||
function setDragActive(aItem, aValue)
|
||||
{
|
||||
var node = aItem;
|
||||
var direction = window.getComputedStyle(aItem, null).direction;
|
||||
var value = direction == "ltr"? "left" : "right";
|
||||
if (aItem.localName == "toolbar") {
|
||||
node = aItem.lastChild;
|
||||
value = direction == "ltr"? "right" : "left";
|
||||
}
|
||||
|
||||
if (!node)
|
||||
return;
|
||||
|
||||
if (aValue) {
|
||||
if (!node.hasAttribute("dragover"))
|
||||
node.setAttribute("dragover", value);
|
||||
} else {
|
||||
node.removeAttribute("dragover");
|
||||
}
|
||||
}
|
||||
|
||||
function addNewToolbar()
|
||||
{
|
||||
var promptService = Services.prompt;
|
||||
var stringBundle = document.getElementById("stringBundle");
|
||||
var message = stringBundle.getString("enterToolbarName");
|
||||
var title = stringBundle.getString("enterToolbarTitle");
|
||||
|
||||
var name = {};
|
||||
|
||||
// Quitting from the toolbar dialog while the new toolbar prompt is up
|
||||
// can cause things to become unresponsive on the Mac. Until dialog modality
|
||||
// is fixed (395465), disable the "Done" button explicitly.
|
||||
var doneButton = document.getElementById("donebutton");
|
||||
doneButton.disabled = true;
|
||||
|
||||
while (true) {
|
||||
|
||||
if (!promptService.prompt(window, title, message, name, null, {})) {
|
||||
doneButton.disabled = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!name.value) {
|
||||
message = stringBundle.getFormattedString("enterToolbarBlank", [name.value]);
|
||||
continue;
|
||||
}
|
||||
|
||||
var dupeFound = false;
|
||||
|
||||
// Check for an existing toolbar with the same display name
|
||||
for (let i = 0; i < gToolbox.childNodes.length; ++i) {
|
||||
var toolbar = gToolbox.childNodes[i];
|
||||
var toolbarName = toolbar.getAttribute("toolbarname");
|
||||
|
||||
if (toolbarName == name.value &&
|
||||
toolbar.getAttribute("type") != "menubar" &&
|
||||
toolbar.nodeName == 'toolbar') {
|
||||
dupeFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dupeFound)
|
||||
break;
|
||||
|
||||
message = stringBundle.getFormattedString("enterToolbarDup", [name.value]);
|
||||
}
|
||||
|
||||
gToolbox.appendCustomToolbar(name.value, "");
|
||||
|
||||
toolboxChanged();
|
||||
|
||||
doneButton.disabled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the default set of buttons to fixed toolbars,
|
||||
* remove all custom toolbars, and rebuild the palette.
|
||||
*/
|
||||
function restoreDefaultSet()
|
||||
{
|
||||
// Unwrap the items on the toolbar.
|
||||
unwrapToolbarItems();
|
||||
|
||||
// Remove all of the customized toolbars.
|
||||
var child = gToolbox.lastChild;
|
||||
while (child) {
|
||||
if (child.hasAttribute("customindex")) {
|
||||
var thisChild = child;
|
||||
child = child.previousSibling;
|
||||
thisChild.currentSet = "__empty";
|
||||
gToolbox.removeChild(thisChild);
|
||||
} else {
|
||||
child = child.previousSibling;
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the defaultset for fixed toolbars.
|
||||
forEachCustomizableToolbar(function (toolbar) {
|
||||
var defaultSet = toolbar.getAttribute("defaultset");
|
||||
if (defaultSet)
|
||||
toolbar.currentSet = defaultSet;
|
||||
});
|
||||
|
||||
// Restore the default icon size and mode.
|
||||
document.getElementById("smallicons").checked = (updateIconSize() == "small");
|
||||
document.getElementById("modelist").value = updateToolbarMode();
|
||||
|
||||
// Now rebuild the palette.
|
||||
buildPalette();
|
||||
|
||||
// Now re-wrap the items on the toolbar.
|
||||
wrapToolbarItems();
|
||||
|
||||
toolboxChanged("reset");
|
||||
}
|
||||
|
||||
function updateIconSize(aSize) {
|
||||
return updateToolboxProperty("iconsize", aSize, "large");
|
||||
}
|
||||
|
||||
function updateToolbarMode(aModeValue) {
|
||||
var mode = updateToolboxProperty("mode", aModeValue, "icons");
|
||||
|
||||
var iconSizeCheckbox = document.getElementById("smallicons");
|
||||
iconSizeCheckbox.disabled = mode == "text";
|
||||
|
||||
return mode;
|
||||
}
|
||||
|
||||
function updateToolboxProperty(aProp, aValue, aToolkitDefault) {
|
||||
var toolboxDefault = gToolbox.getAttribute("default" + aProp) ||
|
||||
aToolkitDefault;
|
||||
|
||||
gToolbox.setAttribute(aProp, aValue || toolboxDefault);
|
||||
gToolboxDocument.persist(gToolbox.id, aProp);
|
||||
|
||||
forEachCustomizableToolbar(function (toolbar) {
|
||||
var toolbarDefault = toolbar.getAttribute("default" + aProp) ||
|
||||
toolboxDefault;
|
||||
if (toolbar.getAttribute("lock" + aProp) == "true" &&
|
||||
toolbar.getAttribute(aProp) == toolbarDefault)
|
||||
return;
|
||||
|
||||
toolbar.setAttribute(aProp, aValue || toolbarDefault);
|
||||
gToolboxDocument.persist(toolbar.id, aProp);
|
||||
});
|
||||
|
||||
toolboxChanged(aProp);
|
||||
|
||||
return aValue || toolboxDefault;
|
||||
}
|
||||
|
||||
function forEachCustomizableToolbar(callback) {
|
||||
Array.filter(gToolbox.childNodes, isCustomizableToolbar).forEach(callback);
|
||||
Array.filter(gToolbox.externalToolbars, isCustomizableToolbar).forEach(callback);
|
||||
}
|
||||
|
||||
function isCustomizableToolbar(aElt)
|
||||
{
|
||||
return aElt.localName == "toolbar" &&
|
||||
aElt.getAttribute("customizable") == "true";
|
||||
}
|
||||
|
||||
function isSpecialItem(aElt)
|
||||
{
|
||||
return aElt.localName == "toolbarseparator" ||
|
||||
aElt.localName == "toolbarspring" ||
|
||||
aElt.localName == "toolbarspacer";
|
||||
}
|
||||
|
||||
function isToolbarItem(aElt)
|
||||
{
|
||||
return aElt.localName == "toolbarbutton" ||
|
||||
aElt.localName == "toolbaritem" ||
|
||||
aElt.localName == "toolbarseparator" ||
|
||||
aElt.localName == "toolbarspring" ||
|
||||
aElt.localName == "toolbarspacer";
|
||||
}
|
||||
|
||||
// Drag and Drop observers
|
||||
|
||||
function onToolbarDragExit(aEvent)
|
||||
{
|
||||
if (isUnwantedDragEvent(aEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (gCurrentDragOverItem)
|
||||
setDragActive(gCurrentDragOverItem, false);
|
||||
}
|
||||
|
||||
function onToolbarDragStart(aEvent)
|
||||
{
|
||||
var item = aEvent.target;
|
||||
while (item && item.localName != "toolbarpaletteitem") {
|
||||
if (item.localName == "toolbar")
|
||||
return;
|
||||
item = item.parentNode;
|
||||
}
|
||||
|
||||
item.setAttribute("dragactive", "true");
|
||||
|
||||
var dt = aEvent.dataTransfer;
|
||||
var documentId = gToolboxDocument.documentElement.id;
|
||||
dt.setData("text/toolbarwrapper-id/" + documentId, item.firstChild.id);
|
||||
dt.effectAllowed = "move";
|
||||
}
|
||||
|
||||
function onToolbarDragOver(aEvent)
|
||||
{
|
||||
if (isUnwantedDragEvent(aEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
var documentId = gToolboxDocument.documentElement.id;
|
||||
if (!aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase()))
|
||||
return;
|
||||
|
||||
var toolbar = aEvent.target;
|
||||
var dropTarget = aEvent.target;
|
||||
while (toolbar && toolbar.localName != "toolbar") {
|
||||
dropTarget = toolbar;
|
||||
toolbar = toolbar.parentNode;
|
||||
}
|
||||
|
||||
// Make sure we are dragging over a customizable toolbar.
|
||||
if (!toolbar || !isCustomizableToolbar(toolbar)) {
|
||||
gCurrentDragOverItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
var previousDragItem = gCurrentDragOverItem;
|
||||
|
||||
if (dropTarget.localName == "toolbar") {
|
||||
gCurrentDragOverItem = dropTarget;
|
||||
} else {
|
||||
gCurrentDragOverItem = null;
|
||||
|
||||
var direction = window.getComputedStyle(dropTarget.parentNode, null).direction;
|
||||
var dropTargetCenter = dropTarget.boxObject.x + (dropTarget.boxObject.width / 2);
|
||||
var dragAfter;
|
||||
if (direction == "ltr")
|
||||
dragAfter = aEvent.clientX > dropTargetCenter;
|
||||
else
|
||||
dragAfter = aEvent.clientX < dropTargetCenter;
|
||||
|
||||
if (dragAfter) {
|
||||
gCurrentDragOverItem = dropTarget.nextSibling;
|
||||
if (!gCurrentDragOverItem)
|
||||
gCurrentDragOverItem = toolbar;
|
||||
} else
|
||||
gCurrentDragOverItem = dropTarget;
|
||||
}
|
||||
|
||||
if (previousDragItem && gCurrentDragOverItem != previousDragItem) {
|
||||
setDragActive(previousDragItem, false);
|
||||
}
|
||||
|
||||
setDragActive(gCurrentDragOverItem, true);
|
||||
|
||||
aEvent.preventDefault();
|
||||
aEvent.stopPropagation();
|
||||
}
|
||||
|
||||
function onToolbarDrop(aEvent)
|
||||
{
|
||||
if (isUnwantedDragEvent(aEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gCurrentDragOverItem)
|
||||
return;
|
||||
|
||||
setDragActive(gCurrentDragOverItem, false);
|
||||
|
||||
var documentId = gToolboxDocument.documentElement.id;
|
||||
var draggedItemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
|
||||
if (gCurrentDragOverItem.id == draggedItemId)
|
||||
return;
|
||||
|
||||
var toolbar = aEvent.target;
|
||||
while (toolbar.localName != "toolbar")
|
||||
toolbar = toolbar.parentNode;
|
||||
|
||||
var draggedPaletteWrapper = document.getElementById("wrapper-"+draggedItemId);
|
||||
if (!draggedPaletteWrapper) {
|
||||
// The wrapper has been dragged from the toolbar.
|
||||
// Get the wrapper from the toolbar document and make sure that
|
||||
// it isn't being dropped on itself.
|
||||
let wrapper = gToolboxDocument.getElementById("wrapper-"+draggedItemId);
|
||||
if (wrapper == gCurrentDragOverItem)
|
||||
return;
|
||||
|
||||
// Don't allow non-removable kids (e.g., the menubar) to move.
|
||||
if (wrapper.firstChild.getAttribute("removable") != "true")
|
||||
return;
|
||||
|
||||
// Remove the item from its place in the toolbar.
|
||||
wrapper.parentNode.removeChild(wrapper);
|
||||
|
||||
// Determine which toolbar we are dropping on.
|
||||
var dropToolbar = null;
|
||||
if (gCurrentDragOverItem.localName == "toolbar")
|
||||
dropToolbar = gCurrentDragOverItem;
|
||||
else
|
||||
dropToolbar = gCurrentDragOverItem.parentNode;
|
||||
|
||||
// Insert the item into the toolbar.
|
||||
if (gCurrentDragOverItem != dropToolbar)
|
||||
dropToolbar.insertBefore(wrapper, gCurrentDragOverItem);
|
||||
else
|
||||
dropToolbar.appendChild(wrapper);
|
||||
} else {
|
||||
// The item has been dragged from the palette
|
||||
|
||||
// Create a new wrapper for the item. We don't know the id yet.
|
||||
let wrapper = createWrapper("", gToolboxDocument);
|
||||
|
||||
// Ask the toolbar to clone the item's template, place it inside the wrapper, and insert it in the toolbar.
|
||||
var newItem = toolbar.insertItem(draggedItemId, gCurrentDragOverItem == toolbar ? null : gCurrentDragOverItem, wrapper);
|
||||
|
||||
// Prepare the item and wrapper to look good on the toolbar.
|
||||
cleanupItemForToolbar(newItem, wrapper);
|
||||
wrapper.id = "wrapper-"+newItem.id;
|
||||
wrapper.flex = newItem.flex;
|
||||
|
||||
// Remove the wrapper from the palette.
|
||||
if (draggedItemId != "separator" &&
|
||||
draggedItemId != "spring" &&
|
||||
draggedItemId != "spacer")
|
||||
gPaletteBox.removeChild(draggedPaletteWrapper);
|
||||
}
|
||||
|
||||
gCurrentDragOverItem = null;
|
||||
|
||||
toolboxChanged();
|
||||
}
|
||||
|
||||
function onPaletteDragOver(aEvent)
|
||||
{
|
||||
if (isUnwantedDragEvent(aEvent)) {
|
||||
return;
|
||||
}
|
||||
var documentId = gToolboxDocument.documentElement.id;
|
||||
if (aEvent.dataTransfer.types.includes("text/toolbarwrapper-id/" + documentId.toLowerCase()))
|
||||
aEvent.preventDefault();
|
||||
}
|
||||
|
||||
function onPaletteDrop(aEvent)
|
||||
{
|
||||
if (isUnwantedDragEvent(aEvent)) {
|
||||
return;
|
||||
}
|
||||
var documentId = gToolboxDocument.documentElement.id;
|
||||
var itemId = aEvent.dataTransfer.getData("text/toolbarwrapper-id/" + documentId);
|
||||
|
||||
var wrapper = gToolboxDocument.getElementById("wrapper-"+itemId);
|
||||
if (wrapper) {
|
||||
// Don't allow non-removable kids (e.g., the menubar) to move.
|
||||
if (wrapper.firstChild.getAttribute("removable") != "true")
|
||||
return;
|
||||
|
||||
var wrapperType = wrapper.getAttribute("type");
|
||||
if (wrapperType != "separator" &&
|
||||
wrapperType != "spacer" &&
|
||||
wrapperType != "spring") {
|
||||
restoreItemForToolbar(wrapper.firstChild, wrapper);
|
||||
wrapPaletteItem(document.importNode(wrapper.firstChild, true));
|
||||
gToolbox.palette.appendChild(wrapper.firstChild);
|
||||
}
|
||||
|
||||
// The item was dragged out of the toolbar.
|
||||
wrapper.parentNode.removeChild(wrapper);
|
||||
}
|
||||
|
||||
toolboxChanged();
|
||||
}
|
||||
|
||||
|
||||
function isUnwantedDragEvent(aEvent) {
|
||||
try {
|
||||
if (Services.prefs.getBoolPref("toolkit.customization.unsafe_drag_events")) {
|
||||
return false;
|
||||
}
|
||||
} catch (ex) {}
|
||||
|
||||
/* Discard drag events that originated from a separate window to
|
||||
prevent content->chrome privilege escalations. */
|
||||
let mozSourceNode = aEvent.dataTransfer.mozSourceNode;
|
||||
// mozSourceNode is null in the dragStart event handler or if
|
||||
// the drag event originated in an external application.
|
||||
if (!mozSourceNode) {
|
||||
return true;
|
||||
}
|
||||
let sourceWindow = mozSourceNode.ownerDocument.defaultView;
|
||||
return sourceWindow != window && sourceWindow != gToolboxDocument.defaultView;
|
||||
}
|
||||
|
||||
67
toolkit/content/customizeToolbar.xul
Normal file
67
toolkit/content/customizeToolbar.xul
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE dialog [
|
||||
<!ENTITY % customizeToolbarDTD SYSTEM "chrome://global/locale/customizeToolbar.dtd">
|
||||
%customizeToolbarDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/content/customizeToolbar.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/customizeToolbar.css" type="text/css"?>
|
||||
|
||||
<window id="CustomizeToolbarWindow"
|
||||
title="&dialog.title;"
|
||||
onload="onLoad();"
|
||||
onunload="onUnload();"
|
||||
style="&dialog.dimensions;"
|
||||
persist="width height"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/customizeToolbar.js"/>
|
||||
|
||||
<stringbundle id="stringBundle" src="chrome://global/locale/customizeToolbar.properties"/>
|
||||
|
||||
<keyset id="CustomizeToolbarKeyset">
|
||||
<key id="cmd_close1" keycode="VK_ESCAPE" oncommand="onClose();"/>
|
||||
<key id="cmd_close2" keycode="VK_RETURN" oncommand="onClose();"/>
|
||||
</keyset>
|
||||
|
||||
<vbox id="main-box" flex="1">
|
||||
<description id="instructions">
|
||||
&instructions.description;
|
||||
</description>
|
||||
|
||||
<vbox flex="1" id="palette-box"
|
||||
ondragstart="onToolbarDragStart(event)"
|
||||
ondragover="onPaletteDragOver(event)"
|
||||
ondrop="onPaletteDrop(event)"/>
|
||||
|
||||
<box align="center">
|
||||
<label value="&show.label;"/>
|
||||
<menulist id="modelist" value="icons" oncommand="updateToolbarMode(this.value);">
|
||||
<menupopup id="modelistpopup">
|
||||
<menuitem id="modefull" value="full" label="&iconsAndText.label;"/>
|
||||
<menuitem id="modeicons" value="icons" label="&icons.label;"/>
|
||||
<menuitem id="modetext" value="text" label="&text.label;"/>
|
||||
</menupopup>
|
||||
</menulist>
|
||||
|
||||
<checkbox id="smallicons" oncommand="updateIconSize(this.checked ? 'small' : 'large');" label="&useSmallIcons.label;"/>
|
||||
|
||||
<button id="newtoolbar" label="&addNewToolbar.label;" oncommand="addNewToolbar();" icon="add"/>
|
||||
<button id="restoreDefault" label="&restoreDefaultSet.label;" oncommand="restoreDefaultSet();" icon="revert"/>
|
||||
</box>
|
||||
|
||||
<separator class="groove"/>
|
||||
|
||||
<hbox align="center" pack="end">
|
||||
<button id="donebutton" label="&saveChanges.label;" oncommand="onClose();"
|
||||
default="true" icon="close"/>
|
||||
</hbox>
|
||||
</vbox>
|
||||
|
||||
</window>
|
||||
13
toolkit/content/directionDetector.html
Normal file
13
toolkit/content/directionDetector.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://global/locale/intl.css">
|
||||
</head>
|
||||
<body>
|
||||
<window id="target" style="display: none;"></window>
|
||||
</body>
|
||||
</html>
|
||||
39
toolkit/content/editMenuOverlay.js
Normal file
39
toolkit/content/editMenuOverlay.js
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// update menu items that rely on focus or on the current selection
|
||||
function goUpdateGlobalEditMenuItems()
|
||||
{
|
||||
// Don't bother updating the edit commands if they aren't visible in any way
|
||||
// (i.e. the Edit menu isn't open, nor is the context menu open, nor have the
|
||||
// cut, copy, and paste buttons been added to the toolbars) for performance.
|
||||
// This only works in applications/on platforms that set the gEditUIVisible
|
||||
// flag, so we check to see if that flag is defined before using it.
|
||||
if (typeof gEditUIVisible != "undefined" && !gEditUIVisible)
|
||||
return;
|
||||
|
||||
goUpdateCommand("cmd_undo");
|
||||
goUpdateCommand("cmd_redo");
|
||||
goUpdateCommand("cmd_cut");
|
||||
goUpdateCommand("cmd_copy");
|
||||
goUpdateCommand("cmd_paste");
|
||||
goUpdateCommand("cmd_selectAll");
|
||||
goUpdateCommand("cmd_delete");
|
||||
goUpdateCommand("cmd_switchTextDirection");
|
||||
}
|
||||
|
||||
// update menu items that relate to undo/redo
|
||||
function goUpdateUndoEditMenuItems()
|
||||
{
|
||||
goUpdateCommand("cmd_undo");
|
||||
goUpdateCommand("cmd_redo");
|
||||
}
|
||||
|
||||
// update menu items that depend on clipboard contents
|
||||
function goUpdatePasteMenuItems()
|
||||
{
|
||||
goUpdateCommand("cmd_paste");
|
||||
}
|
||||
108
toolkit/content/editMenuOverlay.xul
Normal file
108
toolkit/content/editMenuOverlay.xul
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
<?xml version="1.0"?> <!-- -*- Mode: HTML -*- -->
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://global/locale/editMenuOverlay.dtd">
|
||||
|
||||
<overlay id="editMenuOverlay"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/editMenuOverlay.js"/>
|
||||
|
||||
<commandset id="editMenuCommands">
|
||||
<commandset id="editMenuCommandSetAll" commandupdater="true" events="focus,select"
|
||||
oncommandupdate="goUpdateGlobalEditMenuItems()"/>
|
||||
<commandset id="editMenuCommandSetUndo" commandupdater="true" events="undo"
|
||||
oncommandupdate="goUpdateUndoEditMenuItems()"/>
|
||||
<commandset id="editMenuCommandSetPaste" commandupdater="true" events="clipboard"
|
||||
oncommandupdate="goUpdatePasteMenuItems()"/>
|
||||
<command id="cmd_undo" oncommand="goDoCommand('cmd_undo')"/>
|
||||
<command id="cmd_redo" oncommand="goDoCommand('cmd_redo')"/>
|
||||
<command id="cmd_cut" oncommand="goDoCommand('cmd_cut')"/>
|
||||
<command id="cmd_copy" oncommand="goDoCommand('cmd_copy')"/>
|
||||
<command id="cmd_paste" oncommand="goDoCommand('cmd_paste')"/>
|
||||
<command id="cmd_delete" oncommand="goDoCommand('cmd_delete')"/>
|
||||
<command id="cmd_selectAll" oncommand="goDoCommand('cmd_selectAll')"/>
|
||||
<command id="cmd_switchTextDirection" oncommand="goDoCommand('cmd_switchTextDirection');"/>
|
||||
</commandset>
|
||||
|
||||
<!-- These key nodes are here only for show. The real bindings come from
|
||||
XBL, in platformHTMLBindings.xml. See bugs 57078 and 71779. -->
|
||||
|
||||
<keyset id="editMenuKeys">
|
||||
<key id="key_undo" key="&undoCmd.key;" modifiers="accel" command="cmd_undo"/>
|
||||
#ifdef XP_UNIX
|
||||
<key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift" command="cmd_redo"/>
|
||||
#else
|
||||
<key id="key_redo" key="&redoCmd.key;" modifiers="accel" command="cmd_redo"/>
|
||||
#endif
|
||||
<key id="key_cut" key="&cutCmd.key;" modifiers="accel" command="cmd_cut"/>
|
||||
<key id="key_copy" key="©Cmd.key;" modifiers="accel" command="cmd_copy"/>
|
||||
<key id="key_paste" key="&pasteCmd.key;" modifiers="accel" command="cmd_paste"/>
|
||||
<key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
|
||||
<key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel" command="cmd_selectAll"/>
|
||||
<key id="key_find" key="&findCmd.key;" modifiers="accel" command="cmd_find"/>
|
||||
<key id="key_findAgain" key="&findAgainCmd.key;" modifiers="accel" command="cmd_findAgain"/>
|
||||
<key id="key_findPrevious" key="&findAgainCmd.key;" modifiers="shift,accel" command="cmd_findPrevious"/>
|
||||
<key id="key_findAgain2" keycode="&findAgainCmd.key2;" command="cmd_findAgain"/>
|
||||
<key id="key_findPrevious2" keycode="&findAgainCmd.key2;" modifiers="shift" command="cmd_findPrevious"/>
|
||||
</keyset>
|
||||
|
||||
<!-- Edit Menu -->
|
||||
<menu id="menu_edit" label="&editMenu.label;"
|
||||
accesskey="&editMenu.accesskey;"/>
|
||||
|
||||
<menuitem id="menu_undo" label="&undoCmd.label;"
|
||||
key="key_undo" accesskey="&undoCmd.accesskey;"
|
||||
command="cmd_undo"/>
|
||||
<menuitem id="menu_redo" label="&redoCmd.label;"
|
||||
key="key_redo" accesskey="&redoCmd.accesskey;"
|
||||
command="cmd_redo"/>
|
||||
<menuitem id="menu_cut" label="&cutCmd.label;"
|
||||
key="key_cut" accesskey="&cutCmd.accesskey;"
|
||||
command="cmd_cut"/>
|
||||
<menuitem id="menu_copy" label="©Cmd.label;"
|
||||
key="key_copy" accesskey="©Cmd.accesskey;"
|
||||
command="cmd_copy"/>
|
||||
<menuitem id="menu_paste" label="&pasteCmd.label;"
|
||||
key="key_paste" accesskey="&pasteCmd.accesskey;"
|
||||
command="cmd_paste"/>
|
||||
<menuitem id="menu_delete" label="&deleteCmd.label;"
|
||||
key="key_delete" accesskey="&deleteCmd.accesskey;"
|
||||
command="cmd_delete"/>
|
||||
<menuitem id="menu_selectAll" label="&selectAllCmd.label;"
|
||||
key="key_selectAll" accesskey="&selectAllCmd.accesskey;"
|
||||
command="cmd_selectAll"/>
|
||||
<menuitem id="menu_find" label="&findCmd.label;"
|
||||
key="key_find" accesskey="&findCmd.accesskey;"
|
||||
command="cmd_find"/>
|
||||
<menuitem id="menu_findAgain" label="&findAgainCmd.label;"
|
||||
key="key_findAgain" accesskey="&findAgainCmd.accesskey;"
|
||||
command="cmd_findAgain"/>
|
||||
<menuitem id="menu_findPrevious" label="&findPreviousCmd.label;"
|
||||
key="key_findPrevious" accesskey="&findPreviousCmd.accesskey;"
|
||||
command="cmd_findPrevious"/>
|
||||
|
||||
<menuitem id="cMenu_undo" label="&undoCmd.label;"
|
||||
accesskey="&undoCmd.accesskey;" command="cmd_undo"/>
|
||||
<menuitem id="cMenu_redo" label="&redoCmd.label;"
|
||||
accesskey="&redoCmd.accesskey;" command="cmd_redo"/>
|
||||
<menuitem id="cMenu_cut" label="&cutCmd.label;"
|
||||
accesskey="&cutCmd.accesskey;" command="cmd_cut"/>
|
||||
<menuitem id="cMenu_copy" label="©Cmd.label;"
|
||||
accesskey="©Cmd.accesskey;" command="cmd_copy"/>
|
||||
<menuitem id="cMenu_paste" label="&pasteCmd.label;"
|
||||
accesskey="&pasteCmd.accesskey;" command="cmd_paste"/>
|
||||
<menuitem id="cMenu_delete" label="&deleteCmd.label;"
|
||||
accesskey="&deleteCmd.accesskey;" command="cmd_delete"/>
|
||||
<menuitem id="cMenu_selectAll" label="&selectAllCmd.label;"
|
||||
accesskey="&selectAllCmd.accesskey;" command="cmd_selectAll"/>
|
||||
<menuitem id="cMenu_find" label="&findCmd.label;"
|
||||
accesskey="&findCmd.accesskey;" command="cmd_find"/>
|
||||
<menuitem id="cMenu_findAgain" label="&findAgainCmd.label;"
|
||||
accesskey="&findAgainCmd.accesskey;" command="cmd_findAgain"/>
|
||||
<menuitem id="cMenu_findPrevious" label="&findPreviousCmd.label;"
|
||||
accesskey="&findPreviousCmd.accesskey;" command="cmd_findPrevious"/>
|
||||
</overlay>
|
||||
12
toolkit/content/filepicker.properties
Normal file
12
toolkit/content/filepicker.properties
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# 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/.
|
||||
|
||||
allFilter=*
|
||||
htmlFilter=*.html; *.htm; *.shtml; *.xhtml
|
||||
textFilter=*.txt; *.text
|
||||
imageFilter=*.jpe; *.jpg; *.jpeg; *.gif; *.png; *.bmp; *.ico; *.svg; *.svgz; *.tif; *.tiff; *.ai; *.drw; *.pct; *.psp; *.xcf; *.psd; *.raw
|
||||
xmlFilter=*.xml
|
||||
xulFilter=*.xul
|
||||
audioFilter=*.aac; *.aif; *.flac; *.iff; *.m4a; *.m4b; *.mid; *.midi; *.mp3; *.mpa; *.mpc; *.oga; *.ogg; *.ra; *.ram; *.snd; *.wav; *.wma
|
||||
videoFilter=*.avi; *.divx; *.flv; *.m4v; *.mkv; *.mov; *.mp4; *.mpeg; *.mpg; *.ogm; *.ogv; *.ogx; *.rm; *.rmvb; *.smil; *.webm; *.wmv; *.xvid
|
||||
111
toolkit/content/findUtils.js
Normal file
111
toolkit/content/findUtils.js
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var gFindBundle;
|
||||
|
||||
function nsFindInstData() {}
|
||||
nsFindInstData.prototype =
|
||||
{
|
||||
// set the next three attributes on your object to override the defaults
|
||||
browser : null,
|
||||
|
||||
get rootSearchWindow() { return this._root || this.window.content; },
|
||||
set rootSearchWindow(val) { this._root = val; },
|
||||
|
||||
get currentSearchWindow() {
|
||||
if (this._current)
|
||||
return this._current;
|
||||
|
||||
var focusedWindow = this.window.document.commandDispatcher.focusedWindow;
|
||||
if (!focusedWindow || focusedWindow == this.window)
|
||||
focusedWindow = this.window.content;
|
||||
|
||||
return focusedWindow;
|
||||
},
|
||||
set currentSearchWindow(val) { this._current = val; },
|
||||
|
||||
get webBrowserFind() { return this.browser.webBrowserFind; },
|
||||
|
||||
init : function() {
|
||||
var findInst = this.webBrowserFind;
|
||||
// set up the find to search the focussedWindow, bounded by the content window.
|
||||
var findInFrames = findInst.QueryInterface(Components.interfaces.nsIWebBrowserFindInFrames);
|
||||
findInFrames.rootSearchFrame = this.rootSearchWindow;
|
||||
findInFrames.currentSearchFrame = this.currentSearchWindow;
|
||||
|
||||
// always search in frames for now. We could add a checkbox to the dialog for this.
|
||||
findInst.searchFrames = true;
|
||||
},
|
||||
|
||||
window : window,
|
||||
_root : null,
|
||||
_current : null
|
||||
}
|
||||
|
||||
// browser is the <browser> element
|
||||
// rootSearchWindow is the window to constrain the search to (normally window.content)
|
||||
// currentSearchWindow is the frame to start searching (can be, and normally, rootSearchWindow)
|
||||
function findInPage(findInstData)
|
||||
{
|
||||
// is the dialog up already?
|
||||
if ("findDialog" in window && window.findDialog)
|
||||
window.findDialog.focus();
|
||||
else
|
||||
{
|
||||
findInstData.init();
|
||||
window.findDialog = window.openDialog("chrome://global/content/finddialog.xul", "_blank", "chrome,resizable=no,dependent=yes", findInstData);
|
||||
}
|
||||
}
|
||||
|
||||
function findAgainInPage(findInstData, reverse)
|
||||
{
|
||||
if ("findDialog" in window && window.findDialog)
|
||||
window.findDialog.focus();
|
||||
else
|
||||
{
|
||||
// get the find service, which stores global find state, and init the
|
||||
// nsIWebBrowser find with it. We don't assume that there was a previous
|
||||
// Find that set this up.
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
|
||||
var searchString = findService.searchString;
|
||||
if (searchString.length == 0) {
|
||||
// no previous find text
|
||||
findInPage(findInstData);
|
||||
return;
|
||||
}
|
||||
|
||||
findInstData.init();
|
||||
var findInst = findInstData.webBrowserFind;
|
||||
findInst.searchString = searchString;
|
||||
findInst.matchCase = findService.matchCase;
|
||||
findInst.wrapFind = findService.wrapFind;
|
||||
findInst.entireWord = findService.entireWord;
|
||||
findInst.findBackwards = findService.findBackwards ^ reverse;
|
||||
|
||||
var found = findInst.findNext();
|
||||
if (!found) {
|
||||
if (!gFindBundle)
|
||||
gFindBundle = document.getElementById("findBundle");
|
||||
|
||||
Services.prompt.alert(window, gFindBundle.getString("notFoundTitle"), gFindBundle.getString("notFoundWarning"));
|
||||
}
|
||||
|
||||
// Reset to normal value, otherwise setting can get changed in find dialog
|
||||
findInst.findBackwards = findService.findBackwards;
|
||||
}
|
||||
}
|
||||
|
||||
function canFindAgainInPage()
|
||||
{
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
return (findService.searchString.length > 0);
|
||||
}
|
||||
|
||||
151
toolkit/content/finddialog.js
Normal file
151
toolkit/content/finddialog.js
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/FormHistory.jsm");
|
||||
|
||||
var dialog; // Quick access to document/form elements.
|
||||
var gFindInst; // nsIWebBrowserFind that we're going to use
|
||||
var gFindInstData; // use this to update the find inst data
|
||||
|
||||
function initDialogObject()
|
||||
{
|
||||
// Create dialog object and initialize.
|
||||
dialog = {};
|
||||
dialog.findKey = document.getElementById("dialog.findKey");
|
||||
dialog.caseSensitive = document.getElementById("dialog.caseSensitive");
|
||||
dialog.wrap = document.getElementById("dialog.wrap");
|
||||
dialog.find = document.getElementById("btnFind");
|
||||
dialog.up = document.getElementById("radioUp");
|
||||
dialog.down = document.getElementById("radioDown");
|
||||
dialog.rg = dialog.up.radioGroup;
|
||||
dialog.bundle = null;
|
||||
|
||||
// Move dialog to center, if it not been shown before
|
||||
var windowElement = document.getElementById("findDialog");
|
||||
if (!windowElement.hasAttribute("screenX") || !windowElement.hasAttribute("screenY"))
|
||||
{
|
||||
sizeToContent();
|
||||
moveToAlertPosition();
|
||||
}
|
||||
}
|
||||
|
||||
function fillDialog()
|
||||
{
|
||||
// get the find service, which stores global find state
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
|
||||
// Set initial dialog field contents. Use the gFindInst attributes first,
|
||||
// this is necessary for window.find()
|
||||
dialog.findKey.value = gFindInst.searchString ? gFindInst.searchString : findService.searchString;
|
||||
dialog.caseSensitive.checked = gFindInst.matchCase ? gFindInst.matchCase : findService.matchCase;
|
||||
dialog.wrap.checked = gFindInst.wrapFind ? gFindInst.wrapFind : findService.wrapFind;
|
||||
var findBackwards = gFindInst.findBackwards ? gFindInst.findBackwards : findService.findBackwards;
|
||||
if (findBackwards)
|
||||
dialog.rg.selectedItem = dialog.up;
|
||||
else
|
||||
dialog.rg.selectedItem = dialog.down;
|
||||
}
|
||||
|
||||
function saveFindData()
|
||||
{
|
||||
// get the find service, which stores global find state
|
||||
var findService = Components.classes["@mozilla.org/find/find_service;1"]
|
||||
.getService(Components.interfaces.nsIFindService);
|
||||
|
||||
// Set data attributes per user input.
|
||||
findService.searchString = dialog.findKey.value;
|
||||
findService.matchCase = dialog.caseSensitive.checked;
|
||||
findService.wrapFind = dialog.wrap.checked;
|
||||
findService.findBackwards = dialog.up.selected;
|
||||
}
|
||||
|
||||
function onLoad()
|
||||
{
|
||||
initDialogObject();
|
||||
|
||||
// get the find instance
|
||||
var arg0 = window.arguments[0];
|
||||
// If the dialog was opened from window.find(),
|
||||
// arg0 will be an instance of nsIWebBrowserFind
|
||||
if (arg0 instanceof Components.interfaces.nsIWebBrowserFind) {
|
||||
gFindInst = arg0;
|
||||
} else {
|
||||
gFindInstData = arg0;
|
||||
gFindInst = gFindInstData.webBrowserFind;
|
||||
}
|
||||
|
||||
fillDialog();
|
||||
doEnabling();
|
||||
|
||||
if (dialog.findKey.value)
|
||||
dialog.findKey.select();
|
||||
dialog.findKey.focus();
|
||||
}
|
||||
|
||||
function onUnload()
|
||||
{
|
||||
window.opener.findDialog = 0;
|
||||
}
|
||||
|
||||
function onAccept()
|
||||
{
|
||||
if (gFindInstData && gFindInst != gFindInstData.webBrowserFind) {
|
||||
gFindInstData.init();
|
||||
gFindInst = gFindInstData.webBrowserFind;
|
||||
}
|
||||
|
||||
// Transfer dialog contents to the find service.
|
||||
saveFindData();
|
||||
updateFormHistory();
|
||||
|
||||
// set up the find instance
|
||||
gFindInst.searchString = dialog.findKey.value;
|
||||
gFindInst.matchCase = dialog.caseSensitive.checked;
|
||||
gFindInst.wrapFind = dialog.wrap.checked;
|
||||
gFindInst.findBackwards = dialog.up.selected;
|
||||
|
||||
// Search.
|
||||
var result = gFindInst.findNext();
|
||||
|
||||
if (!result)
|
||||
{
|
||||
if (!dialog.bundle)
|
||||
dialog.bundle = document.getElementById("findBundle");
|
||||
Services.prompt.alert(window, dialog.bundle.getString("notFoundTitle"),
|
||||
dialog.bundle.getString("notFoundWarning"));
|
||||
dialog.findKey.select();
|
||||
dialog.findKey.focus();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function doEnabling()
|
||||
{
|
||||
dialog.find.disabled = !dialog.findKey.value;
|
||||
}
|
||||
|
||||
function updateFormHistory()
|
||||
{
|
||||
if (window.opener.PrivateBrowsingUtils &&
|
||||
window.opener.PrivateBrowsingUtils.isWindowPrivate(window.opener) ||
|
||||
!dialog.findKey.value)
|
||||
return;
|
||||
|
||||
if (FormHistory.enabled) {
|
||||
FormHistory.update({
|
||||
op: "bump",
|
||||
fieldname: "find-dialog",
|
||||
value: dialog.findKey.value
|
||||
}, {
|
||||
handleError: function(aError) {
|
||||
Components.utils.reportError("Saving find to form history failed: " +
|
||||
aError.message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
58
toolkit/content/finddialog.xul
Normal file
58
toolkit/content/finddialog.xul
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0"?> <!-- -*- Mode: HTML -*- -->
|
||||
# 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/.
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window SYSTEM "chrome://global/locale/finddialog.dtd">
|
||||
|
||||
<dialog id="findDialog"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
orient="horizontal"
|
||||
windowtype="findInPage"
|
||||
onload="onLoad();"
|
||||
onunload="onUnload();"
|
||||
ondialogaccept="return onAccept();"
|
||||
buttons="accept,cancel"
|
||||
title="&findDialog.title;"
|
||||
persist="screenX screenY">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/finddialog.js"/>
|
||||
<stringbundle id="findBundle" src="chrome://global/locale/finddialog.properties"/>
|
||||
|
||||
<hbox>
|
||||
<vbox>
|
||||
<hbox align="center">
|
||||
<label value="&findField.label;" accesskey="&findField.accesskey;" control="dialog.findKey"/>
|
||||
<textbox id="dialog.findKey" flex="1"
|
||||
type="autocomplete"
|
||||
autocompletesearch="form-history"
|
||||
autocompletesearchparam="find-dialog"
|
||||
oninput="doEnabling();"/>
|
||||
</hbox>
|
||||
<hbox align="center">
|
||||
<vbox>
|
||||
<checkbox id="dialog.caseSensitive" label="&caseSensitiveCheckbox.label;" accesskey="&caseSensitiveCheckbox.accesskey;"/>
|
||||
<checkbox id="dialog.wrap" label="&wrapCheckbox.label;" accesskey="&wrapCheckbox.accesskey;" checked="true"/>
|
||||
</vbox>
|
||||
<groupbox orient="horizontal">
|
||||
<caption label="&direction.label;"/>
|
||||
<radiogroup orient="horizontal">
|
||||
<radio id="radioUp" label="&up.label;" accesskey="&up.accesskey;"/>
|
||||
<radio id="radioDown" label="&down.label;" accesskey="&down.accesskey;" selected="true"/>
|
||||
</radiogroup>
|
||||
</groupbox>
|
||||
</hbox>
|
||||
</vbox>
|
||||
<vbox>
|
||||
<button id="btnFind" label="&findButton.label;" accesskey="&findButton.accesskey;"
|
||||
dlgtype="accept" icon="find"/>
|
||||
#ifdef XP_UNIX
|
||||
<button label="&closeButton.label;" icon="close" dlgtype="cancel"/>
|
||||
#else
|
||||
<button label="&cancelButton.label;" icon="cancel" dlgtype="cancel"/>
|
||||
#endif
|
||||
</vbox>
|
||||
</hbox>
|
||||
</dialog>
|
||||
168
toolkit/content/globalOverlay.js
Normal file
168
toolkit/content/globalOverlay.js
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
/* 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/. */
|
||||
|
||||
function closeWindow(aClose, aPromptFunction)
|
||||
{
|
||||
let { AppConstants } = Components.utils.import("resource://gre/modules/AppConstants.jsm");
|
||||
|
||||
// Closing the last window doesn't quit the application on OS X.
|
||||
if (AppConstants.platform != "macosx") {
|
||||
var windowCount = 0;
|
||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||
.getService(Components.interfaces.nsIWindowMediator);
|
||||
var e = wm.getEnumerator(null);
|
||||
|
||||
while (e.hasMoreElements()) {
|
||||
var w = e.getNext();
|
||||
if (w.closed) {
|
||||
continue;
|
||||
}
|
||||
if (++windowCount == 2)
|
||||
break;
|
||||
}
|
||||
|
||||
// If we're down to the last window and someone tries to shut down, check to make sure we can!
|
||||
if (windowCount == 1 && !canQuitApplication("lastwindow"))
|
||||
return false;
|
||||
if (windowCount != 1 && typeof(aPromptFunction) == "function" && !aPromptFunction())
|
||||
return false;
|
||||
} else if (typeof(aPromptFunction) == "function" && !aPromptFunction()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aClose) {
|
||||
window.close();
|
||||
return window.closed;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function canQuitApplication(aData)
|
||||
{
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
if (!os) return true;
|
||||
|
||||
try {
|
||||
var cancelQuit = Components.classes["@mozilla.org/supports-PRBool;1"]
|
||||
.createInstance(Components.interfaces.nsISupportsPRBool);
|
||||
os.notifyObservers(cancelQuit, "quit-application-requested", aData || null);
|
||||
|
||||
// Something aborted the quit process.
|
||||
if (cancelQuit.data)
|
||||
return false;
|
||||
}
|
||||
catch (ex) { }
|
||||
return true;
|
||||
}
|
||||
|
||||
function goQuitApplication()
|
||||
{
|
||||
if (!canQuitApplication())
|
||||
return false;
|
||||
|
||||
var appStartup = Components.classes['@mozilla.org/toolkit/app-startup;1'].
|
||||
getService(Components.interfaces.nsIAppStartup);
|
||||
|
||||
appStartup.quit(Components.interfaces.nsIAppStartup.eAttemptQuit);
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// Command Updater functions
|
||||
//
|
||||
function goUpdateCommand(aCommand)
|
||||
{
|
||||
try {
|
||||
var controller = top.document.commandDispatcher
|
||||
.getControllerForCommand(aCommand);
|
||||
|
||||
var enabled = false;
|
||||
if (controller)
|
||||
enabled = controller.isCommandEnabled(aCommand);
|
||||
|
||||
goSetCommandEnabled(aCommand, enabled);
|
||||
}
|
||||
catch (e) {
|
||||
Components.utils.reportError("An error occurred updating the " +
|
||||
aCommand + " command: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
function goDoCommand(aCommand)
|
||||
{
|
||||
try {
|
||||
var controller = top.document.commandDispatcher
|
||||
.getControllerForCommand(aCommand);
|
||||
if (controller && controller.isCommandEnabled(aCommand))
|
||||
controller.doCommand(aCommand);
|
||||
}
|
||||
catch (e) {
|
||||
Components.utils.reportError("An error occurred executing the " +
|
||||
aCommand + " command: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function goSetCommandEnabled(aID, aEnabled)
|
||||
{
|
||||
var node = document.getElementById(aID);
|
||||
|
||||
if (node) {
|
||||
if (aEnabled)
|
||||
node.removeAttribute("disabled");
|
||||
else
|
||||
node.setAttribute("disabled", "true");
|
||||
}
|
||||
}
|
||||
|
||||
function goSetMenuValue(aCommand, aLabelAttribute)
|
||||
{
|
||||
var commandNode = top.document.getElementById(aCommand);
|
||||
if (commandNode) {
|
||||
var label = commandNode.getAttribute(aLabelAttribute);
|
||||
if (label)
|
||||
commandNode.setAttribute("label", label);
|
||||
}
|
||||
}
|
||||
|
||||
function goSetAccessKey(aCommand, aValueAttribute)
|
||||
{
|
||||
var commandNode = top.document.getElementById(aCommand);
|
||||
if (commandNode) {
|
||||
var value = commandNode.getAttribute(aValueAttribute);
|
||||
if (value)
|
||||
commandNode.setAttribute("accesskey", value);
|
||||
}
|
||||
}
|
||||
|
||||
// this function is used to inform all the controllers attached to a node that an event has occurred
|
||||
// (e.g. the tree controllers need to be informed of blur events so that they can change some of the
|
||||
// menu items back to their default values)
|
||||
function goOnEvent(aNode, aEvent)
|
||||
{
|
||||
var numControllers = aNode.controllers.getControllerCount();
|
||||
var controller;
|
||||
|
||||
for (var controllerIndex = 0; controllerIndex < numControllers; controllerIndex++) {
|
||||
controller = aNode.controllers.getControllerAt(controllerIndex);
|
||||
if (controller)
|
||||
controller.onEvent(aEvent);
|
||||
}
|
||||
}
|
||||
|
||||
function setTooltipText(aID, aTooltipText)
|
||||
{
|
||||
var element = document.getElementById(aID);
|
||||
if (element)
|
||||
element.setAttribute("tooltiptext", aTooltipText);
|
||||
}
|
||||
|
||||
this.__defineGetter__("NS_ASSERT", function() {
|
||||
delete this.NS_ASSERT;
|
||||
var tmpScope = {};
|
||||
Components.utils.import("resource://gre/modules/debug.js", tmpScope);
|
||||
return this.NS_ASSERT = tmpScope.NS_ASSERT;
|
||||
});
|
||||
31
toolkit/content/gmp-sources/eme-adobe.json
Normal file
31
toolkit/content/gmp-sources/eme-adobe.json
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"vendors": {
|
||||
"gmp-eme-adobe": {
|
||||
"platforms": {
|
||||
"WINNT_x86-msvc-x64": {
|
||||
"alias": "WINNT_x86-msvc"
|
||||
},
|
||||
"WINNT_x86-msvc": {
|
||||
"fileUrl": "https://cdmdownload.adobe.com/firefox/win/x86/primetime_gmp_win_x86_gmc_40673.zip",
|
||||
"hashValue": "8aad35fc13814b0f1daacddb0d599eedd685287d5afddc97c2f740c8aea270636ccd75b1d1a57364b84e8eb1b23c9f1c126c057d95f3d8217b331dc4b1d5340f",
|
||||
"filesize": 3694349
|
||||
},
|
||||
"WINNT_x86_64-msvc-x64": {
|
||||
"alias": "WINNT_x86_64-msvc"
|
||||
},
|
||||
"WINNT_x86-msvc-x86": {
|
||||
"alias": "WINNT_x86-msvc"
|
||||
},
|
||||
"WINNT_x86_64-msvc": {
|
||||
"fileUrl": "https://cdmdownload.adobe.com/firefox/win/x64/primetime_gmp_win_x64_gmc_40673.zip",
|
||||
"hashValue": "bd1e1a370c5f9dadc247c9f00dd203fab1a75ff3afed8439a0a0bfcc7e1767d0da68497140cbe48daa70e2535dde5f220dd7b344619cecd830a6b685efb9d5a0",
|
||||
"filesize": 4853103
|
||||
}
|
||||
},
|
||||
"version": "17"
|
||||
}
|
||||
},
|
||||
"hashFunction": "sha512",
|
||||
"name": "CDM-17",
|
||||
"schema_version": 1000
|
||||
}
|
||||
67
toolkit/content/gmp-sources/openh264.json
Normal file
67
toolkit/content/gmp-sources/openh264.json
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"vendors": {
|
||||
"gmp-gmpopenh264": {
|
||||
"platforms": {
|
||||
"WINNT_x86-msvc-x64": {
|
||||
"alias": "WINNT_x86-msvc"
|
||||
},
|
||||
"Android_x86-gcc3": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-android-x86-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "8ce4d4318aa6ae9ac1376500d5fceecb3df38727aa920efd9f7829c139face4a069cab683d3902e7cdb89daad2a7e928ffba120812ae343f052a833812dad387",
|
||||
"filesize": 1640053
|
||||
},
|
||||
"WINNT_x86-msvc": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-win32-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "991e01c3b95fa13fac52e0512e1936f1edae42ecbbbcc55447a36915eb3ca8f836546cc780343751691e0188872e5bc56fe3ad5f23f3243e90b96a637561b89e",
|
||||
"filesize": 356940
|
||||
},
|
||||
"WINNT_x86-msvc-x86": {
|
||||
"alias": "WINNT_x86-msvc"
|
||||
},
|
||||
"Linux_x86_64-gcc3": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-linux64-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "e1086ee6e4fb60a1aa11b5626594b97695533a8e269d776877cebd5cf29088619e2c164e7bd1eba5486f772c943f2efec723f69cc48478ec84a11d7b61ca1865",
|
||||
"filesize": 515722
|
||||
},
|
||||
"Darwin_x86-gcc3-u-i386-x86_64": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-macosx32-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "64b0e13e6319b7a31ed35a46bea5abcfe6af04ba59a277db07677236cfb685813763731ff6b44b85e03e1489f3b15f8df0128a299a36720531b9f4ba6e1c1f58",
|
||||
"filesize": 382435
|
||||
},
|
||||
"Darwin_x86_64-gcc3": {
|
||||
"alias": "Darwin_x86_64-gcc3-u-i386-x86_64"
|
||||
},
|
||||
"Linux_x86-gcc3": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-linux32-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "19084f0230218c584715861f4723e072b1af02e26995762f368105f670f60ecb4082531bc4e33065a4675dd1296f6872a6cb101547ef2d19ef3e25e2e16d4dc0",
|
||||
"filesize": 515857
|
||||
},
|
||||
"Darwin_x86_64-gcc3-u-i386-x86_64": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-macosx64-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "3b52343070a2f75e91b7b0d3bb33935352237c7e1d2fdc6a467d039ffbbda6a72087f9e0a369fe95e6c4c789ff3052f0c134af721d7273db9ba66d077d85b327",
|
||||
"filesize": 390308
|
||||
},
|
||||
"Android_arm-eabi-gcc3": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-android-arm-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "7a15245c781f32df310ebb88cb8a783512eab934b38ffd889d6420473d40eddbe8a89c17cc60d4e7647c156b04d20030e1ae0081e3f90a0d8f94626ec5f4d817",
|
||||
"filesize": 1515895
|
||||
},
|
||||
"Darwin_x86-gcc3": {
|
||||
"alias": "Darwin_x86-gcc3-u-i386-x86_64"
|
||||
},
|
||||
"WINNT_x86_64-msvc-x64": {
|
||||
"alias": "WINNT_x86_64-msvc"
|
||||
},
|
||||
"WINNT_x86_64-msvc": {
|
||||
"fileUrl": "http://ciscobinary.openh264.org/openh264-win64-0410d336bb748149a4f560eb6108090f078254b1.zip",
|
||||
"hashValue": "5030b47065e817db5c40bca9c62ac27292bbf636e24698f45dc67f03fa6420b97bd2f792c1cb39df65776c1e7597c70122ac7abf36fb2ad0603734e9e8ec4ef3",
|
||||
"filesize": 404355
|
||||
}
|
||||
},
|
||||
"version": "1.6"
|
||||
}
|
||||
},
|
||||
"hashFunction": "sha512",
|
||||
"name": "OpenH264-1.6",
|
||||
"schema_version": 1000
|
||||
}
|
||||
49
toolkit/content/gmp-sources/widevinecdm.json
Normal file
49
toolkit/content/gmp-sources/widevinecdm.json
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"vendors": {
|
||||
"gmp-widevinecdm": {
|
||||
"platforms": {
|
||||
"WINNT_x86-msvc-x64": {
|
||||
"alias": "WINNT_x86-msvc"
|
||||
},
|
||||
"WINNT_x86-msvc": {
|
||||
"fileUrl": "https://redirector.gvt1.com/edgedl/widevine-cdm/903-win-ia32.zip",
|
||||
"hashValue": "d7e10d09c87a157af865f8388ba70ae672bd9e38987bdd94077af52d6b1abaa745b3db92e9f93f607af6420c68210f7cfd518a9d2c99fecf79aed3385cbcbc0b",
|
||||
"filesize": 2884452
|
||||
},
|
||||
"WINNT_x86-msvc-x86": {
|
||||
"alias": "WINNT_x86-msvc"
|
||||
},
|
||||
"Linux_x86_64-gcc3": {
|
||||
"fileUrl": "https://redirector.gvt1.com/edgedl/widevine-cdm/903-linux-x64.zip",
|
||||
"hashValue": "1edfb58a44792d2a53694f46fcc698161edafb2a1fe0e5c31b50c1d52408b5e8918d9f33271c62a19a65017694ebeacb4f390fe914688ca7b1952cdb84ed55ec",
|
||||
"filesize": 2975492
|
||||
},
|
||||
"Darwin_x86_64-gcc3-u-i386-x86_64": {
|
||||
"fileUrl": "https://redirector.gvt1.com/edgedl/widevine-cdm/903-mac-x64.zip",
|
||||
"hashValue": "1916805e84a49e04748204f4e4c48ae52c8312f7c04afedacacd7dfab2de424412bc988a8c3e5bcb0865f8844b569c0eb9589dae51e74d9bdfe46792c9d1631f",
|
||||
"filesize": 2155607
|
||||
},
|
||||
"Darwin_x86_64-gcc3": {
|
||||
"alias": "Darwin_x86_64-gcc3-u-i386-x86_64"
|
||||
},
|
||||
"Linux_x86-gcc3": {
|
||||
"fileUrl": "https://redirector.gvt1.com/edgedl/widevine-cdm/903-linux-ia32.zip",
|
||||
"hashValue": "5c4beb72ea693740a013b60bc5491a042bb82fa5ca6845a2f450579e2e1e465263f19e7ab6d08d91deb8219b30f092ab6e6745300d5adda627f270c95e5a66e0",
|
||||
"filesize": 3084582
|
||||
},
|
||||
"WINNT_x86_64-msvc-x64": {
|
||||
"alias": "WINNT_x86_64-msvc"
|
||||
},
|
||||
"WINNT_x86_64-msvc": {
|
||||
"fileUrl": "https://redirector.gvt1.com/edgedl/widevine-cdm/903-win-x64.zip",
|
||||
"hashValue": "33497f3458846e11fa52413f6477bfe1a7f502da262c3a2ce9fe6d773a4a2d023c54228596eb162444b55c87fb126de01f60fa729d897ef5e6eec73b2dfbdc7a",
|
||||
"filesize": 2853777
|
||||
}
|
||||
},
|
||||
"version": "1.4.8.903"
|
||||
}
|
||||
},
|
||||
"hashFunction": "sha512",
|
||||
"name": "Widevine-1.4.8.903",
|
||||
"schema_version": 1000
|
||||
}
|
||||
120
toolkit/content/jar.mn
Normal file
120
toolkit/content/jar.mn
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
toolkit.jar:
|
||||
% content global %content/global/ contentaccessible=yes
|
||||
% content global-platform %content/global-platform/ platform
|
||||
* content/global/license.html
|
||||
content/global/XPCNativeWrapper.js
|
||||
content/global/minimal-xul.css
|
||||
* content/global/xul.css
|
||||
content/global/textbox.css
|
||||
content/global/menulist.css
|
||||
content/global/autocomplete.css
|
||||
content/global/about.js
|
||||
content/global/about.xhtml
|
||||
content/global/aboutAbout.js
|
||||
content/global/aboutAbout.xhtml
|
||||
#ifdef MOZILLA_OFFICIAL
|
||||
content/global/aboutRights.xhtml
|
||||
#else
|
||||
content/global/aboutRights.xhtml (aboutRights-unbranded.xhtml)
|
||||
#endif
|
||||
content/global/aboutNetworking.js
|
||||
content/global/aboutNetworking.xhtml
|
||||
#ifndef ANDROID
|
||||
content/global/aboutProfiles.js
|
||||
content/global/aboutProfiles.xhtml
|
||||
#endif
|
||||
content/global/aboutServiceWorkers.js
|
||||
content/global/aboutServiceWorkers.xhtml
|
||||
content/global/aboutwebrtc/aboutWebrtc.css (aboutwebrtc/aboutWebrtc.css)
|
||||
content/global/aboutwebrtc/aboutWebrtc.js (aboutwebrtc/aboutWebrtc.js)
|
||||
content/global/aboutwebrtc/aboutWebrtc.html (aboutwebrtc/aboutWebrtc.html)
|
||||
content/global/aboutSupport.js
|
||||
* content/global/aboutSupport.xhtml
|
||||
content/global/aboutTelemetry.js
|
||||
content/global/aboutTelemetry.xhtml
|
||||
content/global/aboutTelemetry.css
|
||||
content/global/directionDetector.html
|
||||
content/global/plugins.html
|
||||
content/global/plugins.css
|
||||
content/global/browser-child.js
|
||||
content/global/browser-content.js
|
||||
* content/global/buildconfig.html
|
||||
content/global/contentAreaUtils.js
|
||||
#ifndef MOZ_FENNEC
|
||||
content/global/customizeToolbar.css
|
||||
content/global/customizeToolbar.js
|
||||
content/global/customizeToolbar.xul
|
||||
#endif
|
||||
#ifndef MOZ_FENNEC
|
||||
content/global/editMenuOverlay.js
|
||||
* content/global/editMenuOverlay.xul
|
||||
content/global/finddialog.js
|
||||
* content/global/finddialog.xul
|
||||
content/global/findUtils.js
|
||||
#endif
|
||||
content/global/filepicker.properties
|
||||
content/global/globalOverlay.js
|
||||
content/global/mozilla.xhtml
|
||||
content/global/process-content.js
|
||||
content/global/resetProfile.css
|
||||
content/global/resetProfile.js
|
||||
content/global/resetProfile.xul
|
||||
content/global/resetProfileProgress.xul
|
||||
content/global/select-child.js
|
||||
content/global/TopLevelVideoDocument.js
|
||||
content/global/timepicker.xhtml
|
||||
content/global/treeUtils.js
|
||||
content/global/viewZoomOverlay.js
|
||||
content/global/bindings/autocomplete.xml (widgets/autocomplete.xml)
|
||||
content/global/bindings/browser.xml (widgets/browser.xml)
|
||||
content/global/bindings/button.xml (widgets/button.xml)
|
||||
content/global/bindings/checkbox.xml (widgets/checkbox.xml)
|
||||
content/global/bindings/colorpicker.xml (widgets/colorpicker.xml)
|
||||
content/global/bindings/datetimepicker.xml (widgets/datetimepicker.xml)
|
||||
content/global/bindings/datetimepopup.xml (widgets/datetimepopup.xml)
|
||||
content/global/bindings/datetimebox.xml (widgets/datetimebox.xml)
|
||||
content/global/bindings/datetimebox.css (widgets/datetimebox.css)
|
||||
* content/global/bindings/dialog.xml (widgets/dialog.xml)
|
||||
content/global/bindings/editor.xml (widgets/editor.xml)
|
||||
content/global/bindings/expander.xml (widgets/expander.xml)
|
||||
content/global/bindings/filefield.xml (widgets/filefield.xml)
|
||||
* content/global/bindings/findbar.xml (widgets/findbar.xml)
|
||||
content/global/bindings/general.xml (widgets/general.xml)
|
||||
content/global/bindings/groupbox.xml (widgets/groupbox.xml)
|
||||
content/global/bindings/listbox.xml (widgets/listbox.xml)
|
||||
content/global/bindings/menu.xml (widgets/menu.xml)
|
||||
content/global/bindings/menulist.xml (widgets/menulist.xml)
|
||||
content/global/bindings/notification.xml (widgets/notification.xml)
|
||||
content/global/bindings/numberbox.xml (widgets/numberbox.xml)
|
||||
content/global/bindings/popup.xml (widgets/popup.xml)
|
||||
* content/global/bindings/preferences.xml (widgets/preferences.xml)
|
||||
content/global/bindings/progressmeter.xml (widgets/progressmeter.xml)
|
||||
content/global/bindings/radio.xml (widgets/radio.xml)
|
||||
content/global/bindings/remote-browser.xml (widgets/remote-browser.xml)
|
||||
content/global/bindings/resizer.xml (widgets/resizer.xml)
|
||||
content/global/bindings/richlistbox.xml (widgets/richlistbox.xml)
|
||||
content/global/bindings/scale.xml (widgets/scale.xml)
|
||||
content/global/bindings/scrollbar.xml (widgets/scrollbar.xml)
|
||||
content/global/bindings/scrollbox.xml (widgets/scrollbox.xml)
|
||||
content/global/bindings/spinner.js (widgets/spinner.js)
|
||||
content/global/bindings/splitter.xml (widgets/splitter.xml)
|
||||
content/global/bindings/spinbuttons.xml (widgets/spinbuttons.xml)
|
||||
content/global/bindings/stringbundle.xml (widgets/stringbundle.xml)
|
||||
* content/global/bindings/tabbox.xml (widgets/tabbox.xml)
|
||||
content/global/bindings/text.xml (widgets/text.xml)
|
||||
* content/global/bindings/textbox.xml (widgets/textbox.xml)
|
||||
content/global/bindings/timekeeper.js (widgets/timekeeper.js)
|
||||
content/global/bindings/timepicker.js (widgets/timepicker.js)
|
||||
content/global/bindings/toolbar.xml (widgets/toolbar.xml)
|
||||
content/global/bindings/toolbarbutton.xml (widgets/toolbarbutton.xml)
|
||||
* content/global/bindings/tree.xml (widgets/tree.xml)
|
||||
content/global/bindings/videocontrols.xml (widgets/videocontrols.xml)
|
||||
content/global/bindings/videocontrols.css (widgets/videocontrols.css)
|
||||
* content/global/bindings/wizard.xml (widgets/wizard.xml)
|
||||
#ifdef XP_MACOSX
|
||||
content/global/macWindowMenu.js
|
||||
#endif
|
||||
content/global/svg/svgBindings.xml (/layout/svg/resources/content/svgBindings.xml)
|
||||
content/global/gmp-sources/eme-adobe.json (gmp-sources/eme-adobe.json)
|
||||
content/global/gmp-sources/openh264.json (gmp-sources/openh264.json)
|
||||
content/global/gmp-sources/widevinecdm.json (gmp-sources/widevinecdm.json)
|
||||
5906
toolkit/content/license.html
Normal file
5906
toolkit/content/license.html
Normal file
File diff suppressed because it is too large
Load diff
40
toolkit/content/macWindowMenu.inc
Normal file
40
toolkit/content/macWindowMenu.inc
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
<script type="application/javascript" src="chrome://global/content/macWindowMenu.js"/>
|
||||
<commandset id="baseMenuCommandSet">
|
||||
<command id="minimizeWindow"
|
||||
label="&minimizeWindow.label;"
|
||||
oncommand="window.minimize();" />
|
||||
<command id="zoomWindow"
|
||||
label="&zoomWindow.label;"
|
||||
oncommand="zoomWindow();" />
|
||||
</commandset>
|
||||
<keyset id="baseMenuKeyset">
|
||||
<key id="key_minimizeWindow"
|
||||
command="minimizeWindow"
|
||||
key="&minimizeWindow.key;"
|
||||
modifiers="accel"/>
|
||||
</keyset>
|
||||
<menu id="windowMenu"
|
||||
label="&windowMenu.label;"
|
||||
datasources="rdf:window-mediator" ref="NC:WindowMediatorRoot"
|
||||
onpopupshowing="macWindowMenuDidShow();"
|
||||
hidden="false">
|
||||
<template>
|
||||
<rule>
|
||||
<menupopup>
|
||||
<menuitem uri="rdf:*"
|
||||
label="rdf:http://home.netscape.com/NC-rdf#Name"
|
||||
type="radio"
|
||||
name="windowList"
|
||||
oncommand="ShowWindowFromResource(event.target)"/>
|
||||
</menupopup>
|
||||
</rule>
|
||||
</template>
|
||||
<menupopup id="windowPopup">
|
||||
<menuitem command="minimizeWindow" key="key_minimizeWindow"/>
|
||||
<menuitem command="zoomWindow"/>
|
||||
<!-- decomment when "BringAllToFront" is implemented
|
||||
<menuseparator/>
|
||||
<menuitem label="&bringAllToFront.label;" disabled="true"/> -->
|
||||
<menuseparator id="sep-window-list"/>
|
||||
</menupopup>
|
||||
</menu>
|
||||
51
toolkit/content/macWindowMenu.js
Normal file
51
toolkit/content/macWindowMenu.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
|
||||
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
function macWindowMenuDidShow()
|
||||
{
|
||||
var windowManagerDS =
|
||||
Components.classes['@mozilla.org/rdf/datasource;1?name=window-mediator']
|
||||
.getService(Components.interfaces.nsIWindowDataSource);
|
||||
var sep = document.getElementById("sep-window-list");
|
||||
// Using double parens to avoid warning
|
||||
while ((sep = sep.nextSibling)) {
|
||||
var url = sep.getAttribute('id');
|
||||
var win = windowManagerDS.getWindowForResource(url);
|
||||
if (win.document.documentElement.getAttribute("inwindowmenu") == "false")
|
||||
sep.hidden = true;
|
||||
else if (win == window)
|
||||
sep.setAttribute("checked", "true");
|
||||
}
|
||||
}
|
||||
|
||||
function toOpenWindow( aWindow )
|
||||
{
|
||||
// deminiaturize the window, if it's in the Dock
|
||||
if (aWindow.windowState == STATE_MINIMIZED)
|
||||
aWindow.restore();
|
||||
aWindow.document.commandDispatcher.focusedWindow.focus();
|
||||
}
|
||||
|
||||
function ShowWindowFromResource( node )
|
||||
{
|
||||
var windowManagerDS =
|
||||
Components.classes['@mozilla.org/rdf/datasource;1?name=window-mediator']
|
||||
.getService(Components.interfaces.nsIWindowDataSource);
|
||||
|
||||
var desiredWindow = null;
|
||||
var url = node.getAttribute('id');
|
||||
desiredWindow = windowManagerDS.getWindowForResource( url );
|
||||
if (desiredWindow)
|
||||
toOpenWindow(desiredWindow);
|
||||
}
|
||||
|
||||
function zoomWindow()
|
||||
{
|
||||
if (window.windowState == STATE_NORMAL)
|
||||
window.maximize();
|
||||
else
|
||||
window.restore();
|
||||
}
|
||||
11
toolkit/content/menulist.css
Normal file
11
toolkit/content/menulist.css
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* 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/. */
|
||||
|
||||
@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
|
||||
|
||||
html|*.menulist-editable-input {
|
||||
-moz-appearance: none !important;
|
||||
background: transparent ! important;
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
133
toolkit/content/minimal-xul.css
Normal file
133
toolkit/content/minimal-xul.css
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
/* 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/. */
|
||||
|
||||
/**
|
||||
* This file should only contain a minimal set of rules for the XUL elements
|
||||
* that may be implicitly created as part of HTML/SVG documents (e.g.
|
||||
* scrollbars). Rules for everything else related to XUL can be found in
|
||||
* xul.css. (This split of the XUL rules is to minimize memory use and improve
|
||||
* performance in HTML/SVG documents.)
|
||||
*
|
||||
* This file should also not contain any app specific styling. Defaults for
|
||||
* widgets of a particular application should be in that application's style
|
||||
* sheet. For example style definitions for navigator can be found in
|
||||
* navigator.css.
|
||||
*
|
||||
* THIS FILE IS LOCKED DOWN. YOU ARE NOT ALLOWED TO MODIFY IT WITHOUT FIRST
|
||||
* HAVING YOUR CHANGES REVIEWED BY enndeakin@gmail.com
|
||||
*/
|
||||
|
||||
@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); /* set default namespace to XUL */
|
||||
@namespace html url("http://www.w3.org/1999/xhtml"); /* namespace for HTML elements */
|
||||
|
||||
* {
|
||||
-moz-user-focus: ignore;
|
||||
-moz-user-select: none;
|
||||
display: -moz-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
:root {
|
||||
text-rendering: optimizeLegibility;
|
||||
-moz-binding: url("chrome://global/content/bindings/general.xml#root-element");
|
||||
-moz-control-character-visibility: visible;
|
||||
}
|
||||
|
||||
:root:-moz-locale-dir(rtl) {
|
||||
direction: rtl;
|
||||
}
|
||||
|
||||
/* hide the content and destroy the frame */
|
||||
[hidden="true"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* hide the content, but don't destroy the frames */
|
||||
[collapsed="true"],
|
||||
[moz-collapsed="true"] {
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
/********** label **********/
|
||||
|
||||
description {
|
||||
-moz-binding: url("chrome://global/content/bindings/text.xml#text-base");
|
||||
}
|
||||
|
||||
label {
|
||||
-moz-binding: url("chrome://global/content/bindings/text.xml#text-label");
|
||||
}
|
||||
|
||||
label.text-link, label[onclick] {
|
||||
-moz-binding: url("chrome://global/content/bindings/text.xml#text-link");
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
label[control], label.radio-label, label.checkbox-label, label.toolbarbutton-multiline-text {
|
||||
-moz-binding: url("chrome://global/content/bindings/text.xml#label-control");
|
||||
}
|
||||
|
||||
html|span.accesskey {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/********** resizer **********/
|
||||
|
||||
resizer {
|
||||
-moz-binding: url("chrome://global/content/bindings/resizer.xml#resizer");
|
||||
position: relative;
|
||||
z-index: 2147483647;
|
||||
}
|
||||
|
||||
/********** scrollbar **********/
|
||||
|
||||
/* Scrollbars are never flipped even if BiDI kicks in. */
|
||||
scrollbar[orient="horizontal"] {
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
thumb {
|
||||
-moz-binding: url(chrome://global/content/bindings/scrollbar.xml#thumb);
|
||||
display: -moz-box !important;
|
||||
}
|
||||
|
||||
.scale-thumb {
|
||||
-moz-binding: url(chrome://global/content/bindings/scale.xml#scalethumb);
|
||||
}
|
||||
|
||||
scrollbar, scrollbarbutton, scrollcorner, slider, thumb, scale {
|
||||
-moz-user-select: none;
|
||||
}
|
||||
|
||||
scrollcorner {
|
||||
display: -moz-box !important;
|
||||
}
|
||||
|
||||
scrollcorner[hidden="true"] {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
scrollbar[value="hidden"] {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
scale {
|
||||
-moz-binding: url(chrome://global/content/bindings/scale.xml#scale);
|
||||
}
|
||||
|
||||
.scale-slider {
|
||||
-moz-binding: url(chrome://global/content/bindings/scale.xml#scaleslider);
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
scrollbarbutton[sbattr="scrollbar-up-top"]:not(:-moz-system-metric(scrollbar-start-backward)),
|
||||
scrollbarbutton[sbattr="scrollbar-down-top"]:not(:-moz-system-metric(scrollbar-start-forward)),
|
||||
scrollbarbutton[sbattr="scrollbar-up-bottom"]:not(:-moz-system-metric(scrollbar-end-backward)),
|
||||
scrollbarbutton[sbattr="scrollbar-down-bottom"]:not(:-moz-system-metric(scrollbar-end-forward)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
thumb[sbattr="scrollbar-thumb"]:-moz-system-metric(scrollbar-thumb-proportional) {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
34
toolkit/content/moz.build
Normal file
34
toolkit/content/moz.build
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
TEST_DIRS += ['tests']
|
||||
|
||||
for var in ('target', 'MOZ_CONFIGURE_OPTIONS', 'CC', 'CC_VERSION', 'CXX'):
|
||||
DEFINES[var] = CONFIG[var]
|
||||
|
||||
DEFINES['CFLAGS'] = CONFIG['OS_CFLAGS']
|
||||
|
||||
if CONFIG['OS_TARGET'] == 'Android':
|
||||
DEFINES['ANDROID_PACKAGE_NAME'] = CONFIG['ANDROID_PACKAGE_NAME']
|
||||
|
||||
if CONFIG['MOZ_ANDROID_CXX_STL'] == 'libc++':
|
||||
DEFINES['MOZ_USE_LIBCXX'] = True
|
||||
|
||||
if CONFIG['MOZ_BUILD_APP'] == 'mobile/android':
|
||||
DEFINES['MOZ_FENNEC'] = True
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
with Files('aboutTelemetry.*'):
|
||||
BUG_COMPONENT = ('Toolkit', 'Telemetry')
|
||||
|
||||
with Files('customizeToolbar.*'):
|
||||
BUG_COMPONENT = ('Toolkit', 'Toolbars and Toolbar Customization')
|
||||
|
||||
with Files('widgets/*'):
|
||||
BUG_COMPONENT = ('Toolkit', 'XUL Widgets')
|
||||
|
||||
DEFINES['TOPOBJDIR'] = TOPOBJDIR
|
||||
66
toolkit/content/mozilla.xhtml
Normal file
66
toolkit/content/mozilla.xhtml
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
<!DOCTYPE html
|
||||
[
|
||||
<!ENTITY % mozillaDTD SYSTEM "chrome://global/locale/mozilla.dtd" >
|
||||
%mozillaDTD;
|
||||
<!ENTITY % directionDTD SYSTEM "chrome://global/locale/global.dtd" >
|
||||
%directionDTD;
|
||||
]>
|
||||
|
||||
<!-- 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>
|
||||
<meta charset='utf-8' />
|
||||
<title>&mozilla.title.15.1;</title>
|
||||
|
||||
<style>
|
||||
html {
|
||||
background: maroon radial-gradient( circle, #a01010 0%, #800000 80%) center center / cover no-repeat;
|
||||
color: white;
|
||||
font-style: italic;
|
||||
text-rendering: optimizeLegibility;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
#moztext {
|
||||
margin-top: 15%;
|
||||
font-size: 1.1em;
|
||||
font-family: serif;
|
||||
text-align: center;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
#from {
|
||||
font-size: 1.95em;
|
||||
font-family: serif;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
em {
|
||||
font-size: 1.3em;
|
||||
line-height: 0;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body dir="&locale.dir;">
|
||||
|
||||
<section>
|
||||
<p id="moztext">
|
||||
&mozilla.quote.15.1;
|
||||
</p>
|
||||
|
||||
<p id="from">
|
||||
&mozilla.from.15.1;
|
||||
</p>
|
||||
</section>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
88
toolkit/content/plugins.css
Normal file
88
toolkit/content/plugins.css
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
/* 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/. */
|
||||
|
||||
/* ===== plugins.css =====================================================
|
||||
== Styles used by the about:plugins page.
|
||||
======================================================================= */
|
||||
|
||||
body {
|
||||
background-color: -moz-Field;
|
||||
color: -moz-FieldText;
|
||||
font: message-box;
|
||||
}
|
||||
|
||||
div#outside {
|
||||
text-align: justify;
|
||||
width: 90%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
}
|
||||
|
||||
#plugs {
|
||||
text-align: center;
|
||||
font-size: xx-large;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#noplugs {
|
||||
font-size: x-large;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.plugname {
|
||||
margin-top: 2em;
|
||||
margin-bottom: 1em;
|
||||
font-size: large;
|
||||
text-align: start;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin: 0px 0px 3px 0px;
|
||||
}
|
||||
|
||||
table {
|
||||
background-color: -moz-Dialog;
|
||||
color: -moz-DialogText;
|
||||
font: message-box;
|
||||
text-align: start;
|
||||
width: 100%;
|
||||
border: 1px solid ThreeDShadow;
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: none;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: center;
|
||||
background-color: Highlight;
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
th + th,
|
||||
td + td {
|
||||
border-inline-start: 1px dotted ThreeDShadow;
|
||||
}
|
||||
|
||||
td {
|
||||
text-align: start;
|
||||
border-top: 1px dotted ThreeDShadow;
|
||||
}
|
||||
|
||||
th.type, th.suff {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
th.desc {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.notice {
|
||||
background: -moz-cellhighlight;
|
||||
border: 1px solid ThreeDShadow;
|
||||
padding: 10px;
|
||||
}
|
||||
217
toolkit/content/plugins.html
Normal file
217
toolkit/content/plugins.html
Normal file
|
|
@ -0,0 +1,217 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
|
||||
|
||||
<!-- 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>
|
||||
<head>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
var Ci = Components.interfaces;
|
||||
var strBundleService = Components.classes["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
|
||||
var pluginsbundle = strBundleService.createBundle("chrome://global/locale/plugins.properties");
|
||||
|
||||
document.writeln("<title>" + pluginsbundle.GetStringFromName("title_label") + "<\/title>");
|
||||
</script>
|
||||
<link rel="stylesheet" type="text/css" href="chrome://global/content/plugins.css">
|
||||
<link rel="stylesheet" type="text/css" href="chrome://global/skin/plugins.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="outside">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
|
||||
function setDirection() {
|
||||
var frame = document.getElementById("directionDetector");
|
||||
var direction = frame.contentDocument
|
||||
.defaultView
|
||||
.window
|
||||
.getComputedStyle(frame.contentDocument.getElementById("target"), "")
|
||||
.getPropertyValue("direction");
|
||||
document.body.removeChild(frame);
|
||||
document.dir = direction;
|
||||
}
|
||||
|
||||
function setupDirection() {
|
||||
var frame = document.createElement("iframe");
|
||||
frame.setAttribute("id", "directionDetector");
|
||||
frame.setAttribute("src", "chrome://global/content/directionDetector.html");
|
||||
frame.setAttribute("width", "0");
|
||||
frame.setAttribute("height", "0");
|
||||
frame.setAttribute("style", "visibility: hidden;");
|
||||
frame.setAttribute("onload", "setDirection();");
|
||||
document.body.appendChild(frame);
|
||||
}
|
||||
setupDirection();
|
||||
|
||||
/* JavaScript to enumerate and display all installed plug-ins
|
||||
|
||||
* First, refresh plugins in case anything has been changed recently in
|
||||
* prefs: (The "false" argument tells refresh not to reload or activate
|
||||
* any plug-ins that would be active otherwise. In contrast, one would
|
||||
* use "true" in the case of ASD instead of restarting)
|
||||
*/
|
||||
navigator.plugins.refresh(false);
|
||||
|
||||
addMessageListener("PluginList", function({ data: aPlugins }) {
|
||||
var fragment = document.createDocumentFragment();
|
||||
|
||||
// "Installed plugins"
|
||||
var id, label;
|
||||
if (aPlugins.length > 0) {
|
||||
id = "plugs";
|
||||
label = "installedplugins_label";
|
||||
} else {
|
||||
id = "noplugs";
|
||||
label = "nopluginsareinstalled_label";
|
||||
}
|
||||
var enabledplugins = document.createElement("h1");
|
||||
enabledplugins.setAttribute("id", id);
|
||||
enabledplugins.appendChild(document.createTextNode(pluginsbundle.GetStringFromName(label)));
|
||||
fragment.appendChild(enabledplugins);
|
||||
|
||||
var deprecation = document.createElement("p");
|
||||
deprecation.setAttribute("class", "notice");
|
||||
deprecation.textContent = pluginsbundle.GetStringFromName("deprecation_description") + " \u00A0 ";
|
||||
var deprecationLink = document.createElement("a");
|
||||
deprecationLink.textContent = pluginsbundle.GetStringFromName("deprecation_learn_more");
|
||||
deprecationLink.href = Services.urlFormatter.formatURLPref("app.support.baseURL") + "npapi";
|
||||
deprecation.appendChild(deprecationLink);
|
||||
fragment.appendChild(deprecation);
|
||||
|
||||
var stateNames = {};
|
||||
["STATE_SOFTBLOCKED",
|
||||
"STATE_BLOCKED",
|
||||
"STATE_OUTDATED",
|
||||
"STATE_VULNERABLE_UPDATE_AVAILABLE",
|
||||
"STATE_VULNERABLE_NO_UPDATE"].forEach(function(label) {
|
||||
stateNames[Ci.nsIBlocklistService[label]] = label;
|
||||
});
|
||||
|
||||
for (var i = 0; i < aPlugins.length; i++) {
|
||||
var plugin = aPlugins[i];
|
||||
if (plugin) {
|
||||
// "Shockwave Flash"
|
||||
var plugname = document.createElement("h2");
|
||||
plugname.setAttribute("class", "plugname");
|
||||
plugname.appendChild(document.createTextNode(plugin.name));
|
||||
fragment.appendChild(plugname);
|
||||
|
||||
var dl = document.createElement("dl");
|
||||
fragment.appendChild(dl);
|
||||
|
||||
// "File: Flash Player.plugin"
|
||||
var fileDd = document.createElement("dd");
|
||||
var file = document.createElement("span");
|
||||
file.setAttribute("class", "label");
|
||||
file.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("file_label") + " "));
|
||||
fileDd.appendChild(file);
|
||||
fileDd.appendChild(document.createTextNode(plugin.pluginLibraries));
|
||||
dl.appendChild(fileDd);
|
||||
|
||||
// "Path: /usr/lib/mozilla/plugins/libtotem-cone-plugin.so"
|
||||
var pathDd = document.createElement("dd");
|
||||
var path = document.createElement("span");
|
||||
path.setAttribute("class", "label");
|
||||
path.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("path_label") + " "));
|
||||
pathDd.appendChild(path);
|
||||
pathDd.appendChild(document.createTextNode(plugin.pluginFullpath));
|
||||
dl.appendChild(pathDd);
|
||||
|
||||
// "Version: "
|
||||
var versionDd = document.createElement("dd");
|
||||
var version = document.createElement("span");
|
||||
version.setAttribute("class", "label");
|
||||
version.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("version_label") + " "));
|
||||
versionDd.appendChild(version);
|
||||
versionDd.appendChild(document.createTextNode(plugin.version));
|
||||
dl.appendChild(versionDd);
|
||||
|
||||
// "State: "
|
||||
var stateDd = document.createElement("dd");
|
||||
var state = document.createElement("span");
|
||||
state.setAttribute("label", "state");
|
||||
state.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("state_label") + " "));
|
||||
stateDd.appendChild(state);
|
||||
var status = plugin.isActive ? pluginsbundle.GetStringFromName("state_enabled") : pluginsbundle.GetStringFromName("state_disabled");
|
||||
if (plugin.blocklistState in stateNames) {
|
||||
status += " (" + stateNames[plugin.blocklistState] + ")";
|
||||
}
|
||||
stateDd.appendChild(document.createTextNode(status));
|
||||
dl.appendChild(stateDd);
|
||||
|
||||
// Plugin Description
|
||||
var descDd = document.createElement("dd");
|
||||
descDd.appendChild(document.createTextNode(plugin.description));
|
||||
dl.appendChild(descDd);
|
||||
|
||||
// MIME Type table
|
||||
var mimetypeTable = document.createElement("table");
|
||||
mimetypeTable.setAttribute("border", "1");
|
||||
mimetypeTable.setAttribute("class", "contenttable");
|
||||
fragment.appendChild(mimetypeTable);
|
||||
|
||||
var thead = document.createElement("thead");
|
||||
mimetypeTable.appendChild(thead);
|
||||
var tr = document.createElement("tr");
|
||||
thead.appendChild(tr);
|
||||
|
||||
// "MIME Type" column header
|
||||
var typeTh = document.createElement("th");
|
||||
typeTh.setAttribute("class", "type");
|
||||
typeTh.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("mimetype_label")));
|
||||
tr.appendChild(typeTh);
|
||||
|
||||
// "Description" column header
|
||||
var descTh = document.createElement("th");
|
||||
descTh.setAttribute("class", "desc");
|
||||
descTh.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("description_label")));
|
||||
tr.appendChild(descTh);
|
||||
|
||||
// "Suffixes" column header
|
||||
var suffixesTh = document.createElement("th");
|
||||
suffixesTh.setAttribute("class", "suff");
|
||||
suffixesTh.appendChild(document.createTextNode(pluginsbundle.GetStringFromName("suffixes_label")));
|
||||
tr.appendChild(suffixesTh);
|
||||
|
||||
var tbody = document.createElement("tbody");
|
||||
mimetypeTable.appendChild(tbody);
|
||||
|
||||
var mimeTypes = plugin.pluginMimeTypes;
|
||||
for (var j = 0; j < mimeTypes.length; j++) {
|
||||
var mimetype = mimeTypes[j];
|
||||
if (mimetype) {
|
||||
var mimetypeRow = document.createElement("tr");
|
||||
tbody.appendChild(mimetypeRow);
|
||||
|
||||
// "application/x-shockwave-flash"
|
||||
var typename = document.createElement("td");
|
||||
typename.appendChild(document.createTextNode(mimetype.type));
|
||||
mimetypeRow.appendChild(typename);
|
||||
|
||||
// "Shockwave Flash"
|
||||
var description = document.createElement("td");
|
||||
description.appendChild(document.createTextNode(mimetype.description));
|
||||
mimetypeRow.appendChild(description);
|
||||
|
||||
// "swf"
|
||||
var suffixes = document.createElement("td");
|
||||
suffixes.appendChild(document.createTextNode(mimetype.suffixes));
|
||||
mimetypeRow.appendChild(suffixes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById("outside").appendChild(fragment);
|
||||
});
|
||||
|
||||
sendAsyncMessage("RequestPlugins");
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
84
toolkit/content/process-content.js
Normal file
84
toolkit/content/process-content.js
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/* 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";
|
||||
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
// Creates a new PageListener for this process. This will listen for page loads
|
||||
// and for those that match URLs provided by the parent process will set up
|
||||
// a dedicated message port and notify the parent process.
|
||||
Cu.import("resource://gre/modules/RemotePageManager.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const gInContentProcess = Services.appinfo.processType == Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT;
|
||||
|
||||
Services.cpmm.addMessageListener("gmp-plugin-crash", msg => {
|
||||
let gmpservice = Cc["@mozilla.org/gecko-media-plugin-service;1"]
|
||||
.getService(Ci.mozIGeckoMediaPluginService);
|
||||
|
||||
gmpservice.RunPluginCrashCallbacks(msg.data.pluginID, msg.data.pluginName);
|
||||
});
|
||||
|
||||
if (gInContentProcess) {
|
||||
let ProcessObserver = {
|
||||
TOPICS: [
|
||||
"inner-window-destroyed",
|
||||
"xpcom-shutdown",
|
||||
],
|
||||
|
||||
init() {
|
||||
for (let topic of this.TOPICS) {
|
||||
Services.obs.addObserver(this, topic, false);
|
||||
Services.cpmm.addMessageListener("Memory:GetSummary", this);
|
||||
}
|
||||
},
|
||||
|
||||
uninit() {
|
||||
for (let topic of this.TOPICS) {
|
||||
Services.obs.removeObserver(this, topic);
|
||||
Services.cpmm.removeMessageListener("Memory:GetSummary", this);
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage(msg) {
|
||||
if (msg.name != "Memory:GetSummary") {
|
||||
return;
|
||||
}
|
||||
let pid = Services.appinfo.processID;
|
||||
let memMgr = Cc["@mozilla.org/memory-reporter-manager;1"]
|
||||
.getService(Ci.nsIMemoryReporterManager);
|
||||
let rss = memMgr.resident;
|
||||
let uss = memMgr.residentUnique;
|
||||
Services.cpmm.sendAsyncMessage("Memory:Summary", {
|
||||
pid,
|
||||
summary: {
|
||||
uss,
|
||||
rss,
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "inner-window-destroyed": {
|
||||
// Forward inner-window-destroyed notifications with the
|
||||
// inner window ID, so that code in the parent that should
|
||||
// do something when content windows go away can do it
|
||||
let innerWindowID =
|
||||
subject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||
Services.cpmm.sendAsyncMessage("Toolkit:inner-window-destroyed",
|
||||
innerWindowID);
|
||||
break;
|
||||
}
|
||||
case "xpcom-shutdown": {
|
||||
this.uninit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
ProcessObserver.init();
|
||||
}
|
||||
15
toolkit/content/resetProfile.css
Normal file
15
toolkit/content/resetProfile.css
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/* 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/. */
|
||||
|
||||
#migratedItems {
|
||||
margin-inline-start: 1.5em;
|
||||
}
|
||||
|
||||
#resetProfileFooter {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#resetProfileProgressDialog {
|
||||
padding: 10px;
|
||||
}
|
||||
20
toolkit/content/resetProfile.js
Normal file
20
toolkit/content/resetProfile.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/* 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";
|
||||
|
||||
// NB: this file can be loaded from aboutSupport.xhtml or from the
|
||||
// resetProfile.xul dialog, and so Cu may or may not exist already.
|
||||
// Proceed with caution:
|
||||
if (!("Cu" in window)) {
|
||||
window.Cu = Components.utils;
|
||||
}
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/ResetProfile.jsm");
|
||||
|
||||
function onResetProfileAccepted() {
|
||||
let retVals = window.arguments[0];
|
||||
retVals.reset = true;
|
||||
}
|
||||
35
toolkit/content/resetProfile.xul
Normal file
35
toolkit/content/resetProfile.xul
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE prefwindow [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
||||
%brandDTD;
|
||||
<!ENTITY % resetProfileDTD SYSTEM "chrome://global/locale/resetProfile.dtd" >
|
||||
%resetProfileDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/skin/"?>
|
||||
<?xml-stylesheet href="chrome://global/content/resetProfile.css"?>
|
||||
|
||||
<dialog id="resetProfileDialog"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&refreshProfile.dialog.title;"
|
||||
buttons="accept,cancel"
|
||||
defaultButton="cancel"
|
||||
buttonlabelaccept="&refreshProfile.dialog.button.label;"
|
||||
ondialogaccept="return onResetProfileAccepted();"
|
||||
ondialogcancel="window.close();">
|
||||
|
||||
<script type="application/javascript" src="chrome://global/content/resetProfile.js"/>
|
||||
|
||||
<description value="&refreshProfile.dialog.description1;"></description>
|
||||
<label value="&refreshProfile.dialog.description2;"/>
|
||||
|
||||
<vbox id="migratedItems">
|
||||
<label class="migratedLabel" value="&refreshProfile.dialog.items.label1;"/>
|
||||
<label class="migratedLabel" value="&refreshProfile.dialog.items.label2;"/>
|
||||
</vbox>
|
||||
</dialog>
|
||||
25
toolkit/content/resetProfileProgress.xul
Normal file
25
toolkit/content/resetProfileProgress.xul
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<!DOCTYPE window [
|
||||
<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
|
||||
%brandDTD;
|
||||
<!ENTITY % resetProfileDTD SYSTEM "chrome://global/locale/resetProfile.dtd" >
|
||||
%resetProfileDTD;
|
||||
]>
|
||||
|
||||
<?xml-stylesheet href="chrome://global/content/resetProfile.css"?>
|
||||
<?xml-stylesheet href="chrome://global/skin/"?>
|
||||
|
||||
<window id="resetProfileProgressDialog"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
title="&refreshProfile.dialog.title;"
|
||||
style="min-width: 300px;">
|
||||
<vbox>
|
||||
<description>&refreshProfile.cleaning.description;</description>
|
||||
<progressmeter mode="undetermined"/>
|
||||
</vbox>
|
||||
</window>
|
||||
26
toolkit/content/select-child.js
Normal file
26
toolkit/content/select-child.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/* 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/. */
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "SelectContentHelper",
|
||||
"resource://gre/modules/SelectContentHelper.jsm");
|
||||
|
||||
addEventListener("mozshowdropdown", event => {
|
||||
if (!event.isTrusted)
|
||||
return;
|
||||
|
||||
if (!SelectContentHelper.open) {
|
||||
new SelectContentHelper(event.target, {isOpenedViaTouch: false}, this);
|
||||
}
|
||||
});
|
||||
|
||||
addEventListener("mozshowdropdown-sourcetouch", event => {
|
||||
if (!event.isTrusted)
|
||||
return;
|
||||
|
||||
if (!SelectContentHelper.open) {
|
||||
new SelectContentHelper(event.target, {isOpenedViaTouch: true}, this);
|
||||
}
|
||||
});
|
||||
7
toolkit/content/tests/browser/.eslintrc.js
Normal file
7
toolkit/content/tests/browser/.eslintrc.js
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
"use strict";
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"../../../../testing/mochitest/browser.eslintrc.js"
|
||||
]
|
||||
};
|
||||
BIN
toolkit/content/tests/browser/audio.ogg
Normal file
BIN
toolkit/content/tests/browser/audio.ogg
Normal file
Binary file not shown.
75
toolkit/content/tests/browser/browser.ini
Normal file
75
toolkit/content/tests/browser/browser.ini
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
[DEFAULT]
|
||||
support-files =
|
||||
head.js
|
||||
file_contentTitle.html
|
||||
audio.ogg
|
||||
|
||||
[browser_audioCompeting.js]
|
||||
tags = audiochannel
|
||||
support-files =
|
||||
file_multipleAudio.html
|
||||
[browser_audioCompeting_onlyForActiveAgent.js]
|
||||
tags = audiochannel
|
||||
support-files =
|
||||
file_multiplePlayingAudio.html
|
||||
[browser_autoscroll_disabled.js]
|
||||
[browser_block_autoplay_media.js]
|
||||
tags = audiochannel
|
||||
support-files =
|
||||
file_multipleAudio.html
|
||||
[browser_bug295977_autoscroll_overflow.js]
|
||||
[browser_bug451286.js]
|
||||
skip-if = !e10s
|
||||
[browser_bug594509.js]
|
||||
[browser_bug982298.js]
|
||||
[browser_bug1198465.js]
|
||||
[browser_contentTitle.js]
|
||||
[browser_crash_previous_frameloader.js]
|
||||
run-if = e10s && crashreporter
|
||||
[browser_default_image_filename.js]
|
||||
[browser_f7_caret_browsing.js]
|
||||
[browser_findbar.js]
|
||||
[browser_label_textlink.js]
|
||||
[browser_isSynthetic.js]
|
||||
support-files =
|
||||
empty.png
|
||||
[browser_keyevents_during_autoscrolling.js]
|
||||
[browser_save_resend_postdata.js]
|
||||
support-files =
|
||||
common/mockTransfer.js
|
||||
data/post_form_inner.sjs
|
||||
data/post_form_outer.sjs
|
||||
skip-if = e10s # Bug ?????? - test directly manipulates content (gBrowser.contentDocument.getElementById("postForm").submit();)
|
||||
[browser_content_url_annotation.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
support-files =
|
||||
file_redirect.html
|
||||
file_redirect_to.html
|
||||
[browser_bug1170531.js]
|
||||
[browser_mediaPlayback.js]
|
||||
tags = audiochannel
|
||||
support-files =
|
||||
file_mediaPlayback.html
|
||||
file_mediaPlaybackFrame.html
|
||||
[browser_mediaPlayback_mute.js]
|
||||
tags = audiochannel
|
||||
support-files =
|
||||
file_mediaPlayback2.html
|
||||
file_mediaPlaybackFrame2.html
|
||||
[browser_mediaPlayback_suspended.js]
|
||||
tags = audiochannel
|
||||
support-files =
|
||||
file_mediaPlayback2.html
|
||||
[browser_mediaPlayback_suspended_multipleAudio.js]
|
||||
tags = audiochannel
|
||||
support-files =
|
||||
file_multipleAudio.html
|
||||
[browser_mute.js]
|
||||
tags = audiochannel
|
||||
[browser_mute2.js]
|
||||
tags = audiochannel
|
||||
[browser_quickfind_editable.js]
|
||||
[browser_saveImageURL.js]
|
||||
support-files =
|
||||
image.jpg
|
||||
image_page.html
|
||||
115
toolkit/content/tests/browser/browser_audioCompeting.js
Normal file
115
toolkit/content/tests/browser/browser_audioCompeting.js
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html";
|
||||
|
||||
function* wait_for_tab_playing_event(tab, expectPlaying) {
|
||||
if (tab.soundPlaying == expectPlaying) {
|
||||
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
} else {
|
||||
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("soundplaying") >= 0) {
|
||||
is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function play_audio_from_invisible_tab () {
|
||||
return new Promise(resolve => {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
if (!autoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.paused, true, "Audio in tab 1 was paused by audio competing.");
|
||||
autoPlay.play();
|
||||
autoPlay.onpause = function() {
|
||||
autoPlay.onpause = null;
|
||||
ok(true, "Audio in tab 1 can't playback when other tab is playing in foreground.");
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function audio_should_keep_playing_even_go_to_background () {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
if (!autoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.paused, false, "Audio in tab 2 is still playing in the background.");
|
||||
}
|
||||
|
||||
function play_non_autoplay_audio () {
|
||||
return new Promise(resolve => {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!autoPlay || !nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(nonAutoPlay.paused, true, "Non-autoplay audio isn't started playing yet.");
|
||||
nonAutoPlay.play();
|
||||
|
||||
nonAutoPlay.onplay = function() {
|
||||
nonAutoPlay.onplay = null;
|
||||
is(nonAutoPlay.paused, false, "Start Non-autoplay audio.");
|
||||
is(autoPlay.paused, false, "Autoplay audio is still playing.");
|
||||
resolve();
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* setup_test_preference() {
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["dom.audiochannel.audioCompeting", true],
|
||||
["dom.ipc.processCount", 1]
|
||||
]}, resolve);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* cross_tabs_audio_competing () {
|
||||
info("- open tab 1 in foreground -");
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
tab1.linkedBrowser.loadURI(PAGE);
|
||||
yield wait_for_tab_playing_event(tab1, true);
|
||||
|
||||
info("- open tab 2 in foreground -");
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
tab2.linkedBrowser.loadURI(PAGE);
|
||||
yield wait_for_tab_playing_event(tab1, false);
|
||||
|
||||
info("- open tab 3 in foreground -");
|
||||
let tab3 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
yield ContentTask.spawn(tab2.linkedBrowser, null,
|
||||
audio_should_keep_playing_even_go_to_background);
|
||||
|
||||
info("- play audio from background tab 1 -");
|
||||
yield ContentTask.spawn(tab1.linkedBrowser, null,
|
||||
play_audio_from_invisible_tab);
|
||||
|
||||
info("- remove tabs -");
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
yield BrowserTestUtils.removeTab(tab2);
|
||||
yield BrowserTestUtils.removeTab(tab3);
|
||||
});
|
||||
|
||||
add_task(function* within_one_tab_audio_competing () {
|
||||
info("- open tab and play audio1 -");
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
tab.linkedBrowser.loadURI(PAGE);
|
||||
yield wait_for_tab_playing_event(tab, true);
|
||||
|
||||
info("- play audio2 in the same tab -");
|
||||
yield ContentTask.spawn(tab.linkedBrowser, null,
|
||||
play_non_autoplay_audio);
|
||||
|
||||
info("- remove tab -");
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multiplePlayingAudio.html";
|
||||
|
||||
var SuspendedType = {
|
||||
NONE_SUSPENDED : 0,
|
||||
SUSPENDED_PAUSE : 1,
|
||||
SUSPENDED_BLOCK : 2,
|
||||
SUSPENDED_PAUSE_DISPOSABLE : 3
|
||||
};
|
||||
|
||||
function wait_for_event(browser, event) {
|
||||
return BrowserTestUtils.waitForEvent(browser, event, false, (event) => {
|
||||
is(event.originalTarget, browser, "Event must be dispatched to correct browser.");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function check_all_audio_suspended(suspendedType) {
|
||||
var audio1 = content.document.getElementById("audio1");
|
||||
var audio2 = content.document.getElementById("audio2");
|
||||
if (!audio1 || !audio2) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio1.computedSuspended, suspendedType,
|
||||
"The suspeded state of audio1 is correct.");
|
||||
is(audio2.computedSuspended, suspendedType,
|
||||
"The suspeded state of audio2 is correct.");
|
||||
}
|
||||
|
||||
function check_audio1_suspended(suspendedType) {
|
||||
var audio1 = content.document.getElementById("audio1");
|
||||
if (!audio1) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio1.computedSuspended, suspendedType,
|
||||
"The suspeded state of audio1 is correct.");
|
||||
}
|
||||
|
||||
function check_audio2_suspended(suspendedType) {
|
||||
var audio2 = content.document.getElementById("audio2");
|
||||
if (!audio2) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio2.computedSuspended, suspendedType,
|
||||
"The suspeded state of audio2 is correct.");
|
||||
}
|
||||
|
||||
function check_all_audio_pause_state(expectedPauseState) {
|
||||
var audio1 = content.document.getElementById("audio1");
|
||||
var audio2 = content.document.getElementById("audio2");
|
||||
if (!audio1 | !audio2) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio1.paused, expectedPauseState,
|
||||
"The pause state of audio1 is correct.");
|
||||
is(audio2.paused, expectedPauseState,
|
||||
"The pause state of audio2 is correct.");
|
||||
}
|
||||
|
||||
function check_audio1_pause_state(expectedPauseState) {
|
||||
var audio1 = content.document.getElementById("audio1");
|
||||
if (!audio1) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio1.paused, expectedPauseState,
|
||||
"The pause state of audio1 is correct.");
|
||||
}
|
||||
|
||||
function check_audio2_pause_state(expectedPauseState) {
|
||||
var audio2 = content.document.getElementById("audio2");
|
||||
if (!audio2) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio2.paused, expectedPauseState,
|
||||
"The pause state of audio2 is correct.");
|
||||
}
|
||||
|
||||
function play_audio1_from_page() {
|
||||
var audio1 = content.document.getElementById("audio1");
|
||||
if (!audio1) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio1.paused, true, "Audio1 is paused.");
|
||||
audio1.play();
|
||||
return new Promise(resolve => {
|
||||
audio1.onplay = function() {
|
||||
audio1.onplay = null;
|
||||
ok(true, "Audio1 started playing.");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function stop_audio1_from_page() {
|
||||
var audio1 = content.document.getElementById("audio1");
|
||||
if (!audio1) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(audio1.paused, false, "Audio1 is playing.");
|
||||
audio1.pause();
|
||||
return new Promise(resolve => {
|
||||
audio1.onpause = function() {
|
||||
audio1.onpause = null;
|
||||
ok(true, "Audio1 stopped playing.");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function* audio_competing_for_active_agent(url, browser) {
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the default suspended state of all audio should be non-suspened -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- only pause playing audio in the page -");
|
||||
browser.pauseMedia(true /* disposable */);
|
||||
|
||||
info("- page shouldn't have any playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStopped");
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_all_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE_DISPOSABLE,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- resume audio1 from page -");
|
||||
yield ContentTask.spawn(browser, null,
|
||||
play_audio1_from_page);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio1_suspended);
|
||||
|
||||
info("- audio2 should still be suspended -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE_DISPOSABLE,
|
||||
check_audio2_suspended);
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_audio2_pause_state);
|
||||
|
||||
info("- stop audio1 from page -");
|
||||
yield ContentTask.spawn(browser, null,
|
||||
stop_audio1_from_page);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio1_suspended);
|
||||
|
||||
info("- audio2 should still be suspended -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE_DISPOSABLE,
|
||||
check_audio2_suspended);
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_audio2_pause_state);
|
||||
|
||||
}
|
||||
|
||||
add_task(function* setup_test_preference() {
|
||||
yield SpecialPowers.pushPrefEnv({"set": [
|
||||
["media.useAudioChannelService.testing", true],
|
||||
["dom.audiochannel.audioCompeting", true],
|
||||
["dom.audiochannel.audioCompeting.allAgents", true]
|
||||
]});
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_pause_disposable() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, audio_competing_for_active_agent.bind(this, PAGE));
|
||||
});
|
||||
67
toolkit/content/tests/browser/browser_autoscroll_disabled.js
Normal file
67
toolkit/content/tests/browser/browser_autoscroll_disabled.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
add_task(function* ()
|
||||
{
|
||||
const kPrefName_AutoScroll = "general.autoScroll";
|
||||
Services.prefs.setBoolPref(kPrefName_AutoScroll, false);
|
||||
|
||||
let dataUri = 'data:text/html,<html><body id="i" style="overflow-y: scroll"><div style="height: 2000px"></div>\
|
||||
<iframe id="iframe" style="display: none;"></iframe>\
|
||||
</body></html>';
|
||||
|
||||
let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
gBrowser.loadURI(dataUri);
|
||||
yield loadedPromise;
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouse("#i", 50, 50, { button: 1 },
|
||||
gBrowser.selectedBrowser);
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
|
||||
var iframe = content.document.getElementById("iframe");
|
||||
|
||||
if (iframe) {
|
||||
var e = new iframe.contentWindow.PageTransitionEvent("pagehide",
|
||||
{ bubbles: true,
|
||||
cancelable: true,
|
||||
persisted: false });
|
||||
iframe.contentDocument.dispatchEvent(e);
|
||||
iframe.contentDocument.documentElement.dispatchEvent(e);
|
||||
}
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouse("#i", 100, 100,
|
||||
{ type: "mousemove", clickCount: "0" },
|
||||
gBrowser.selectedBrowser);
|
||||
|
||||
// If scrolling didn't work, we wouldn't do any redraws and thus time out, so
|
||||
// request and force redraws to get the chance to check for scrolling at all.
|
||||
yield new Promise(resolve => window.requestAnimationFrame(resolve));
|
||||
|
||||
let msg = yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
|
||||
// Skip the first animation frame callback as it's the same callback that
|
||||
// the browser uses to kick off the scrolling.
|
||||
return new Promise(resolve => {
|
||||
function checkScroll() {
|
||||
let msg = "";
|
||||
let elem = content.document.getElementById('i');
|
||||
if (elem.scrollTop != 0) {
|
||||
msg += "element should not have scrolled vertically";
|
||||
}
|
||||
if (elem.scrollLeft != 0) {
|
||||
msg += "element should not have scrolled horizontally";
|
||||
}
|
||||
|
||||
resolve(msg);
|
||||
}
|
||||
|
||||
content.requestAnimationFrame(checkScroll);
|
||||
});
|
||||
});
|
||||
|
||||
ok(!msg, "element scroll " + msg);
|
||||
|
||||
// restore the changed prefs
|
||||
if (Services.prefs.prefHasUserValue(kPrefName_AutoScroll))
|
||||
Services.prefs.clearUserPref(kPrefName_AutoScroll);
|
||||
|
||||
// wait for focus to fix a failure in the next test if the latter runs too soon.
|
||||
yield SimpleTest.promiseFocus();
|
||||
});
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html";
|
||||
|
||||
var SuspendedType = {
|
||||
NONE_SUSPENDED : 0,
|
||||
SUSPENDED_PAUSE : 1,
|
||||
SUSPENDED_BLOCK : 2,
|
||||
SUSPENDED_PAUSE_DISPOSABLE : 3
|
||||
};
|
||||
|
||||
function* wait_for_tab_playing_event(tab, expectPlaying) {
|
||||
if (tab.soundPlaying == expectPlaying) {
|
||||
ok(true, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
} else {
|
||||
yield BrowserTestUtils.waitForEvent(tab, "TabAttrModified", false, (event) => {
|
||||
if (event.detail.changed.indexOf("soundplaying") >= 0) {
|
||||
is(tab.soundPlaying, expectPlaying, "The tab should " + (expectPlaying ? "" : "not ") + "be playing");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function check_audio_suspended(suspendedType) {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
if (!autoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.computedSuspended, suspendedType,
|
||||
"The suspeded state of autoplay audio is correct.");
|
||||
}
|
||||
|
||||
add_task(function* setup_test_preference() {
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["media.useAudioChannelService.testing", true],
|
||||
["media.block-autoplay-until-in-foreground", true]
|
||||
]}, resolve);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* block_autoplay_media() {
|
||||
info("- open new background tab1 -");
|
||||
let tab1 = window.gBrowser.addTab("about:blank");
|
||||
tab1.linkedBrowser.loadURI(PAGE);
|
||||
yield BrowserTestUtils.browserLoaded(tab1.linkedBrowser);
|
||||
|
||||
info("- should block autoplay media for non-visited tab1 -");
|
||||
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- open new background tab2 -");
|
||||
let tab2 = window.gBrowser.addTab("about:blank");
|
||||
tab2.linkedBrowser.loadURI(PAGE);
|
||||
yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
|
||||
|
||||
info("- should block autoplay for non-visited tab2 -");
|
||||
yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- select tab1 as foreground tab -");
|
||||
yield BrowserTestUtils.switchTab(window.gBrowser, tab1);
|
||||
|
||||
info("- media should be unblocked because the tab was visited -");
|
||||
yield wait_for_tab_playing_event(tab1, true);
|
||||
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- open another new foreground tab3 -");
|
||||
let tab3 = yield BrowserTestUtils.openNewForegroundTab(window.gBrowser,
|
||||
"about:blank");
|
||||
info("- should still play media from tab1 -");
|
||||
yield wait_for_tab_playing_event(tab1, true);
|
||||
yield ContentTask.spawn(tab1.linkedBrowser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- should still block media from tab2 -");
|
||||
yield wait_for_tab_playing_event(tab2, false);
|
||||
yield ContentTask.spawn(tab2.linkedBrowser, SuspendedType.SUSPENDED_BLOCK,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- remove tabs -");
|
||||
yield BrowserTestUtils.removeTab(tab1);
|
||||
yield BrowserTestUtils.removeTab(tab2);
|
||||
yield BrowserTestUtils.removeTab(tab3);
|
||||
});
|
||||
92
toolkit/content/tests/browser/browser_bug1170531.js
Normal file
92
toolkit/content/tests/browser/browser_bug1170531.js
Normal file
|
|
@ -0,0 +1,92 @@
|
|||
// Test for bug 1170531
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=1170531
|
||||
|
||||
add_task(function* () {
|
||||
// Get a bunch of DOM nodes
|
||||
let winUtils = window.QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
let editMenu = document.getElementById("edit-menu");
|
||||
let menubar = editMenu.parentNode;
|
||||
let menuPopup = editMenu.menupopup;
|
||||
let editMenuIndex = -1;
|
||||
for (let i = 0; i < menubar.children.length; i++) {
|
||||
if (menubar.children[i] === editMenu) {
|
||||
editMenuIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let closeMenu = function(aCallback) {
|
||||
if (OS.Constants.Sys.Name == "Darwin") {
|
||||
executeSoon(aCallback);
|
||||
return;
|
||||
}
|
||||
|
||||
menuPopup.addEventListener("popuphidden", function onPopupHidden() {
|
||||
menuPopup.removeEventListener("popuphidden", onPopupHidden, false);
|
||||
executeSoon(aCallback);
|
||||
}, false);
|
||||
|
||||
executeSoon(function() {
|
||||
editMenu.open = false;
|
||||
});
|
||||
};
|
||||
|
||||
let openMenu = function(aCallback) {
|
||||
if (OS.Constants.Sys.Name == "Darwin") {
|
||||
goUpdateGlobalEditMenuItems();
|
||||
// On OSX, we have a native menu, so it has to be updated. In single process browsers,
|
||||
// this happens synchronously, but in e10s, we have to wait for the main thread
|
||||
// to deal with it for us. 1 second should be plenty of time.
|
||||
setTimeout(aCallback, 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
menuPopup.addEventListener("popupshown", function onPopupShown() {
|
||||
menuPopup.removeEventListener("popupshown", onPopupShown, false);
|
||||
executeSoon(aCallback);
|
||||
}, false);
|
||||
|
||||
executeSoon(function() {
|
||||
editMenu.open = true;
|
||||
});
|
||||
};
|
||||
|
||||
yield BrowserTestUtils.withNewTab({ gBrowser: gBrowser, url: "about:blank" }, function* (browser) {
|
||||
let menu_cut_disabled, menu_copy_disabled;
|
||||
|
||||
yield BrowserTestUtils.loadURI(browser, "data:text/html,<div>hello!</div>");
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
browser.focus();
|
||||
yield new Promise(resolve => waitForFocus(resolve, window));
|
||||
yield new Promise(openMenu);
|
||||
menu_cut_disabled = menuPopup.querySelector("#menu_cut").getAttribute('disabled') == "true";
|
||||
is(menu_cut_disabled, false, "menu_cut should be enabled");
|
||||
menu_copy_disabled = menuPopup.querySelector("#menu_copy").getAttribute('disabled') == "true";
|
||||
is(menu_copy_disabled, false, "menu_copy should be enabled");
|
||||
yield new Promise(closeMenu);
|
||||
|
||||
yield BrowserTestUtils.loadURI(browser, "data:text/html,<div contentEditable='true'>hello!</div>");
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
browser.focus();
|
||||
yield new Promise(resolve => waitForFocus(resolve, window));
|
||||
yield new Promise(openMenu);
|
||||
menu_cut_disabled = menuPopup.querySelector("#menu_cut").getAttribute('disabled') == "true";
|
||||
is(menu_cut_disabled, false, "menu_cut should be enabled");
|
||||
menu_copy_disabled = menuPopup.querySelector("#menu_copy").getAttribute('disabled') == "true";
|
||||
is(menu_copy_disabled, false, "menu_copy should be enabled");
|
||||
yield new Promise(closeMenu);
|
||||
|
||||
yield BrowserTestUtils.loadURI(browser, "about:preferences");
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
browser.focus();
|
||||
yield new Promise(resolve => waitForFocus(resolve, window));
|
||||
yield new Promise(openMenu);
|
||||
menu_cut_disabled = menuPopup.querySelector("#menu_cut").getAttribute('disabled') == "true";
|
||||
is(menu_cut_disabled, true, "menu_cut should be disabled");
|
||||
menu_copy_disabled = menuPopup.querySelector("#menu_copy").getAttribute('disabled') == "true";
|
||||
is(menu_copy_disabled, true, "menu_copy should be disabled");
|
||||
yield new Promise(closeMenu);
|
||||
});
|
||||
});
|
||||
75
toolkit/content/tests/browser/browser_bug1198465.js
Normal file
75
toolkit/content/tests/browser/browser_bug1198465.js
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var kPrefName = "accessibility.typeaheadfind.prefillwithselection";
|
||||
var kEmptyURI = "data:text/html,";
|
||||
|
||||
// This pref is false by default in OSX; ensure the test still works there.
|
||||
Services.prefs.setBoolPref(kPrefName, true);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
Services.prefs.clearUserPref(kPrefName);
|
||||
});
|
||||
|
||||
add_task(function* () {
|
||||
let aTab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kEmptyURI);
|
||||
ok(!gFindBarInitialized, "findbar isn't initialized yet");
|
||||
|
||||
// Note: the use case here is when the user types directly in the findbar
|
||||
// _before_ it's prefilled with a text selection in the page.
|
||||
|
||||
// So `yield BrowserTestUtils.sendChar()` can't be used here:
|
||||
// - synthesizing a key in the browser won't actually send it to the
|
||||
// findbar; the findbar isn't part of the browser content.
|
||||
// - we need to _not_ wait for _startFindDeferred to be resolved; yielding
|
||||
// a synthesized keypress on the browser implicitely happens after the
|
||||
// browser has dispatched its return message with the prefill value for
|
||||
// the findbar, which essentially nulls these tests.
|
||||
|
||||
let findBar = gFindBar;
|
||||
is(findBar._findField.value, "", "findbar is empty");
|
||||
|
||||
// Test 1
|
||||
// Any input in the findbar should erase a previous search.
|
||||
|
||||
findBar._findField.value = "xy";
|
||||
findBar.startFind();
|
||||
is(findBar._findField.value, "xy", "findbar should have xy initial query");
|
||||
is(findBar._findField.mInputField,
|
||||
document.activeElement,
|
||||
"findbar is now focused");
|
||||
|
||||
EventUtils.sendChar("z", window);
|
||||
is(findBar._findField.value, "z", "z erases xy");
|
||||
|
||||
findBar._findField.value = "";
|
||||
ok(!findBar._findField.value, "erase findbar after first test");
|
||||
|
||||
// Test 2
|
||||
// Prefilling the findbar should be ignored if a search has been run.
|
||||
|
||||
findBar.startFind();
|
||||
ok(findBar._startFindDeferred, "prefilled value hasn't been fetched yet");
|
||||
is(findBar._findField.mInputField,
|
||||
document.activeElement,
|
||||
"findbar is still focused");
|
||||
|
||||
EventUtils.sendChar("a", window);
|
||||
EventUtils.sendChar("b", window);
|
||||
is(findBar._findField.value, "ab", "initial ab typed in the findbar");
|
||||
|
||||
// This resolves _startFindDeferred if it's still pending; let's just skip
|
||||
// over waiting for the browser's return message that should do this as it
|
||||
// doesn't really matter.
|
||||
findBar.onCurrentSelection("foo", true);
|
||||
ok(!findBar._startFindDeferred, "prefilled value fetched");
|
||||
is(findBar._findField.value, "ab", "ab kept instead of prefill value");
|
||||
|
||||
EventUtils.sendChar("c", window);
|
||||
is(findBar._findField.value, "abc", "c is appended after ab");
|
||||
|
||||
// Clear the findField value to make the test run successfully
|
||||
// for multiple runs in the same browser session.
|
||||
findBar._findField.value = "";
|
||||
yield BrowserTestUtils.removeTab(aTab);
|
||||
});
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
requestLongerTimeout(2);
|
||||
add_task(function* ()
|
||||
{
|
||||
function pushPref(name, value) {
|
||||
return new Promise(resolve => SpecialPowers.pushPrefEnv({"set": [[name, value]]}, resolve));
|
||||
}
|
||||
|
||||
yield pushPref("general.autoScroll", true);
|
||||
|
||||
const expectScrollNone = 0;
|
||||
const expectScrollVert = 1;
|
||||
const expectScrollHori = 2;
|
||||
const expectScrollBoth = 3;
|
||||
|
||||
var allTests = [
|
||||
{dataUri: 'data:text/html,<html><head><meta charset="utf-8"></head><body><style type="text/css">div { display: inline-block; }</style>\
|
||||
<div id="a" style="width: 100px; height: 100px; overflow: hidden;"><div style="width: 200px; height: 200px;"></div></div>\
|
||||
<div id="b" style="width: 100px; height: 100px; overflow: auto;"><div style="width: 200px; height: 200px;"></div></div>\
|
||||
<div id="c" style="width: 100px; height: 100px; overflow-x: auto; overflow-y: hidden;"><div style="width: 200px; height: 200px;"></div></div>\
|
||||
<div id="d" style="width: 100px; height: 100px; overflow-y: auto; overflow-x: hidden;"><div style="width: 200px; height: 200px;"></div></div>\
|
||||
<select id="e" style="width: 100px; height: 100px;" multiple="multiple"><option>aaaaaaaaaaaaaaaaaaaaaaaa</option><option>a</option><option>a</option>\
|
||||
<option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option>\
|
||||
<option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option></select>\
|
||||
<select id="f" style="width: 100px; height: 100px;"><option>a</option><option>aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa</option><option>a</option>\
|
||||
<option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option>\
|
||||
<option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option><option>a</option></select>\
|
||||
<div id="g" style="width: 99px; height: 99px; border: 10px solid black; margin: 10px; overflow: auto;"><div style="width: 100px; height: 100px;"></div></div>\
|
||||
<div id="h" style="width: 100px; height: 100px; overflow: -moz-hidden-unscrollable;"><div style="width: 200px; height: 200px;"></div></div>\
|
||||
<iframe id="iframe" style="display: none;"></iframe>\
|
||||
</body></html>'},
|
||||
{elem: 'a', expected: expectScrollNone},
|
||||
{elem: 'b', expected: expectScrollBoth},
|
||||
{elem: 'c', expected: expectScrollHori},
|
||||
{elem: 'd', expected: expectScrollVert},
|
||||
{elem: 'e', expected: expectScrollVert},
|
||||
{elem: 'f', expected: expectScrollNone},
|
||||
{elem: 'g', expected: expectScrollBoth},
|
||||
{elem: 'h', expected: expectScrollNone},
|
||||
{dataUri: 'data:text/html,<html><head><meta charset="utf-8"></head><body id="i" style="overflow-y: scroll"><div style="height: 2000px"></div>\
|
||||
<iframe id="iframe" style="display: none;"></iframe>\
|
||||
</body></html>'},
|
||||
{elem: 'i', expected: expectScrollVert}, // bug 695121
|
||||
{dataUri: 'data:text/html,<html><head><meta charset="utf-8"></head><style>html, body { width: 100%; height: 100%; overflow-x: hidden; overflow-y: scroll; }</style>\
|
||||
<body id="j"><div style="height: 2000px"></div>\
|
||||
<iframe id="iframe" style="display: none;"></iframe>\
|
||||
</body></html>'},
|
||||
{elem: 'j', expected: expectScrollVert}, // bug 914251
|
||||
{dataUri: 'data:text/html,<html><head><meta charset="utf-8">\
|
||||
<style>\
|
||||
body > div {scroll-behavior: smooth;width: 300px;height: 300px;overflow: scroll;}\
|
||||
body > div > div {width: 1000px;height: 1000px;}\
|
||||
</style>\
|
||||
</head><body><div id="t"><div></div></div></body></html>'},
|
||||
{elem: 't', expected: expectScrollBoth}, // bug 1308775
|
||||
{dataUri: 'data:text/html,<html><head><meta charset="utf-8"></head><body>\
|
||||
<div id="k" style="height: 150px; width: 200px; overflow: scroll; border: 1px solid black;">\
|
||||
<iframe style="height: 200px; width: 300px;"></iframe>\
|
||||
</div>\
|
||||
<div id="l" style="height: 150px; width: 300px; overflow: scroll; border: 1px dashed black;">\
|
||||
<iframe style="height: 200px; width: 200px;" src="data:text/html,<div style=\'border: 5px solid blue; height: 200%; width: 200%;\'></div>"></iframe>\
|
||||
</div>\
|
||||
<iframe id="m"></iframe>\
|
||||
<div style="height: 200%; border: 5px dashed black;">filler to make document overflow: scroll;</div>\
|
||||
</body></html>'},
|
||||
{elem: 'k', expected: expectScrollBoth},
|
||||
{elem: 'k', expected: expectScrollNone, testwindow: true},
|
||||
{elem: 'l', expected: expectScrollNone},
|
||||
{elem: 'm', expected: expectScrollVert, testwindow: true},
|
||||
{dataUri: 'data:text/html,<html><head><meta charset="utf-8"></head><body>\
|
||||
<img width="100" height="100" alt="image map" usemap="%23planetmap">\
|
||||
<map name="planetmap">\
|
||||
<area id="n" shape="rect" coords="0,0,100,100" href="javascript:void(null)">\
|
||||
</map>\
|
||||
<a href="javascript:void(null)" id="o" style="width: 100px; height: 100px; border: 1px solid black; display: inline-block; vertical-align: top;">link</a>\
|
||||
<input id="p" style="width: 100px; height: 100px; vertical-align: top;">\
|
||||
<textarea id="q" style="width: 100px; height: 100px; vertical-align: top;"></textarea>\
|
||||
<div style="height: 200%; border: 1px solid black;"></div>\
|
||||
</body></html>'},
|
||||
{elem: 'n', expected: expectScrollNone, testwindow: true},
|
||||
{elem: 'o', expected: expectScrollNone, testwindow: true},
|
||||
{elem: 'p', expected: expectScrollVert, testwindow: true, middlemousepastepref: false},
|
||||
{elem: 'q', expected: expectScrollVert, testwindow: true, middlemousepastepref: false},
|
||||
{dataUri: 'data:text/html,<html><head><meta charset="utf-8"></head><body>\
|
||||
<input id="r" style="width: 100px; height: 100px; vertical-align: top;">\
|
||||
<textarea id="s" style="width: 100px; height: 100px; vertical-align: top;"></textarea>\
|
||||
<div style="height: 200%; border: 1px solid black;"></div>\
|
||||
</body></html>'},
|
||||
{elem: 'r', expected: expectScrollNone, testwindow: true, middlemousepastepref: true},
|
||||
{elem: 's', expected: expectScrollNone, testwindow: true, middlemousepastepref: true}
|
||||
];
|
||||
|
||||
for (let test of allTests) {
|
||||
if (test.dataUri) {
|
||||
let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
gBrowser.loadURI(test.dataUri);
|
||||
yield loadedPromise;
|
||||
continue;
|
||||
}
|
||||
|
||||
let prefsChanged = (test.middlemousepastepref == false || test.middlemousepastepref == true);
|
||||
if (prefsChanged) {
|
||||
yield pushPref("middlemouse.paste", test.middlemousepastepref);
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouse("#" + test.elem, 50, 80, { button: 1 },
|
||||
gBrowser.selectedBrowser);
|
||||
|
||||
// This ensures bug 605127 is fixed: pagehide in an unrelated document
|
||||
// should not cancel the autoscroll.
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser, { }, function* () {
|
||||
var iframe = content.document.getElementById("iframe");
|
||||
|
||||
if (iframe) {
|
||||
var e = new iframe.contentWindow.PageTransitionEvent("pagehide",
|
||||
{ bubbles: true,
|
||||
cancelable: true,
|
||||
persisted: false });
|
||||
iframe.contentDocument.dispatchEvent(e);
|
||||
iframe.contentDocument.documentElement.dispatchEvent(e);
|
||||
}
|
||||
});
|
||||
|
||||
is(document.activeElement, gBrowser.selectedBrowser, "Browser still focused after autoscroll started");
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouse("#" + test.elem, 100, 100,
|
||||
{ type: "mousemove", clickCount: "0" },
|
||||
gBrowser.selectedBrowser);
|
||||
|
||||
if (prefsChanged) {
|
||||
yield new Promise(resolve => SpecialPowers.popPrefEnv(resolve));
|
||||
}
|
||||
|
||||
// Start checking for the scroll.
|
||||
let firstTimestamp = undefined;
|
||||
let timeCompensation;
|
||||
do {
|
||||
let timestamp = yield new Promise(resolve => window.requestAnimationFrame(resolve));
|
||||
if (firstTimestamp === undefined) {
|
||||
firstTimestamp = timestamp;
|
||||
}
|
||||
|
||||
// This value is calculated similarly to the value of the same name in
|
||||
// ClickEventHandler.autoscrollLoop, except here it's cumulative across
|
||||
// all frames after the first one instead of being based only on the
|
||||
// current frame.
|
||||
timeCompensation = (timestamp - firstTimestamp) / 20;
|
||||
info("timestamp=" + timestamp + " firstTimestamp=" + firstTimestamp +
|
||||
" timeCompensation=" + timeCompensation);
|
||||
|
||||
// Try to wait until enough time has passed to allow the scroll to happen.
|
||||
// autoscrollLoop incrementally scrolls during each animation frame, but
|
||||
// due to how its calculations work, when a frame is very close to the
|
||||
// previous frame, no scrolling may actually occur during that frame.
|
||||
// After 100ms's worth of frames, timeCompensation will be 1, making it
|
||||
// more likely that the accumulated scroll in autoscrollLoop will be >= 1,
|
||||
// although it also depends on acceleration, which here in this test
|
||||
// should be > 1 due to how it synthesizes mouse events below.
|
||||
} while (timeCompensation < 5);
|
||||
|
||||
// Close the autoscroll popup by synthesizing Esc.
|
||||
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||
let scrollVert = test.expected & expectScrollVert;
|
||||
let scrollHori = test.expected & expectScrollHori;
|
||||
|
||||
yield ContentTask.spawn(gBrowser.selectedBrowser,
|
||||
{ scrollVert : scrollVert,
|
||||
scrollHori: scrollHori,
|
||||
elemid : test.elem,
|
||||
checkWindow: test.testwindow },
|
||||
function* (args) {
|
||||
let msg = "";
|
||||
if (args.checkWindow) {
|
||||
if (!((args.scrollVert && content.scrollY > 0) ||
|
||||
(!args.scrollVert && content.scrollY == 0))) {
|
||||
msg += "Failed: ";
|
||||
}
|
||||
msg += 'Window for ' + args.elemid + ' should' + (args.scrollVert ? '' : ' not') + ' have scrolled vertically\n';
|
||||
|
||||
if (!((args.scrollHori && content.scrollX > 0) ||
|
||||
(!args.scrollHori && content.scrollX == 0))) {
|
||||
msg += "Failed: ";
|
||||
}
|
||||
msg += ' Window for ' + args.elemid + ' should' + (args.scrollHori ? '' : ' not') + ' have scrolled horizontally\n';
|
||||
} else {
|
||||
let elem = content.document.getElementById(args.elemid);
|
||||
if (!((args.scrollVert && elem.scrollTop > 0) ||
|
||||
(!args.scrollVert && elem.scrollTop == 0))) {
|
||||
msg += "Failed: ";
|
||||
}
|
||||
msg += ' ' + args.elemid + ' should' + (args.scrollVert ? '' : ' not') + ' have scrolled vertically\n';
|
||||
if (!((args.scrollHori && elem.scrollLeft > 0) ||
|
||||
(!args.scrollHori && elem.scrollLeft == 0))) {
|
||||
msg += "Failed: ";
|
||||
}
|
||||
msg += args.elemid + ' should' + (args.scrollHori ? '' : ' not') + ' have scrolled horizontally';
|
||||
}
|
||||
|
||||
Assert.ok(msg.indexOf("Failed") == -1, msg);
|
||||
}
|
||||
);
|
||||
|
||||
// Before continuing the test, we need to ensure that the IPC
|
||||
// message that stops autoscrolling has had time to arrive.
|
||||
yield new Promise(resolve => executeSoon(resolve));
|
||||
}
|
||||
|
||||
// remove 2 tabs that were opened by middle-click on links
|
||||
while (gBrowser.visibleTabs.length > 1) {
|
||||
gBrowser.removeTab(gBrowser.visibleTabs[gBrowser.visibleTabs.length - 1]);
|
||||
}
|
||||
|
||||
// wait for focus to fix a failure in the next test if the latter runs too soon.
|
||||
yield SimpleTest.promiseFocus();
|
||||
});
|
||||
152
toolkit/content/tests/browser/browser_bug451286.js
Normal file
152
toolkit/content/tests/browser/browser_bug451286.js
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
Services.scriptloader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/WindowSnapshot.js", this);
|
||||
|
||||
add_task(function*() {
|
||||
const SEARCH_TEXT = "text";
|
||||
const DATAURI = "data:text/html," + SEARCH_TEXT;
|
||||
|
||||
// Bug 451286. An iframe that should be highlighted
|
||||
let visible = "<iframe id='visible' src='" + DATAURI + "'></iframe>";
|
||||
|
||||
// Bug 493658. An invisible iframe that shouldn't interfere with
|
||||
// highlighting matches lying after it in the document
|
||||
let invisible = "<iframe id='invisible' style='display: none;' " +
|
||||
"src='" + DATAURI + "'></iframe>";
|
||||
|
||||
let uri = DATAURI + invisible + SEARCH_TEXT + visible + SEARCH_TEXT;
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, uri);
|
||||
let contentRect = tab.linkedBrowser.getBoundingClientRect();
|
||||
let noHighlightSnapshot = snapshotRect(window, contentRect);
|
||||
ok(noHighlightSnapshot, "Got noHighlightSnapshot");
|
||||
|
||||
yield openFindBarAndWait();
|
||||
gFindBar._findField.value = SEARCH_TEXT;
|
||||
yield findAgainAndWait();
|
||||
var matchCase = gFindBar.getElement("find-case-sensitive");
|
||||
if (matchCase.checked)
|
||||
matchCase.doCommand();
|
||||
|
||||
// Turn on highlighting
|
||||
yield toggleHighlightAndWait(true);
|
||||
yield closeFindBarAndWait();
|
||||
|
||||
// Take snapshot of highlighting
|
||||
let findSnapshot = snapshotRect(window, contentRect);
|
||||
ok(findSnapshot, "Got findSnapshot");
|
||||
|
||||
// Now, remove the highlighting, and take a snapshot to compare
|
||||
// to our original state
|
||||
yield openFindBarAndWait();
|
||||
yield toggleHighlightAndWait(false);
|
||||
yield closeFindBarAndWait();
|
||||
|
||||
let unhighlightSnapshot = snapshotRect(window, contentRect);
|
||||
ok(unhighlightSnapshot, "Got unhighlightSnapshot");
|
||||
|
||||
// Select the matches that should have been highlighted manually
|
||||
yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
|
||||
let doc = content.document;
|
||||
let win = doc.defaultView;
|
||||
|
||||
// Create a manual highlight in the visible iframe to test bug 451286
|
||||
let iframe = doc.getElementById("visible");
|
||||
let ifBody = iframe.contentDocument.body;
|
||||
let range = iframe.contentDocument.createRange();
|
||||
range.selectNodeContents(ifBody.childNodes[0]);
|
||||
let ifWindow = iframe.contentWindow;
|
||||
let ifDocShell = ifWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
let ifController = ifDocShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsISelectionDisplay)
|
||||
.QueryInterface(Ci.nsISelectionController);
|
||||
|
||||
let frameFindSelection =
|
||||
ifController.getSelection(ifController.SELECTION_FIND);
|
||||
frameFindSelection.addRange(range);
|
||||
|
||||
// Create manual highlights in the main document (the matches that lie
|
||||
// before/after the iframes
|
||||
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
let controller = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsISelectionDisplay)
|
||||
.QueryInterface(Ci.nsISelectionController);
|
||||
|
||||
let docFindSelection =
|
||||
controller.getSelection(ifController.SELECTION_FIND);
|
||||
|
||||
range = doc.createRange();
|
||||
range.selectNodeContents(doc.body.childNodes[0]);
|
||||
docFindSelection.addRange(range);
|
||||
range = doc.createRange();
|
||||
range.selectNodeContents(doc.body.childNodes[2]);
|
||||
docFindSelection.addRange(range);
|
||||
range = doc.createRange();
|
||||
range.selectNodeContents(doc.body.childNodes[4]);
|
||||
docFindSelection.addRange(range);
|
||||
});
|
||||
|
||||
// Take snapshot of manual highlighting
|
||||
let manualSnapshot = snapshotRect(window, contentRect);
|
||||
ok(manualSnapshot, "Got manualSnapshot");
|
||||
|
||||
// Test 1: Were the matches in iframe correctly highlighted?
|
||||
let res = compareSnapshots(findSnapshot, manualSnapshot, true);
|
||||
ok(res[0], "Matches found in iframe correctly highlighted");
|
||||
|
||||
// Test 2: Were the matches in iframe correctly unhighlighted?
|
||||
res = compareSnapshots(noHighlightSnapshot, unhighlightSnapshot, true);
|
||||
ok(res[0], "Highlighting in iframe correctly removed");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
function toggleHighlightAndWait(shouldHighlight) {
|
||||
return new Promise((resolve) => {
|
||||
let listener = {
|
||||
onFindResult() {},
|
||||
onHighlightFinished() {
|
||||
gFindBar.browser.finder.removeResultListener(listener);
|
||||
resolve();
|
||||
},
|
||||
onMatchesCountResult() {}
|
||||
};
|
||||
gFindBar.browser.finder.addResultListener(listener);
|
||||
gFindBar.toggleHighlight(shouldHighlight);
|
||||
});
|
||||
}
|
||||
|
||||
function findAgainAndWait() {
|
||||
return new Promise(resolve => {
|
||||
let listener = {
|
||||
onFindResult() {
|
||||
gFindBar.browser.finder.removeResultListener(listener);
|
||||
resolve();
|
||||
},
|
||||
onHighlightFinished() {},
|
||||
onMatchesCountResult() {}
|
||||
};
|
||||
gFindBar.browser.finder.addResultListener(listener);
|
||||
gFindBar.onFindAgainCommand();
|
||||
});
|
||||
}
|
||||
|
||||
function* openFindBarAndWait() {
|
||||
let awaitTransitionEnd = BrowserTestUtils.waitForEvent(gFindBar, "transitionend");
|
||||
gFindBar.open();
|
||||
yield awaitTransitionEnd;
|
||||
}
|
||||
|
||||
// This test is comparing snapshots. It is necessary to wait for the gFindBar
|
||||
// to close before taking the snapshot so the gFindBar does not take up space
|
||||
// on the new snapshot.
|
||||
function* closeFindBarAndWait() {
|
||||
let awaitTransitionEnd = BrowserTestUtils.waitForEvent(gFindBar, "transitionend", false, event => {
|
||||
return event.propertyName == "visibility";
|
||||
});
|
||||
gFindBar.close();
|
||||
yield awaitTransitionEnd;
|
||||
}
|
||||
9
toolkit/content/tests/browser/browser_bug594509.js
Normal file
9
toolkit/content/tests/browser/browser_bug594509.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
add_task(function* () {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:rights");
|
||||
|
||||
yield ContentTask.spawn(tab.linkedBrowser, null, function* () {
|
||||
Assert.ok(content.document.getElementById("your-rights"), "about:rights content loaded");
|
||||
});
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
70
toolkit/content/tests/browser/browser_bug982298.js
Normal file
70
toolkit/content/tests/browser/browser_bug982298.js
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
const scrollHtml =
|
||||
"<textarea id=\"textarea1\" row=2>Firefox\n\nFirefox\n\n\n\n\n\n\n\n\n\n" +
|
||||
"</textarea><a href=\"about:blank\">blank</a>";
|
||||
|
||||
add_task(function*() {
|
||||
let url = "data:text/html;base64," + btoa(scrollHtml);
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url}, function*(browser) {
|
||||
let awaitFindResult = new Promise(resolve => {
|
||||
let listener = {
|
||||
onFindResult(aData) {
|
||||
info("got find result");
|
||||
browser.finder.removeResultListener(listener);
|
||||
|
||||
ok(aData.result == Ci.nsITypeAheadFind.FIND_FOUND, "should find string");
|
||||
resolve();
|
||||
},
|
||||
onCurrentSelection() {},
|
||||
onMatchesCountResult() {}
|
||||
};
|
||||
info("about to add results listener, open find bar, and send 'F' string");
|
||||
browser.finder.addResultListener(listener);
|
||||
});
|
||||
gFindBar.onFindCommand();
|
||||
EventUtils.sendString("F");
|
||||
info("added result listener and sent string 'F'");
|
||||
yield awaitFindResult;
|
||||
|
||||
let awaitScrollDone = BrowserTestUtils.waitForMessage(browser.messageManager, "ScrollDone");
|
||||
// scroll textarea to bottom
|
||||
const scrollTest =
|
||||
"var textarea = content.document.getElementById(\"textarea1\");" +
|
||||
"textarea.scrollTop = textarea.scrollHeight;" +
|
||||
"sendAsyncMessage(\"ScrollDone\", { });"
|
||||
browser.messageManager.loadFrameScript("data:text/javascript;base64," +
|
||||
btoa(scrollTest), false);
|
||||
yield awaitScrollDone;
|
||||
info("got ScrollDone event");
|
||||
yield BrowserTestUtils.loadURI(browser, "about:blank");
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
|
||||
ok(browser.currentURI.spec == "about:blank", "got load event for about:blank");
|
||||
|
||||
let awaitFindResult2 = new Promise(resolve => {
|
||||
let listener = {
|
||||
onFindResult(aData) {
|
||||
info("got find result #2");
|
||||
browser.finder.removeResultListener(listener);
|
||||
resolve();
|
||||
},
|
||||
onCurrentSelection() {},
|
||||
onMatchesCountResult() {}
|
||||
};
|
||||
|
||||
browser.finder.addResultListener(listener);
|
||||
info("added result listener");
|
||||
});
|
||||
// find again needs delay for crash test
|
||||
setTimeout(function() {
|
||||
// ignore exception if occured
|
||||
try {
|
||||
info("about to send find again command");
|
||||
gFindBar.onFindAgainCommand(false);
|
||||
info("sent find again command");
|
||||
} catch (e) {
|
||||
info("got exception from onFindAgainCommand: " + e);
|
||||
}
|
||||
}, 0);
|
||||
yield awaitFindResult2;
|
||||
});
|
||||
});
|
||||
16
toolkit/content/tests/browser/browser_contentTitle.js
Normal file
16
toolkit/content/tests/browser/browser_contentTitle.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
var url = "https://example.com/browser/toolkit/content/tests/browser/file_contentTitle.html";
|
||||
|
||||
add_task(function*() {
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
|
||||
let browser = tab.linkedBrowser;
|
||||
yield new Promise((resolve) => {
|
||||
addEventListener("TestLocationChange", function listener() {
|
||||
removeEventListener("TestLocationChange", listener);
|
||||
resolve();
|
||||
}, true, true);
|
||||
});
|
||||
|
||||
is(gBrowser.contentTitle, "Test Page", "Should have the right title.");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
/* global Services, requestLongerTimeout, TestUtils, BrowserTestUtils,
|
||||
ok, info, dump, is, Ci, Cu, Components, ctypes, privateNoteIntentionalCrash,
|
||||
gBrowser, add_task, addEventListener, removeEventListener, ContentTask */
|
||||
|
||||
"use strict";
|
||||
|
||||
// Running this test in ASAN is slow.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
/**
|
||||
* Removes a file from a directory. This is a no-op if the file does not
|
||||
* exist.
|
||||
*
|
||||
* @param directory
|
||||
* The nsIFile representing the directory to remove from.
|
||||
* @param filename
|
||||
* A string for the file to remove from the directory.
|
||||
*/
|
||||
function removeFile(directory, filename) {
|
||||
let file = directory.clone();
|
||||
file.append(filename);
|
||||
if (file.exists()) {
|
||||
file.remove(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the directory where crash dumps are stored.
|
||||
*
|
||||
* @return nsIFile
|
||||
*/
|
||||
function getMinidumpDirectory() {
|
||||
let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
|
||||
dir.append("minidumps");
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks that the URL is correctly annotated on a content process crash.
|
||||
*/
|
||||
add_task(function* test_content_url_annotation() {
|
||||
let url = "https://example.com/browser/toolkit/content/tests/browser/file_redirect.html";
|
||||
let redirect_url = "https://example.com/browser/toolkit/content/tests/browser/file_redirect_to.html";
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser: gBrowser
|
||||
}, function* (browser) {
|
||||
ok(browser.isRemoteBrowser, "Should be a remote browser");
|
||||
|
||||
// file_redirect.html should send us to file_redirect_to.html
|
||||
let promise = ContentTask.spawn(browser, {}, function* () {
|
||||
dump('ContentTask starting...\n');
|
||||
yield new Promise((resolve) => {
|
||||
addEventListener("RedirectDone", function listener() {
|
||||
dump('Got RedirectDone\n');
|
||||
removeEventListener("RedirectDone", listener);
|
||||
resolve();
|
||||
}, true, true);
|
||||
});
|
||||
});
|
||||
browser.loadURI(url);
|
||||
yield promise;
|
||||
|
||||
// Crash the tab
|
||||
let annotations = yield BrowserTestUtils.crashBrowser(browser);
|
||||
|
||||
ok("URL" in annotations, "annotated a URL");
|
||||
is(annotations.URL, redirect_url,
|
||||
"Should have annotated the URL after redirect");
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
"use strict";
|
||||
|
||||
/**
|
||||
* Cleans up the .dmp and .extra file from a crash.
|
||||
*
|
||||
* @param subject (nsISupports)
|
||||
* The subject passed through the ipc:content-shutdown
|
||||
* observer notification when a content process crash has
|
||||
* occurred.
|
||||
*/
|
||||
function cleanUpMinidump(subject) {
|
||||
Assert.ok(subject instanceof Ci.nsIPropertyBag2,
|
||||
"Subject needs to be a nsIPropertyBag2 to clean up properly");
|
||||
let dumpID = subject.getPropertyAsAString("dumpID");
|
||||
|
||||
Assert.ok(dumpID, "There should be a dumpID");
|
||||
if (dumpID) {
|
||||
let dir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||
dir.append("minidumps");
|
||||
|
||||
let file = dir.clone();
|
||||
file.append(dumpID + ".dmp");
|
||||
file.remove(true);
|
||||
|
||||
file = dir.clone();
|
||||
file.append(dumpID + ".extra");
|
||||
file.remove(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test ensures that if a remote frameloader crashes after
|
||||
* the frameloader owner swaps it out for a new frameloader,
|
||||
* that a oop-browser-crashed event is not sent to the new
|
||||
* frameloader's browser element.
|
||||
*/
|
||||
add_task(function* test_crash_in_previous_frameloader() {
|
||||
// On debug builds, crashing tabs results in much thinking, which
|
||||
// slows down the test and results in intermittent test timeouts,
|
||||
// so we'll pump up the expected timeout for this test.
|
||||
requestLongerTimeout(2);
|
||||
|
||||
if (!gMultiProcessBrowser) {
|
||||
Assert.ok(false, "This test should only be run in multi-process mode.");
|
||||
return;
|
||||
}
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "http://example.com",
|
||||
}, function*(browser) {
|
||||
// First, sanity check...
|
||||
Assert.ok(browser.isRemoteBrowser,
|
||||
"This browser needs to be remote if this test is going to " +
|
||||
"work properly.");
|
||||
|
||||
// We will wait for the oop-browser-crashed event to have
|
||||
// a chance to appear. That event is fired when TabParents
|
||||
// are destroyed, and that occurs _before_ ContentParents
|
||||
// are destroyed, so we'll wait on the ipc:content-shutdown
|
||||
// observer notification, which is fired when a ContentParent
|
||||
// goes away. After we see this notification, oop-browser-crashed
|
||||
// events should have fired.
|
||||
let contentProcessGone = TestUtils.topicObserved("ipc:content-shutdown");
|
||||
let sawTabCrashed = false;
|
||||
let onTabCrashed = () => {
|
||||
sawTabCrashed = true;
|
||||
};
|
||||
|
||||
browser.addEventListener("oop-browser-crashed", onTabCrashed);
|
||||
|
||||
// The name of the game is to cause a crash in a remote browser,
|
||||
// and then immediately swap out the browser for a non-remote one.
|
||||
yield ContentTask.spawn(browser, null, function() {
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
let dies = function() {
|
||||
privateNoteIntentionalCrash();
|
||||
let zero = new ctypes.intptr_t(8);
|
||||
let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
|
||||
badptr.contents
|
||||
};
|
||||
|
||||
// When the parent flips the remoteness of the browser, the
|
||||
// page should receive the pagehide event, which we'll then
|
||||
// use to crash the frameloader.
|
||||
addEventListener("pagehide", function() {
|
||||
dump("\nEt tu, Brute?\n");
|
||||
dies();
|
||||
});
|
||||
});
|
||||
|
||||
gBrowser.updateBrowserRemoteness(browser, false);
|
||||
info("Waiting for content process to go away.");
|
||||
let [subject, data] = yield contentProcessGone;
|
||||
|
||||
// If we don't clean up the minidump, the harness will
|
||||
// complain.
|
||||
cleanUpMinidump(subject);
|
||||
|
||||
info("Content process is gone!");
|
||||
Assert.ok(!sawTabCrashed,
|
||||
"Should not have seen the oop-browser-crashed event.");
|
||||
browser.removeEventListener("oop-browser-crashed", onTabCrashed);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
var MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window);
|
||||
|
||||
/**
|
||||
* TestCase for bug 564387
|
||||
* <https://bugzilla.mozilla.org/show_bug.cgi?id=564387>
|
||||
*/
|
||||
add_task(function* () {
|
||||
let loadPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
gBrowser.loadURI("data:image/gif;base64,R0lGODlhEAAOALMAAOazToeHh0tLS/7LZv/0jvb29t/f3//Ub//ge8WSLf/rhf/3kdbW1mxsbP//mf///yH5BAAAAAAALAAAAAAQAA4AAARe8L1Ekyky67QZ1hLnjM5UUde0ECwLJoExKcppV0aCcGCmTIHEIUEqjgaORCMxIC6e0CcguWw6aFjsVMkkIr7g77ZKPJjPZqIyd7sJAgVGoEGv2xsBxqNgYPj/gAwXEQA7");
|
||||
yield loadPromise;
|
||||
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(document, "popupshown");
|
||||
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("img",
|
||||
{ type: "contextmenu", button: 2 },
|
||||
gBrowser.selectedBrowser);
|
||||
|
||||
yield popupShownPromise;
|
||||
|
||||
let showFilePickerPromise = new Promise(resolve => {
|
||||
MockFilePicker.showCallback = function(fp) {
|
||||
is(fp.defaultString, "index.gif");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
MockFilePicker.cleanup();
|
||||
});
|
||||
|
||||
// Select "Save Image As" option from context menu
|
||||
var saveImageAsCommand = document.getElementById("context-saveimage");
|
||||
saveImageAsCommand.doCommand();
|
||||
|
||||
yield showFilePickerPromise;
|
||||
|
||||
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
let popupHiddenPromise = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
|
||||
contextMenu.hidePopup();
|
||||
yield popupHiddenPromise;
|
||||
});
|
||||
227
toolkit/content/tests/browser/browser_f7_caret_browsing.js
Normal file
227
toolkit/content/tests/browser/browser_f7_caret_browsing.js
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
var gListener = null;
|
||||
const kURL = "data:text/html;charset=utf-8,Caret browsing is fun.<input id='in'>";
|
||||
|
||||
const kPrefShortcutEnabled = "accessibility.browsewithcaret_shortcut.enabled";
|
||||
const kPrefWarnOnEnable = "accessibility.warn_on_browsewithcaret";
|
||||
const kPrefCaretBrowsingOn = "accessibility.browsewithcaret";
|
||||
|
||||
var oldPrefs = {};
|
||||
for (let pref of [kPrefShortcutEnabled, kPrefWarnOnEnable, kPrefCaretBrowsingOn]) {
|
||||
oldPrefs[pref] = Services.prefs.getBoolPref(pref);
|
||||
}
|
||||
|
||||
Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
|
||||
Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
|
||||
Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
for (let pref of [kPrefShortcutEnabled, kPrefWarnOnEnable, kPrefCaretBrowsingOn]) {
|
||||
Services.prefs.setBoolPref(pref, oldPrefs[pref]);
|
||||
}
|
||||
});
|
||||
|
||||
// NB: not using BrowserTestUtils.domWindowOpened here because there's no way to
|
||||
// undo waiting for a window open. If we don't want the window to be opened, and
|
||||
// wait for it to verify that it indeed does not open, we need to be able to
|
||||
// then "stop" waiting so that when we next *do* want it to open, our "old"
|
||||
// listener doesn't fire and do things we don't want (like close the window...).
|
||||
let gCaretPromptOpeningObserver;
|
||||
function promiseCaretPromptOpened() {
|
||||
return new Promise(resolve => {
|
||||
function observer(subject, topic, data) {
|
||||
if (topic == "domwindowopened") {
|
||||
Services.ww.unregisterNotification(observer);
|
||||
let win = subject.QueryInterface(Ci.nsIDOMWindow);
|
||||
BrowserTestUtils.waitForEvent(win, "load", false, e => e.target.location.href != "about:blank").then(() => resolve(win));
|
||||
gCaretPromptOpeningObserver = null;
|
||||
}
|
||||
}
|
||||
Services.ww.registerNotification(observer);
|
||||
gCaretPromptOpeningObserver = observer;
|
||||
});
|
||||
}
|
||||
|
||||
function hitF7(async = true) {
|
||||
let f7 = () => EventUtils.sendKey("F7");
|
||||
// Need to not stop execution inside this task:
|
||||
if (async) {
|
||||
executeSoon(f7);
|
||||
} else {
|
||||
f7();
|
||||
}
|
||||
}
|
||||
|
||||
function syncToggleCaretNoDialog(expected) {
|
||||
let openedDialog = false;
|
||||
promiseCaretPromptOpened().then(function(win) {
|
||||
openedDialog = true;
|
||||
win.close(); // This will eventually return focus here and allow the test to continue...
|
||||
});
|
||||
// Cause the dialog to appear sync, if it still does.
|
||||
hitF7(false);
|
||||
|
||||
let expectedStr = expected ? "on." : "off.";
|
||||
ok(!openedDialog, "Shouldn't open a dialog to turn caret browsing " + expectedStr);
|
||||
// Need to clean up if the dialog wasn't opened, so the observer doesn't get
|
||||
// re-triggered later on causing "issues".
|
||||
if (!openedDialog) {
|
||||
Services.ww.unregisterNotification(gCaretPromptOpeningObserver);
|
||||
gCaretPromptOpeningObserver = null;
|
||||
}
|
||||
let prefVal = Services.prefs.getBoolPref(kPrefCaretBrowsingOn);
|
||||
is(prefVal, expected, "Caret browsing should now be " + expectedStr);
|
||||
}
|
||||
|
||||
function waitForFocusOnInput(browser)
|
||||
{
|
||||
return ContentTask.spawn(browser, null, function* () {
|
||||
let textEl = content.document.getElementById("in");
|
||||
return ContentTaskUtils.waitForCondition(() => {
|
||||
return content.document.activeElement == textEl;
|
||||
}, "Input should get focused.");
|
||||
});
|
||||
}
|
||||
|
||||
function focusInput(browser)
|
||||
{
|
||||
return ContentTask.spawn(browser, null, function* () {
|
||||
let textEl = content.document.getElementById("in");
|
||||
textEl.focus();
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* checkTogglingCaretBrowsing() {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kURL);
|
||||
yield focusInput(tab.linkedBrowser);
|
||||
|
||||
let promiseGotKey = promiseCaretPromptOpened();
|
||||
hitF7();
|
||||
let prompt = yield promiseGotKey;
|
||||
let doc = prompt.document;
|
||||
is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
|
||||
ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
|
||||
let promiseDialogUnloaded = BrowserTestUtils.waitForEvent(prompt, "unload");
|
||||
|
||||
doc.documentElement.cancelDialog();
|
||||
yield promiseDialogUnloaded;
|
||||
info("Dialog unloaded");
|
||||
yield waitForFocusOnInput(tab.linkedBrowser);
|
||||
ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off after cancelling the dialog.");
|
||||
|
||||
promiseGotKey = promiseCaretPromptOpened();
|
||||
hitF7();
|
||||
prompt = yield promiseGotKey;
|
||||
|
||||
doc = prompt.document;
|
||||
is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
|
||||
ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
|
||||
promiseDialogUnloaded = BrowserTestUtils.waitForEvent(prompt, "unload");
|
||||
|
||||
doc.documentElement.acceptDialog();
|
||||
yield promiseDialogUnloaded;
|
||||
info("Dialog unloaded");
|
||||
yield waitForFocusOnInput(tab.linkedBrowser);
|
||||
ok(Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should be on after accepting the dialog.");
|
||||
|
||||
syncToggleCaretNoDialog(false);
|
||||
|
||||
promiseGotKey = promiseCaretPromptOpened();
|
||||
hitF7();
|
||||
prompt = yield promiseGotKey;
|
||||
doc = prompt.document;
|
||||
|
||||
is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
|
||||
ok(!doc.getElementById("checkbox").checked, "Checkbox shouldn't be checked by default.");
|
||||
|
||||
promiseDialogUnloaded = BrowserTestUtils.waitForEvent(prompt, "unload");
|
||||
doc.documentElement.cancelDialog();
|
||||
yield promiseDialogUnloaded;
|
||||
info("Dialog unloaded");
|
||||
yield waitForFocusOnInput(tab.linkedBrowser);
|
||||
|
||||
ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off after cancelling the dialog.");
|
||||
|
||||
Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
|
||||
Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
|
||||
Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(function* toggleCheckboxNoCaretBrowsing() {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kURL);
|
||||
yield focusInput(tab.linkedBrowser);
|
||||
|
||||
let promiseGotKey = promiseCaretPromptOpened();
|
||||
hitF7();
|
||||
let prompt = yield promiseGotKey;
|
||||
let doc = prompt.document;
|
||||
is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
|
||||
let checkbox = doc.getElementById("checkbox");
|
||||
ok(!checkbox.checked, "Checkbox shouldn't be checked by default.");
|
||||
|
||||
// Check the box:
|
||||
checkbox.click();
|
||||
|
||||
let promiseDialogUnloaded = BrowserTestUtils.waitForEvent(prompt, "unload");
|
||||
|
||||
// Say no:
|
||||
doc.documentElement.getButton("cancel").click();
|
||||
|
||||
yield promiseDialogUnloaded;
|
||||
info("Dialog unloaded");
|
||||
yield waitForFocusOnInput(tab.linkedBrowser);
|
||||
ok(!Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should still be off.");
|
||||
ok(!Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should now be disabled.");
|
||||
|
||||
syncToggleCaretNoDialog(false);
|
||||
ok(!Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should still be disabled.");
|
||||
|
||||
Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
|
||||
Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
|
||||
Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
|
||||
add_task(function* toggleCheckboxWantCaretBrowsing() {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, kURL);
|
||||
yield focusInput(tab.linkedBrowser);
|
||||
|
||||
let promiseGotKey = promiseCaretPromptOpened();
|
||||
hitF7();
|
||||
let prompt = yield promiseGotKey;
|
||||
let doc = prompt.document;
|
||||
is(doc.documentElement.defaultButton, "cancel", "No button should be the default");
|
||||
let checkbox = doc.getElementById("checkbox");
|
||||
ok(!checkbox.checked, "Checkbox shouldn't be checked by default.");
|
||||
|
||||
// Check the box:
|
||||
checkbox.click();
|
||||
|
||||
let promiseDialogUnloaded = BrowserTestUtils.waitForEvent(prompt, "unload");
|
||||
|
||||
// Say yes:
|
||||
doc.documentElement.acceptDialog();
|
||||
yield promiseDialogUnloaded;
|
||||
info("Dialog unloaded");
|
||||
yield waitForFocusOnInput(tab.linkedBrowser);
|
||||
ok(Services.prefs.getBoolPref(kPrefCaretBrowsingOn), "Caret browsing should now be on.");
|
||||
ok(Services.prefs.getBoolPref(kPrefShortcutEnabled), "Shortcut should still be enabled.");
|
||||
ok(!Services.prefs.getBoolPref(kPrefWarnOnEnable), "Should no longer warn when enabling.");
|
||||
|
||||
syncToggleCaretNoDialog(false);
|
||||
syncToggleCaretNoDialog(true);
|
||||
syncToggleCaretNoDialog(false);
|
||||
|
||||
Services.prefs.setBoolPref(kPrefShortcutEnabled, true);
|
||||
Services.prefs.setBoolPref(kPrefWarnOnEnable, true);
|
||||
Services.prefs.setBoolPref(kPrefCaretBrowsingOn, false);
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
249
toolkit/content/tests/browser/browser_findbar.js
Normal file
249
toolkit/content/tests/browser/browser_findbar.js
Normal file
|
|
@ -0,0 +1,249 @@
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
Components.utils.import("resource://gre/modules/Timer.jsm", this);
|
||||
|
||||
const TEST_PAGE_URI = "data:text/html;charset=utf-8,The letter s.";
|
||||
// Using 'javascript' schema to bypass E10SUtils.canLoadURIInProcess, because
|
||||
// it does not allow 'data:' URI to be loaded in the parent process.
|
||||
const E10S_PARENT_TEST_PAGE_URI = "javascript:document.write('The letter s.');";
|
||||
|
||||
/**
|
||||
* Makes sure that the findbar hotkeys (' and /) event listeners
|
||||
* are added to the system event group and do not get blocked
|
||||
* by calling stopPropagation on a keypress event on a page.
|
||||
*/
|
||||
add_task(function* test_hotkey_event_propagation() {
|
||||
info("Ensure hotkeys are not affected by stopPropagation.");
|
||||
|
||||
// Opening new tab
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
let findbar = gBrowser.getFindBar();
|
||||
|
||||
// Pressing these keys open the findbar.
|
||||
const HOTKEYS = ["/", "'"];
|
||||
|
||||
// Checking if findbar appears when any hotkey is pressed.
|
||||
for (let key of HOTKEYS) {
|
||||
is(findbar.hidden, true, "Findbar is hidden now.");
|
||||
gBrowser.selectedTab = tab;
|
||||
yield SimpleTest.promiseFocus(gBrowser.selectedBrowser);
|
||||
yield BrowserTestUtils.sendChar(key, browser);
|
||||
is(findbar.hidden, false, "Findbar should not be hidden.");
|
||||
yield closeFindbarAndWait(findbar);
|
||||
}
|
||||
|
||||
// Stop propagation for all keyboard events.
|
||||
let frameScript = () => {
|
||||
const stopPropagation = e => e.stopImmediatePropagation();
|
||||
let window = content.document.defaultView;
|
||||
window.removeEventListener("keydown", stopPropagation);
|
||||
window.removeEventListener("keypress", stopPropagation);
|
||||
window.removeEventListener("keyup", stopPropagation);
|
||||
};
|
||||
|
||||
let mm = browser.messageManager;
|
||||
mm.loadFrameScript("data:,(" + frameScript.toString() + ")();", false);
|
||||
|
||||
// Checking if findbar still appears when any hotkey is pressed.
|
||||
for (let key of HOTKEYS) {
|
||||
is(findbar.hidden, true, "Findbar is hidden now.");
|
||||
gBrowser.selectedTab = tab;
|
||||
yield SimpleTest.promiseFocus(gBrowser.selectedBrowser);
|
||||
yield BrowserTestUtils.sendChar(key, browser);
|
||||
is(findbar.hidden, false, "Findbar should not be hidden.");
|
||||
yield closeFindbarAndWait(findbar);
|
||||
}
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(function* test_not_found() {
|
||||
info("Check correct 'Phrase not found' on new tab");
|
||||
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
|
||||
// Search for the first word.
|
||||
yield promiseFindFinished("--- THIS SHOULD NEVER MATCH ---", false);
|
||||
let findbar = gBrowser.getFindBar();
|
||||
is(findbar._findStatusDesc.textContent, findbar._notFoundStr,
|
||||
"Findbar status text should be 'Phrase not found'");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
add_task(function* test_found() {
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
|
||||
// Search for a string that WILL be found, with 'Highlight All' on
|
||||
yield promiseFindFinished("S", true);
|
||||
ok(!gBrowser.getFindBar()._findStatusDesc.textContent,
|
||||
"Findbar status should be empty");
|
||||
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
||||
// Setting first findbar to case-sensitive mode should not affect
|
||||
// new tab find bar.
|
||||
add_task(function* test_tabwise_case_sensitive() {
|
||||
let tab1 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let findbar1 = gBrowser.getFindBar();
|
||||
|
||||
let tab2 = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let findbar2 = gBrowser.getFindBar();
|
||||
|
||||
// Toggle case sensitivity for first findbar
|
||||
findbar1.getElement("find-case-sensitive").click();
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
|
||||
// Not found for first tab.
|
||||
yield promiseFindFinished("S", true);
|
||||
is(findbar1._findStatusDesc.textContent, findbar1._notFoundStr,
|
||||
"Findbar status text should be 'Phrase not found'");
|
||||
|
||||
gBrowser.selectedTab = tab2;
|
||||
|
||||
// But it didn't affect the second findbar.
|
||||
yield promiseFindFinished("S", true);
|
||||
ok(!findbar2._findStatusDesc.textContent, "Findbar status should be empty");
|
||||
|
||||
gBrowser.removeTab(tab1);
|
||||
gBrowser.removeTab(tab2);
|
||||
});
|
||||
|
||||
/**
|
||||
* Navigating from a web page (for example mozilla.org) to an internal page
|
||||
* (like about:addons) might trigger a change of browser's remoteness.
|
||||
* 'Remoteness change' means that rendering page content moves from child
|
||||
* process into the parent process or the other way around.
|
||||
* This test ensures that findbar properly handles such a change.
|
||||
*/
|
||||
add_task(function* test_reinitialization_at_remoteness_change() {
|
||||
// This test only makes sence in e10s evironment.
|
||||
if (!gMultiProcessBrowser) {
|
||||
info("Skipping this test because of non-e10s environment.");
|
||||
return;
|
||||
}
|
||||
|
||||
info("Ensure findbar re-initialization at remoteness change.");
|
||||
|
||||
// Load a remote page and trigger findbar construction.
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
let findbar = gBrowser.getFindBar();
|
||||
|
||||
// Findbar should operate normally.
|
||||
yield promiseFindFinished("z", false);
|
||||
is(findbar._findStatusDesc.textContent, findbar._notFoundStr,
|
||||
"Findbar status text should be 'Phrase not found'");
|
||||
|
||||
yield promiseFindFinished("s", false);
|
||||
ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty");
|
||||
|
||||
// Moving browser into the parent process and reloading sample data.
|
||||
ok(browser.isRemoteBrowser, "Browser should be remote now.");
|
||||
yield promiseRemotenessChange(tab, false);
|
||||
yield BrowserTestUtils.loadURI(browser, E10S_PARENT_TEST_PAGE_URI);
|
||||
ok(!browser.isRemoteBrowser, "Browser should not be remote any more.");
|
||||
|
||||
// Findbar should keep operating normally after remoteness change.
|
||||
yield promiseFindFinished("z", false);
|
||||
is(findbar._findStatusDesc.textContent, findbar._notFoundStr,
|
||||
"Findbar status text should be 'Phrase not found'");
|
||||
|
||||
yield promiseFindFinished("s", false);
|
||||
ok(!findbar._findStatusDesc.textContent, "Findbar status should be empty");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensure that the initial typed characters aren't lost immediately after
|
||||
* opening the find bar.
|
||||
*/
|
||||
add_task(function* () {
|
||||
// This test only makes sence in e10s evironment.
|
||||
if (!gMultiProcessBrowser) {
|
||||
info("Skipping this test because of non-e10s environment.");
|
||||
return;
|
||||
}
|
||||
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE_URI);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
ok(!gFindBarInitialized, "findbar isn't initialized yet");
|
||||
|
||||
let findBar = gFindBar;
|
||||
let initialValue = findBar._findField.value;
|
||||
|
||||
EventUtils.synthesizeKey("f", { accelKey: true }, window);
|
||||
|
||||
let promises = [
|
||||
BrowserTestUtils.sendChar("a", browser),
|
||||
BrowserTestUtils.sendChar("b", browser),
|
||||
BrowserTestUtils.sendChar("c", browser)
|
||||
];
|
||||
|
||||
isnot(document.activeElement, findBar._findField.inputField,
|
||||
"findbar is not yet focused");
|
||||
is(findBar._findField.value, initialValue, "still has initial find query");
|
||||
|
||||
yield Promise.all(promises);
|
||||
is(document.activeElement, findBar._findField.inputField,
|
||||
"findbar is now focused");
|
||||
is(findBar._findField.value, "abc", "abc fully entered as find query");
|
||||
|
||||
yield BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
|
||||
function promiseFindFinished(searchText, highlightOn) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let findbar = gBrowser.getFindBar();
|
||||
findbar.startFind(findbar.FIND_NORMAL);
|
||||
let highlightElement = findbar.getElement("highlight");
|
||||
if (highlightElement.checked != highlightOn)
|
||||
highlightElement.click();
|
||||
executeSoon(() => {
|
||||
findbar._findField.value = searchText;
|
||||
|
||||
let resultListener;
|
||||
// When highlighting is on the finder sends a second "FOUND" message after
|
||||
// the search wraps. This causes timing problems with e10s. waitMore
|
||||
// forces foundOrTimeout wait for the second "FOUND" message before
|
||||
// resolving the promise.
|
||||
let waitMore = highlightOn;
|
||||
let findTimeout = setTimeout(() => foundOrTimedout(null), 2000);
|
||||
let foundOrTimedout = function(aData) {
|
||||
if (aData !== null && waitMore) {
|
||||
waitMore = false;
|
||||
return;
|
||||
}
|
||||
if (aData === null)
|
||||
info("Result listener not called, timeout reached.");
|
||||
clearTimeout(findTimeout);
|
||||
findbar.browser.finder.removeResultListener(resultListener);
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
resultListener = {
|
||||
onFindResult: foundOrTimedout
|
||||
};
|
||||
findbar.browser.finder.addResultListener(resultListener);
|
||||
findbar._find();
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseRemotenessChange(tab, shouldBeRemote) {
|
||||
return new Promise((resolve) => {
|
||||
let browser = gBrowser.getBrowserForTab(tab);
|
||||
tab.addEventListener("TabRemotenessChange", function listener() {
|
||||
tab.removeEventListener("TabRemotenessChange", listener);
|
||||
resolve();
|
||||
});
|
||||
gBrowser.updateBrowserRemoteness(browser, shouldBeRemote);
|
||||
});
|
||||
}
|
||||
72
toolkit/content/tests/browser/browser_isSynthetic.js
Normal file
72
toolkit/content/tests/browser/browser_isSynthetic.js
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
function LocationChangeListener(browser) {
|
||||
this.browser = browser;
|
||||
browser.addProgressListener(this);
|
||||
}
|
||||
|
||||
LocationChangeListener.prototype = {
|
||||
wasSynthetic: false,
|
||||
browser: null,
|
||||
|
||||
destroy: function() {
|
||||
this.browser.removeProgressListener(this);
|
||||
},
|
||||
|
||||
onLocationChange: function(webProgress, request, location, flags) {
|
||||
this.wasSynthetic = this.browser.isSyntheticDocument;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIWebProgressListener,
|
||||
Ci.nsISupportsWeakReference])
|
||||
}
|
||||
|
||||
const FILES = gTestPath.replace("browser_isSynthetic.js", "")
|
||||
.replace("chrome://mochitests/content/", "http://example.com/");
|
||||
|
||||
function waitForPageShow(browser) {
|
||||
return ContentTask.spawn(browser, null, function*() {
|
||||
Cu.import("resource://gre/modules/PromiseUtils.jsm");
|
||||
yield new Promise(resolve => {
|
||||
let listener = () => {
|
||||
removeEventListener("pageshow", listener, true);
|
||||
resolve();
|
||||
}
|
||||
addEventListener("pageshow", listener, true);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
let tab = gBrowser.addTab("about:blank");
|
||||
let browser = tab.linkedBrowser;
|
||||
yield BrowserTestUtils.browserLoaded(browser);
|
||||
let listener = new LocationChangeListener(browser);
|
||||
|
||||
is(browser.isSyntheticDocument, false, "Should not be synthetic");
|
||||
|
||||
let loadPromise = waitForPageShow(browser);
|
||||
browser.loadURI("data:text/html;charset=utf-8,<html/>");
|
||||
yield loadPromise;
|
||||
is(listener.wasSynthetic, false, "Should not be synthetic");
|
||||
is(browser.isSyntheticDocument, false, "Should not be synthetic");
|
||||
|
||||
loadPromise = waitForPageShow(browser);
|
||||
browser.loadURI(FILES + "empty.png");
|
||||
yield loadPromise;
|
||||
is(listener.wasSynthetic, true, "Should be synthetic");
|
||||
is(browser.isSyntheticDocument, true, "Should be synthetic");
|
||||
|
||||
loadPromise = waitForPageShow(browser);
|
||||
browser.goBack();
|
||||
yield loadPromise;
|
||||
is(listener.wasSynthetic, false, "Should not be synthetic");
|
||||
is(browser.isSyntheticDocument, false, "Should not be synthetic");
|
||||
|
||||
loadPromise = waitForPageShow(browser);
|
||||
browser.goForward();
|
||||
yield loadPromise;
|
||||
is(listener.wasSynthetic, true, "Should be synthetic");
|
||||
is(browser.isSyntheticDocument, true, "Should be synthetic");
|
||||
|
||||
listener.destroy();
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
add_task(function * ()
|
||||
{
|
||||
const kPrefName_AutoScroll = "general.autoScroll";
|
||||
Services.prefs.setBoolPref(kPrefName_AutoScroll, true);
|
||||
|
||||
const kNoKeyEvents = 0;
|
||||
const kKeyDownEvent = 1;
|
||||
const kKeyPressEvent = 2;
|
||||
const kKeyUpEvent = 4;
|
||||
const kAllKeyEvents = 7;
|
||||
|
||||
var expectedKeyEvents;
|
||||
var dispatchedKeyEvents;
|
||||
var key;
|
||||
var root;
|
||||
|
||||
/**
|
||||
* Encapsulates EventUtils.sendChar().
|
||||
*/
|
||||
function sendChar(aChar)
|
||||
{
|
||||
key = aChar;
|
||||
dispatchedKeyEvents = kNoKeyEvents;
|
||||
EventUtils.sendChar(key);
|
||||
is(dispatchedKeyEvents, expectedKeyEvents,
|
||||
"unexpected key events were dispatched or not dispatched: " + key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates EventUtils.sendKey().
|
||||
*/
|
||||
function sendKey(aKey)
|
||||
{
|
||||
key = aKey;
|
||||
dispatchedKeyEvents = kNoKeyEvents;
|
||||
EventUtils.sendKey(key);
|
||||
is(dispatchedKeyEvents, expectedKeyEvents,
|
||||
"unexpected key events were dispatched or not dispatched: " + key);
|
||||
}
|
||||
|
||||
function onKey(aEvent)
|
||||
{
|
||||
// if (aEvent.target != root && aEvent.target != root.ownerDocument.body) {
|
||||
// ok(false, "unknown target: " + aEvent.target.tagName);
|
||||
// return;
|
||||
// }
|
||||
|
||||
var keyFlag;
|
||||
switch (aEvent.type) {
|
||||
case "keydown":
|
||||
keyFlag = kKeyDownEvent;
|
||||
break;
|
||||
case "keypress":
|
||||
keyFlag = kKeyPressEvent;
|
||||
break;
|
||||
case "keyup":
|
||||
keyFlag = kKeyUpEvent;
|
||||
break;
|
||||
default:
|
||||
ok(false, "Unknown events: " + aEvent.type);
|
||||
return;
|
||||
}
|
||||
dispatchedKeyEvents |= keyFlag;
|
||||
is(keyFlag, expectedKeyEvents & keyFlag, aEvent.type + " fired: " + key);
|
||||
}
|
||||
|
||||
var dataUri = 'data:text/html,<body style="height:10000px;"></body>';
|
||||
|
||||
let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
gBrowser.loadURI(dataUri);
|
||||
yield loadedPromise;
|
||||
|
||||
yield SimpleTest.promiseFocus(gBrowser.selectedBrowser);
|
||||
|
||||
window.addEventListener("keydown", onKey, false);
|
||||
window.addEventListener("keypress", onKey, false);
|
||||
window.addEventListener("keyup", onKey, false);
|
||||
|
||||
// Test whether the key events are handled correctly under normal condition
|
||||
expectedKeyEvents = kAllKeyEvents;
|
||||
sendChar("A");
|
||||
|
||||
// Start autoscrolling by middle button click on the page
|
||||
let shownPromise = BrowserTestUtils.waitForEvent(window, "popupshown", false,
|
||||
event => event.originalTarget.className == "autoscroller");
|
||||
yield BrowserTestUtils.synthesizeMouseAtPoint(10, 10, { button: 1 },
|
||||
gBrowser.selectedBrowser);
|
||||
yield shownPromise;
|
||||
|
||||
// Most key events should be eaten by the browser.
|
||||
expectedKeyEvents = kNoKeyEvents;
|
||||
sendChar("A");
|
||||
sendKey("DOWN");
|
||||
sendKey("RETURN");
|
||||
sendKey("RETURN");
|
||||
sendKey("HOME");
|
||||
sendKey("END");
|
||||
sendKey("TAB");
|
||||
sendKey("RETURN");
|
||||
|
||||
// Finish autoscrolling by ESC key. Note that only keydown and keypress
|
||||
// events are eaten because keyup event is fired *after* the autoscrolling
|
||||
// is finished.
|
||||
expectedKeyEvents = kKeyUpEvent;
|
||||
sendKey("ESCAPE");
|
||||
|
||||
// Test whether the key events are handled correctly under normal condition
|
||||
expectedKeyEvents = kAllKeyEvents;
|
||||
sendChar("A");
|
||||
|
||||
window.removeEventListener("keydown", onKey, false);
|
||||
window.removeEventListener("keypress", onKey, false);
|
||||
window.removeEventListener("keyup", onKey, false);
|
||||
|
||||
// restore the changed prefs
|
||||
if (Services.prefs.prefHasUserValue(kPrefName_AutoScroll))
|
||||
Services.prefs.clearUserPref(kPrefName_AutoScroll);
|
||||
|
||||
finish();
|
||||
});
|
||||
38
toolkit/content/tests/browser/browser_label_textlink.js
Normal file
38
toolkit/content/tests/browser/browser_label_textlink.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
add_task(function* () {
|
||||
yield BrowserTestUtils.withNewTab({gBrowser, url: "about:config"}, function*(browser) {
|
||||
let newTabURL = "http://www.example.com/";
|
||||
yield ContentTask.spawn(browser, newTabURL, function*(newTabURL) {
|
||||
let doc = content.document;
|
||||
let label = doc.createElement("label");
|
||||
label.href = newTabURL;
|
||||
label.id = "textlink-test";
|
||||
label.className = "text-link";
|
||||
label.textContent = "click me";
|
||||
doc.documentElement.append(label);
|
||||
});
|
||||
|
||||
// Test that click will open tab in foreground.
|
||||
let awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, newTabURL);
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#textlink-test", {}, browser);
|
||||
let newTab = yield awaitNewTab;
|
||||
is(newTab.linkedBrowser, gBrowser.selectedBrowser, "selected tab should be example page");
|
||||
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
|
||||
|
||||
// Test that ctrl+shift+click/meta+shift+click will open tab in background.
|
||||
awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, newTabURL);
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#textlink-test",
|
||||
{ctrlKey: true, metaKey: true, shiftKey: true},
|
||||
browser);
|
||||
yield awaitNewTab;
|
||||
is(gBrowser.selectedBrowser, browser, "selected tab should be original tab");
|
||||
yield BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
|
||||
// Middle-clicking should open tab in foreground.
|
||||
awaitNewTab = BrowserTestUtils.waitForNewTab(gBrowser, newTabURL);
|
||||
yield BrowserTestUtils.synthesizeMouseAtCenter("#textlink-test",
|
||||
{button: 1}, browser);
|
||||
newTab = yield awaitNewTab;
|
||||
is(newTab.linkedBrowser, gBrowser.selectedBrowser, "selected tab should be example page");
|
||||
yield BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
|
||||
});
|
||||
});
|
||||
30
toolkit/content/tests/browser/browser_mediaPlayback.js
Normal file
30
toolkit/content/tests/browser/browser_mediaPlayback.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_mediaPlayback.html";
|
||||
const FRAME = "https://example.com/browser/toolkit/content/tests/browser/file_mediaPlaybackFrame.html";
|
||||
|
||||
function wait_for_event(browser, event) {
|
||||
return BrowserTestUtils.waitForEvent(browser, event, false, (event) => {
|
||||
is(event.originalTarget, browser, "Event must be dispatched to correct browser.");
|
||||
ok(!event.cancelable, "The event should not be cancelable");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function* test_on_browser(url, browser) {
|
||||
browser.loadURI(url);
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStopped");
|
||||
}
|
||||
|
||||
add_task(function* test_page() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
}, test_on_browser.bind(undefined, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_frame() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
}, test_on_browser.bind(undefined, FRAME));
|
||||
});
|
||||
104
toolkit/content/tests/browser/browser_mediaPlayback_mute.js
Normal file
104
toolkit/content/tests/browser/browser_mediaPlayback_mute.js
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_mediaPlayback2.html";
|
||||
const FRAME = "https://example.com/browser/toolkit/content/tests/browser/file_mediaPlaybackFrame2.html";
|
||||
|
||||
function wait_for_event(browser, event) {
|
||||
return BrowserTestUtils.waitForEvent(browser, event, false, (event) => {
|
||||
is(event.originalTarget, browser, "Event must be dispatched to correct browser.");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function* test_audio_in_browser() {
|
||||
function get_audio_element() {
|
||||
var doc = content.document;
|
||||
var list = doc.getElementsByTagName('audio');
|
||||
if (list.length == 1) {
|
||||
return list[0];
|
||||
}
|
||||
|
||||
// iframe?
|
||||
list = doc.getElementsByTagName('iframe');
|
||||
|
||||
var iframe = list[0];
|
||||
list = iframe.contentDocument.getElementsByTagName('audio');
|
||||
return list[0];
|
||||
}
|
||||
|
||||
var audio = get_audio_element();
|
||||
return {
|
||||
computedVolume: audio.computedVolume,
|
||||
computedMuted: audio.computedMuted
|
||||
}
|
||||
}
|
||||
|
||||
function* test_on_browser(url, browser) {
|
||||
browser.loadURI(url);
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
var result = yield ContentTask.spawn(browser, null, test_audio_in_browser);
|
||||
is(result.computedVolume, 1, "Audio volume is 1");
|
||||
is(result.computedMuted, false, "Audio is not muted");
|
||||
|
||||
ok(!browser.audioMuted, "Audio should not be muted by default");
|
||||
browser.mute();
|
||||
ok(browser.audioMuted, "Audio should be muted now");
|
||||
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStopped");
|
||||
|
||||
result = yield ContentTask.spawn(browser, null, test_audio_in_browser);
|
||||
is(result.computedVolume, 0, "Audio volume is 0 when muted");
|
||||
is(result.computedMuted, true, "Audio is muted");
|
||||
}
|
||||
|
||||
function* test_visibility(url, browser) {
|
||||
browser.loadURI(url);
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
var result = yield ContentTask.spawn(browser, null, test_audio_in_browser);
|
||||
is(result.computedVolume, 1, "Audio volume is 1");
|
||||
is(result.computedMuted, false, "Audio is not muted");
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
}, function() {});
|
||||
|
||||
ok(!browser.audioMuted, "Audio should not be muted by default");
|
||||
browser.mute();
|
||||
ok(browser.audioMuted, "Audio should be muted now");
|
||||
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStopped");
|
||||
|
||||
result = yield ContentTask.spawn(browser, null, test_audio_in_browser);
|
||||
is(result.computedVolume, 0, "Audio volume is 0 when muted");
|
||||
is(result.computedMuted, true, "Audio is muted");
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
yield new Promise((resolve) => {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["media.useAudioChannelService.testing", true]
|
||||
]}, resolve);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_page() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
}, test_on_browser.bind(undefined, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_frame() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
}, test_on_browser.bind(undefined, FRAME));
|
||||
});
|
||||
|
||||
add_task(function* test_frame() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank",
|
||||
}, test_visibility.bind(undefined, PAGE));
|
||||
});
|
||||
191
toolkit/content/tests/browser/browser_mediaPlayback_suspended.js
Normal file
191
toolkit/content/tests/browser/browser_mediaPlayback_suspended.js
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_mediaPlayback2.html";
|
||||
|
||||
var SuspendedType = {
|
||||
NONE_SUSPENDED : 0,
|
||||
SUSPENDED_PAUSE : 1,
|
||||
SUSPENDED_BLOCK : 2,
|
||||
SUSPENDED_PAUSE_DISPOSABLE : 3
|
||||
};
|
||||
|
||||
function wait_for_event(browser, event) {
|
||||
return BrowserTestUtils.waitForEvent(browser, event, false, (event) => {
|
||||
is(event.originalTarget, browser, "Event must be dispatched to correct browser.");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function check_audio_onplay() {
|
||||
var list = content.document.getElementsByTagName('audio');
|
||||
if (list.length != 1) {
|
||||
ok(false, "There should be only one audio element in page!")
|
||||
}
|
||||
|
||||
var audio = list[0];
|
||||
return new Promise((resolve, reject) => {
|
||||
audio.onplay = () => {
|
||||
ok(needToReceiveOnPlay, "Should not receive play event!");
|
||||
this.onplay = null;
|
||||
reject();
|
||||
};
|
||||
|
||||
audio.pause();
|
||||
audio.play();
|
||||
|
||||
setTimeout(() => {
|
||||
ok(true, "Doesn't receive play event when media was blocked.");
|
||||
audio.onplay = null;
|
||||
resolve();
|
||||
}, 1000)
|
||||
});
|
||||
}
|
||||
|
||||
function check_audio_suspended(suspendedType) {
|
||||
var list = content.document.getElementsByTagName('audio');
|
||||
if (list.length != 1) {
|
||||
ok(false, "There should be only one audio element in page!")
|
||||
}
|
||||
|
||||
var audio = list[0];
|
||||
is(audio.computedSuspended, suspendedType,
|
||||
"The suspended state of MediaElement is correct.");
|
||||
}
|
||||
|
||||
function check_audio_pause_state(expectedPauseState) {
|
||||
var list = content.document.getElementsByTagName('audio');
|
||||
if (list.length != 1) {
|
||||
ok(false, "There should be only one audio element in page!")
|
||||
}
|
||||
|
||||
var audio = list[0];
|
||||
if (expectedPauseState) {
|
||||
is(audio.paused, true, "Audio is paused correctly.");
|
||||
} else {
|
||||
is(audio.paused, false, "Audio is resumed correctly.");
|
||||
}
|
||||
}
|
||||
|
||||
function* suspended_pause(url, browser) {
|
||||
info("### Start test for suspended-pause ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the suspended state of audio should be non-suspened -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- pause playing audio -");
|
||||
browser.pauseMedia(false /* non-disposable */);
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- resume paused audio -");
|
||||
browser.resumeMedia();
|
||||
yield ContentTask.spawn(browser, false /* expect for playing */,
|
||||
check_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
}
|
||||
|
||||
function* suspended_pause_disposable(url, browser) {
|
||||
info("### Start test for suspended-pause-disposable ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the suspended state of audio should be non-suspened -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- pause playing audio -");
|
||||
browser.pauseMedia(true /* disposable */);
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE_DISPOSABLE,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- resume paused audio -");
|
||||
browser.resumeMedia();
|
||||
yield ContentTask.spawn(browser, false /* expect for playing */,
|
||||
check_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
}
|
||||
|
||||
function* suspended_stop_disposable(url, browser) {
|
||||
info("### Start test for suspended-stop-disposable ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the suspended state of audio should be non-suspened -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
|
||||
info("- stop playing audio -");
|
||||
browser.stopMedia();
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStopped");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
}
|
||||
|
||||
function* suspended_block(url, browser) {
|
||||
info("### Start test for suspended-block ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- block playing audio -");
|
||||
browser.blockMedia();
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_BLOCK,
|
||||
check_audio_suspended);
|
||||
yield ContentTask.spawn(browser, null,
|
||||
check_audio_onplay);
|
||||
|
||||
info("- resume blocked audio -");
|
||||
browser.resumeMedia();
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_audio_suspended);
|
||||
}
|
||||
|
||||
add_task(function* setup_test_preference() {
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["media.useAudioChannelService.testing", true]
|
||||
]}, resolve);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_pause() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_pause.bind(this, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_pause_disposable() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_pause_disposable.bind(this, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_stop_disposable() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_stop_disposable.bind(this, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_block() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_block.bind(this, PAGE));
|
||||
});
|
||||
|
|
@ -0,0 +1,311 @@
|
|||
const PAGE = "https://example.com/browser/toolkit/content/tests/browser/file_multipleAudio.html";
|
||||
|
||||
var SuspendedType = {
|
||||
NONE_SUSPENDED : 0,
|
||||
SUSPENDED_PAUSE : 1,
|
||||
SUSPENDED_BLOCK : 2,
|
||||
SUSPENDED_PAUSE_DISPOSABLE : 3
|
||||
};
|
||||
|
||||
function wait_for_event(browser, event) {
|
||||
return BrowserTestUtils.waitForEvent(browser, event, false, (event) => {
|
||||
is(event.originalTarget, browser, "Event must be dispatched to correct browser.");
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
function check_all_audio_suspended(suspendedType) {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!autoPlay || !nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.computedSuspended, suspendedType,
|
||||
"The suspeded state of autoplay audio is correct.");
|
||||
is(nonAutoPlay.computedSuspended, suspendedType,
|
||||
"The suspeded state of non-autoplay audio is correct.");
|
||||
}
|
||||
|
||||
function check_autoplay_audio_suspended(suspendedType) {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
if (!autoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.computedSuspended, suspendedType,
|
||||
"The suspeded state of autoplay audio is correct.");
|
||||
}
|
||||
|
||||
function check_nonautoplay_audio_suspended(suspendedType) {
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(nonAutoPlay.computedSuspended, suspendedType,
|
||||
"The suspeded state of non-autoplay audio is correct.");
|
||||
}
|
||||
|
||||
function check_autoplay_audio_pause_state(expectedPauseState) {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
if (!autoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
if (autoPlay.paused == expectedPauseState) {
|
||||
if (expectedPauseState) {
|
||||
ok(true, "Audio is paused correctly.");
|
||||
} else {
|
||||
ok(true, "Audio is resumed correctly.");
|
||||
}
|
||||
} else if (expectedPauseState) {
|
||||
autoPlay.onpause = function () {
|
||||
autoPlay.onpause = null;
|
||||
ok(true, "Audio is paused correctly, checking from onpause.");
|
||||
}
|
||||
} else {
|
||||
autoPlay.onplay = function () {
|
||||
autoPlay.onplay = null;
|
||||
ok(true, "Audio is resumed correctly, checking from onplay.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function play_nonautoplay_audio_should_be_paused() {
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
nonAutoPlay.play();
|
||||
return new Promise(resolve => {
|
||||
nonAutoPlay.onpause = function () {
|
||||
nonAutoPlay.onpause = null;
|
||||
is(nonAutoPlay.ended, false, "Audio can't be playback.");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function all_audio_onresume() {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!autoPlay || !nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.paused, false, "Autoplay audio is resumed.");
|
||||
is(nonAutoPlay.paused, false, "Non-AutoPlay audio is resumed.");
|
||||
}
|
||||
|
||||
function all_audio_onpause() {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!autoPlay || !nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.paused, true, "Autoplay audio is paused.");
|
||||
is(nonAutoPlay.paused, true, "Non-AutoPlay audio is paused.");
|
||||
}
|
||||
|
||||
function play_nonautoplay_audio_should_play_until_ended() {
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
nonAutoPlay.play();
|
||||
return new Promise(resolve => {
|
||||
nonAutoPlay.onended = function () {
|
||||
nonAutoPlay.onended = null;
|
||||
ok(true, "Audio can be playback until ended.");
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function no_audio_resumed() {
|
||||
var autoPlay = content.document.getElementById('autoplay');
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!autoPlay || !nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
is(autoPlay.paused && nonAutoPlay.paused, true, "No audio was resumed.");
|
||||
}
|
||||
|
||||
function play_nonautoplay_audio_should_be_blocked(suspendedType) {
|
||||
var nonAutoPlay = content.document.getElementById('nonautoplay');
|
||||
if (!nonAutoPlay) {
|
||||
ok(false, "Can't get the audio element!");
|
||||
}
|
||||
|
||||
nonAutoPlay.play();
|
||||
ok(nonAutoPlay.paused, "The blocked audio can't be playback.");
|
||||
}
|
||||
|
||||
function* suspended_pause(url, browser) {
|
||||
info("### Start test for suspended-pause ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the default suspended state of all audio should be non-suspened-");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- pause all audio in the page -");
|
||||
browser.pauseMedia(false /* non-disposable */);
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_autoplay_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE,
|
||||
check_autoplay_audio_suspended);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_nonautoplay_audio_suspended);
|
||||
|
||||
info("- no audio can be playback during suspended-paused -");
|
||||
yield ContentTask.spawn(browser, null,
|
||||
play_nonautoplay_audio_should_be_paused);
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE,
|
||||
check_nonautoplay_audio_suspended);
|
||||
|
||||
info("- both audio should be resumed at the same time -");
|
||||
browser.resumeMedia();
|
||||
yield ContentTask.spawn(browser, null,
|
||||
all_audio_onresume);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- both audio should be paused at the same time -");
|
||||
browser.pauseMedia(false /* non-disposable */);
|
||||
yield ContentTask.spawn(browser, null, all_audio_onpause);
|
||||
}
|
||||
|
||||
function* suspended_pause_disposable(url, browser) {
|
||||
info("### Start test for suspended-pause-disposable ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the default suspended state of all audio should be non-suspened -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- only pause playing audio in the page -");
|
||||
browser.pauseMedia(true /* non-disposable */);
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_autoplay_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_PAUSE_DISPOSABLE,
|
||||
check_autoplay_audio_suspended);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_nonautoplay_audio_suspended);
|
||||
|
||||
info("- new playing audio should be playback correctly -");
|
||||
yield ContentTask.spawn(browser, null,
|
||||
play_nonautoplay_audio_should_play_until_ended);
|
||||
|
||||
info("- should only resume one audio -");
|
||||
browser.resumeMedia();
|
||||
yield ContentTask.spawn(browser, false /* expect for playing */,
|
||||
check_autoplay_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
}
|
||||
|
||||
function* suspended_stop_disposable(url, browser) {
|
||||
info("### Start test for suspended-stop-disposable ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the default suspended state of all audio should be non-suspened -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- only stop playing audio in the page -");
|
||||
browser.stopMedia();
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStopped");
|
||||
yield ContentTask.spawn(browser, true /* expect for pause */,
|
||||
check_autoplay_audio_pause_state);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- new playing audio should be playback correctly -");
|
||||
yield ContentTask.spawn(browser, null,
|
||||
play_nonautoplay_audio_should_play_until_ended);
|
||||
|
||||
info("- no any audio can be resumed by page -");
|
||||
browser.resumeMedia();
|
||||
yield ContentTask.spawn(browser, null, no_audio_resumed);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
}
|
||||
|
||||
function* suspended_block(url, browser) {
|
||||
info("### Start test for suspended-block ###");
|
||||
browser.loadURI(url);
|
||||
|
||||
info("- page should have playing audio -");
|
||||
yield wait_for_event(browser, "DOMAudioPlaybackStarted");
|
||||
|
||||
info("- the default suspended state of all audio should be non-suspened-");
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
|
||||
info("- block autoplay audio -");
|
||||
browser.blockMedia();
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_BLOCK,
|
||||
check_autoplay_audio_suspended);
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_nonautoplay_audio_suspended);
|
||||
|
||||
info("- no audio can be playback during suspended-block -");
|
||||
yield ContentTask.spawn(browser, SuspendedType.SUSPENDED_BLOCK,
|
||||
play_nonautoplay_audio_should_be_blocked);
|
||||
|
||||
info("- both audio should be resumed at the same time -");
|
||||
browser.resumeMedia();
|
||||
yield ContentTask.spawn(browser, SuspendedType.NONE_SUSPENDED,
|
||||
check_all_audio_suspended);
|
||||
}
|
||||
|
||||
add_task(function* setup_test_preference() {
|
||||
yield new Promise(resolve => {
|
||||
SpecialPowers.pushPrefEnv({"set": [
|
||||
["media.useAudioChannelService.testing", true]
|
||||
]}, resolve);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_pause() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_pause.bind(this, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_pause_disposable() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_pause_disposable.bind(this, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_stop_disposable() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_stop_disposable.bind(this, PAGE));
|
||||
});
|
||||
|
||||
add_task(function* test_suspended_block() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: "about:blank"
|
||||
}, suspended_block.bind(this, PAGE));
|
||||
});
|
||||
16
toolkit/content/tests/browser/browser_mute.js
Normal file
16
toolkit/content/tests/browser/browser_mute.js
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
const PAGE = "data:text/html,page";
|
||||
|
||||
function* test_on_browser(browser) {
|
||||
ok(!browser.audioMuted, "Audio should not be muted by default");
|
||||
browser.mute();
|
||||
ok(browser.audioMuted, "Audio should be muted now");
|
||||
browser.unmute();
|
||||
ok(!browser.audioMuted, "Audio should be unmuted now");
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, test_on_browser);
|
||||
});
|
||||
26
toolkit/content/tests/browser/browser_mute2.js
Normal file
26
toolkit/content/tests/browser/browser_mute2.js
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
const PAGE = "data:text/html,page";
|
||||
|
||||
function* test_on_browser(browser) {
|
||||
ok(!browser.audioMuted, "Audio should not be muted by default");
|
||||
browser.mute();
|
||||
ok(browser.audioMuted, "Audio should be muted now");
|
||||
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, test_on_browser2);
|
||||
|
||||
browser.unmute();
|
||||
ok(!browser.audioMuted, "Audio should be unmuted now");
|
||||
}
|
||||
|
||||
function* test_on_browser2(browser) {
|
||||
ok(!browser.audioMuted, "Audio should not be muted by default");
|
||||
}
|
||||
|
||||
add_task(function*() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE,
|
||||
}, test_on_browser);
|
||||
});
|
||||
47
toolkit/content/tests/browser/browser_quickfind_editable.js
Normal file
47
toolkit/content/tests/browser/browser_quickfind_editable.js
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
const PAGE = "data:text/html,<div contenteditable>foo</div><input><textarea></textarea>";
|
||||
const DESIGNMODE_PAGE = "data:text/html,<body onload='document.designMode=\"on\";'>";
|
||||
const HOTKEYS = ["/", "'"];
|
||||
|
||||
function* test_hotkeys(browser, expected) {
|
||||
let findbar = gBrowser.getFindBar();
|
||||
for (let key of HOTKEYS) {
|
||||
is(findbar.hidden, true, "findbar is hidden");
|
||||
yield BrowserTestUtils.sendChar(key, gBrowser.selectedBrowser);
|
||||
is(findbar.hidden, expected, "findbar should" + (expected ? "" : " not") + " be hidden");
|
||||
if (!expected) {
|
||||
yield closeFindbarAndWait(findbar);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function* focus_element(browser, query) {
|
||||
yield ContentTask.spawn(browser, query, function* focus(query) {
|
||||
let element = content.document.querySelector(query);
|
||||
element.focus();
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function* test_hotkey_on_editable_element() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: PAGE
|
||||
}, function* do_tests(browser) {
|
||||
yield test_hotkeys(browser, false);
|
||||
const ELEMENTS = ["div", "input", "textarea"];
|
||||
for (let elem of ELEMENTS) {
|
||||
yield focus_element(browser, elem);
|
||||
yield test_hotkeys(browser, true);
|
||||
yield focus_element(browser, ":root");
|
||||
yield test_hotkeys(browser, false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_hotkey_on_designMode_document() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: DESIGNMODE_PAGE
|
||||
}, function* do_tests(browser) {
|
||||
yield test_hotkeys(browser, true);
|
||||
});
|
||||
});
|
||||
68
toolkit/content/tests/browser/browser_saveImageURL.js
Normal file
68
toolkit/content/tests/browser/browser_saveImageURL.js
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
"use strict";
|
||||
|
||||
const IMAGE_PAGE = "https://example.com/browser/toolkit/content/tests/browser/image_page.html";
|
||||
const PREF_UNSAFE_FORBIDDEN = "dom.ipc.cpows.forbid-unsafe-from-browser";
|
||||
|
||||
MockFilePicker.init(window);
|
||||
MockFilePicker.returnValue = MockFilePicker.returnCancel;
|
||||
|
||||
registerCleanupFunction(function() {
|
||||
MockFilePicker.cleanup();
|
||||
});
|
||||
|
||||
function waitForFilePicker() {
|
||||
return new Promise((resolve) => {
|
||||
MockFilePicker.showCallback = () => {
|
||||
MockFilePicker.showCallback = null;
|
||||
ok(true, "Saw the file picker");
|
||||
resolve();
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that saveImageURL works when we pass in the aIsContentWindowPrivate
|
||||
* argument instead of a document. This is the preferred API.
|
||||
*/
|
||||
add_task(function* preferred_API() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: IMAGE_PAGE,
|
||||
}, function*(browser) {
|
||||
let url = yield ContentTask.spawn(browser, null, function*() {
|
||||
let image = content.document.getElementById("image");
|
||||
return image.href;
|
||||
});
|
||||
|
||||
saveImageURL(url, "image.jpg", null, true, false, null, null, null, null, false);
|
||||
yield waitForFilePicker();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that saveImageURL will still work when passed a document instead
|
||||
* of the aIsContentWindowPrivate argument. This is the deprecated API, and
|
||||
* will not work in apps using remote browsers having PREF_UNSAFE_FORBIDDEN
|
||||
* set to true.
|
||||
*/
|
||||
add_task(function* deprecated_API() {
|
||||
yield BrowserTestUtils.withNewTab({
|
||||
gBrowser,
|
||||
url: IMAGE_PAGE,
|
||||
}, function*(browser) {
|
||||
yield pushPrefs([PREF_UNSAFE_FORBIDDEN, false]);
|
||||
|
||||
let url = yield ContentTask.spawn(browser, null, function*() {
|
||||
let image = content.document.getElementById("image");
|
||||
return image.href;
|
||||
});
|
||||
|
||||
// Now get the document directly from content. If we run this test with
|
||||
// e10s-enabled, this will be a CPOW, which is forbidden. We'll just
|
||||
// pass the XUL document instead to test this interface.
|
||||
let doc = document;
|
||||
|
||||
saveImageURL(url, "image.jpg", null, true, false, null, doc, null, null);
|
||||
yield waitForFilePicker();
|
||||
});
|
||||
});
|
||||
145
toolkit/content/tests/browser/browser_save_resend_postdata.js
Normal file
145
toolkit/content/tests/browser/browser_save_resend_postdata.js
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
/* 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/. */
|
||||
|
||||
var MockFilePicker = SpecialPowers.MockFilePicker;
|
||||
MockFilePicker.init(window);
|
||||
|
||||
/**
|
||||
* Test for bug 471962 <https://bugzilla.mozilla.org/show_bug.cgi?id=471962>:
|
||||
* When saving an inner frame as file only, the POST data of the outer page is
|
||||
* sent to the address of the inner page.
|
||||
*
|
||||
* Test for bug 485196 <https://bugzilla.mozilla.org/show_bug.cgi?id=485196>:
|
||||
* Web page generated by POST is retried as GET when Save Frame As used, and the
|
||||
* page is no longer in the cache.
|
||||
*/
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
gBrowser.loadURI("http://mochi.test:8888/browser/toolkit/content/tests/browser/data/post_form_outer.sjs");
|
||||
|
||||
gBrowser.addEventListener("pageshow", function pageShown(event) {
|
||||
if (event.target.location == "about:blank")
|
||||
return;
|
||||
gBrowser.removeEventListener("pageshow", pageShown);
|
||||
|
||||
// Submit the form in the outer page, then wait for both the outer
|
||||
// document and the inner frame to be loaded again.
|
||||
gBrowser.addEventListener("DOMContentLoaded", handleOuterSubmit);
|
||||
gBrowser.contentDocument.getElementById("postForm").submit();
|
||||
});
|
||||
|
||||
var framesLoaded = 0;
|
||||
var innerFrame;
|
||||
|
||||
function handleOuterSubmit() {
|
||||
if (++framesLoaded < 2)
|
||||
return;
|
||||
|
||||
gBrowser.removeEventListener("DOMContentLoaded", handleOuterSubmit);
|
||||
|
||||
innerFrame = gBrowser.contentDocument.getElementById("innerFrame");
|
||||
|
||||
// Submit the form in the inner page.
|
||||
gBrowser.addEventListener("DOMContentLoaded", handleInnerSubmit);
|
||||
innerFrame.contentDocument.getElementById("postForm").submit();
|
||||
}
|
||||
|
||||
function handleInnerSubmit() {
|
||||
gBrowser.removeEventListener("DOMContentLoaded", handleInnerSubmit);
|
||||
|
||||
// Create the folder the page will be saved into.
|
||||
var destDir = createTemporarySaveDirectory();
|
||||
var file = destDir.clone();
|
||||
file.append("no_default_file_name");
|
||||
MockFilePicker.returnFiles = [file];
|
||||
MockFilePicker.showCallback = function(fp) {
|
||||
MockFilePicker.filterIndex = 1; // kSaveAsType_URL
|
||||
};
|
||||
|
||||
mockTransferCallback = onTransferComplete;
|
||||
mockTransferRegisterer.register();
|
||||
|
||||
registerCleanupFunction(function () {
|
||||
mockTransferRegisterer.unregister();
|
||||
MockFilePicker.cleanup();
|
||||
destDir.remove(true);
|
||||
});
|
||||
|
||||
var docToSave = innerFrame.contentDocument;
|
||||
// We call internalSave instead of saveDocument to bypass the history
|
||||
// cache.
|
||||
internalSave(docToSave.location.href, docToSave, null, null,
|
||||
docToSave.contentType, false, null, null,
|
||||
docToSave.referrer ? makeURI(docToSave.referrer) : null,
|
||||
docToSave, false, null);
|
||||
}
|
||||
|
||||
function onTransferComplete(downloadSuccess) {
|
||||
ok(downloadSuccess, "The inner frame should have been downloaded successfully");
|
||||
|
||||
// Read the entire saved file.
|
||||
var file = MockFilePicker.returnFiles[0];
|
||||
var fileContents = readShortFile(file);
|
||||
|
||||
// Check if outer POST data is found (bug 471962).
|
||||
is(fileContents.indexOf("inputfield=outer"), -1,
|
||||
"The saved inner frame does not contain outer POST data");
|
||||
|
||||
// Check if inner POST data is found (bug 485196).
|
||||
isnot(fileContents.indexOf("inputfield=inner"), -1,
|
||||
"The saved inner frame was generated using the correct POST data");
|
||||
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
|
||||
this);
|
||||
|
||||
function createTemporarySaveDirectory() {
|
||||
var saveDir = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties)
|
||||
.get("TmpD", Ci.nsIFile);
|
||||
saveDir.append("testsavedir");
|
||||
if (!saveDir.exists())
|
||||
saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0o755);
|
||||
return saveDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the contents of the provided short file (up to 1 MiB).
|
||||
*
|
||||
* @param aFile
|
||||
* nsIFile object pointing to the file to be read.
|
||||
*
|
||||
* @return
|
||||
* String containing the raw octets read from the file.
|
||||
*/
|
||||
function readShortFile(aFile) {
|
||||
var inputStream = Cc["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Ci.nsIFileInputStream);
|
||||
inputStream.init(aFile, -1, 0, 0);
|
||||
try {
|
||||
var scrInputStream = Cc["@mozilla.org/scriptableinputstream;1"]
|
||||
.createInstance(Ci.nsIScriptableInputStream);
|
||||
scrInputStream.init(inputStream);
|
||||
try {
|
||||
// Assume that the file is much shorter than 1 MiB.
|
||||
return scrInputStream.read(1048576);
|
||||
}
|
||||
finally {
|
||||
// Close the scriptable stream after reading, even if the operation
|
||||
// failed.
|
||||
scrInputStream.close();
|
||||
}
|
||||
}
|
||||
finally {
|
||||
// Close the stream after reading, if it is still open, even if the read
|
||||
// operation failed.
|
||||
inputStream.close();
|
||||
}
|
||||
}
|
||||
67
toolkit/content/tests/browser/common/mockTransfer.js
Normal file
67
toolkit/content/tests/browser/common/mockTransfer.js
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* 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/. */
|
||||
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://mochikit/content/tests/SimpleTest/MockObjects.js", this);
|
||||
|
||||
var mockTransferCallback;
|
||||
|
||||
/**
|
||||
* This "transfer" object implementation continues the currently running test
|
||||
* when the download is completed, reporting true for success or false for
|
||||
* failure as the first argument of the testRunner.continueTest function.
|
||||
*/
|
||||
function MockTransfer() {
|
||||
this._downloadIsSuccessful = true;
|
||||
}
|
||||
|
||||
MockTransfer.prototype = {
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIWebProgressListener,
|
||||
Ci.nsIWebProgressListener2,
|
||||
Ci.nsITransfer,
|
||||
]),
|
||||
|
||||
/* nsIWebProgressListener */
|
||||
onStateChange: function MTFC_onStateChange(aWebProgress, aRequest,
|
||||
aStateFlags, aStatus) {
|
||||
// If at least one notification reported an error, the download failed.
|
||||
if (!Components.isSuccessCode(aStatus))
|
||||
this._downloadIsSuccessful = false;
|
||||
|
||||
// If the download is finished
|
||||
if ((aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) &&
|
||||
(aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK))
|
||||
// Continue the test, reporting the success or failure condition.
|
||||
mockTransferCallback(this._downloadIsSuccessful);
|
||||
},
|
||||
onProgressChange: function () {},
|
||||
onLocationChange: function () {},
|
||||
onStatusChange: function MTFC_onStatusChange(aWebProgress, aRequest, aStatus,
|
||||
aMessage) {
|
||||
// If at least one notification reported an error, the download failed.
|
||||
if (!Components.isSuccessCode(aStatus))
|
||||
this._downloadIsSuccessful = false;
|
||||
},
|
||||
onSecurityChange: function () {},
|
||||
|
||||
/* nsIWebProgressListener2 */
|
||||
onProgressChange64: function () {},
|
||||
onRefreshAttempted: function () {},
|
||||
|
||||
/* nsITransfer */
|
||||
init: function() {},
|
||||
setSha256Hash: function() {},
|
||||
setSignatureInfo: function() {}
|
||||
};
|
||||
|
||||
// Create an instance of a MockObjectRegisterer whose methods can be used to
|
||||
// temporarily replace the default "@mozilla.org/transfer;1" object factory with
|
||||
// one that provides the mock implementation above. To activate the mock object
|
||||
// factory, call the "register" method. Starting from that moment, all the
|
||||
// transfer objects that are requested will be mock objects, until the
|
||||
// "unregister" method is called.
|
||||
var mockTransferRegisterer =
|
||||
new MockObjectRegisterer("@mozilla.org/transfer;1", MockTransfer);
|
||||
31
toolkit/content/tests/browser/data/post_form_inner.sjs
Normal file
31
toolkit/content/tests/browser/data/post_form_inner.sjs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/* 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/. */
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var body =
|
||||
'<html>\
|
||||
<body>\
|
||||
Inner POST data: ';
|
||||
|
||||
var bodyStream = new BinaryInputStream(request.bodyInputStream);
|
||||
var bytes = [], avail = 0;
|
||||
while ((avail = bodyStream.available()) > 0)
|
||||
body += String.fromCharCode.apply(String, bodyStream.readByteArray(avail));
|
||||
|
||||
body +=
|
||||
'<form id="postForm" action="post_form_inner.sjs" method="post">\
|
||||
<input type="text" name="inputfield" value="inner">\
|
||||
<input type="submit">\
|
||||
</form>\
|
||||
</body>\
|
||||
</html>';
|
||||
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
34
toolkit/content/tests/browser/data/post_form_outer.sjs
Normal file
34
toolkit/content/tests/browser/data/post_form_outer.sjs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/* 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/. */
|
||||
|
||||
const CC = Components.Constructor;
|
||||
const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1",
|
||||
"nsIBinaryInputStream",
|
||||
"setInputStream");
|
||||
|
||||
function handleRequest(request, response)
|
||||
{
|
||||
var body =
|
||||
'<html>\
|
||||
<body>\
|
||||
Outer POST data: ';
|
||||
|
||||
var bodyStream = new BinaryInputStream(request.bodyInputStream);
|
||||
var bytes = [], avail = 0;
|
||||
while ((avail = bodyStream.available()) > 0)
|
||||
body += String.fromCharCode.apply(String, bodyStream.readByteArray(avail));
|
||||
|
||||
body +=
|
||||
'<form id="postForm" action="post_form_outer.sjs" method="post">\
|
||||
<input type="text" name="inputfield" value="outer">\
|
||||
<input type="submit">\
|
||||
</form>\
|
||||
\
|
||||
<iframe id="innerFrame" src="post_form_inner.sjs" width="400" height="200">\
|
||||
\
|
||||
</body>\
|
||||
</html>';
|
||||
|
||||
response.bodyOutputStream.write(body, body.length);
|
||||
}
|
||||
BIN
toolkit/content/tests/browser/empty.png
Normal file
BIN
toolkit/content/tests/browser/empty.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
14
toolkit/content/tests/browser/file_contentTitle.html
Normal file
14
toolkit/content/tests/browser/file_contentTitle.html
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<html>
|
||||
<head><title>Test Page</title></head>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
dump("Script!\n");
|
||||
addEventListener("load", () => {
|
||||
// Trigger an onLocationChange event. We want to make sure the title is still correct afterwards.
|
||||
location.hash = "#x2";
|
||||
var event = new Event("TestLocationChange");
|
||||
document.dispatchEvent(event);
|
||||
}, false);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
9
toolkit/content/tests/browser/file_mediaPlayback.html
Normal file
9
toolkit/content/tests/browser/file_mediaPlayback.html
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html>
|
||||
<script type="text/javascript">
|
||||
var audio = new Audio();
|
||||
audio.oncanplay = function() {
|
||||
audio.oncanplay = null;
|
||||
audio.play();
|
||||
};
|
||||
audio.src = "audio.ogg";
|
||||
</script>
|
||||
12
toolkit/content/tests/browser/file_mediaPlayback2.html
Normal file
12
toolkit/content/tests/browser/file_mediaPlayback2.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
var audio = new Audio();
|
||||
audio.oncanplay = function() {
|
||||
audio.oncanplay = null;
|
||||
audio.play();
|
||||
};
|
||||
audio.src = "audio.ogg";
|
||||
document.body.appendChild(audio);
|
||||
</script>
|
||||
</body>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<iframe src="file_mediaPlayback.html"></iframe>
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE html>
|
||||
<iframe src="file_mediaPlayback2.html"></iframe>
|
||||
19
toolkit/content/tests/browser/file_multipleAudio.html
Normal file
19
toolkit/content/tests/browser/file_multipleAudio.html
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||
<meta content="utf-8" http-equiv="encoding">
|
||||
</head>
|
||||
<body>
|
||||
<audio id="autoplay" src="audio.ogg"></audio>
|
||||
<audio id="nonautoplay" src="audio.ogg"></audio>
|
||||
<script type="text/javascript">
|
||||
|
||||
// In linux debug on try server, sometimes the download process would fail, so
|
||||
// we can't activate the "auto-play" or playing after receving "oncanplay".
|
||||
// Therefore, we just call play here.
|
||||
var audio = document.getElementById("autoplay");
|
||||
audio.loop = true;
|
||||
audio.play();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
23
toolkit/content/tests/browser/file_multiplePlayingAudio.html
Normal file
23
toolkit/content/tests/browser/file_multiplePlayingAudio.html
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<head>
|
||||
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
||||
<meta content="utf-8" http-equiv="encoding">
|
||||
</head>
|
||||
<body>
|
||||
<audio id="audio1" src="audio.ogg" controls></audio>
|
||||
<audio id="audio2" src="audio.ogg" controls></audio>
|
||||
<script type="text/javascript">
|
||||
|
||||
// In linux debug on try server, sometimes the download process would fail, so
|
||||
// we can't activate the "auto-play" or playing after receving "oncanplay".
|
||||
// Therefore, we just call play here.
|
||||
var audio1 = document.getElementById("audio1");
|
||||
audio1.loop = true;
|
||||
audio1.play();
|
||||
|
||||
var audio2 = document.getElementById("audio2");
|
||||
audio2.loop = true;
|
||||
audio2.play();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue