diff options
Diffstat (limited to '')
24 files changed, 1315 insertions, 0 deletions
diff --git a/browser/fxr/content/assets/icon-backward.svg b/browser/fxr/content/assets/icon-backward.svg new file mode 100644 index 0000000000..a824b50cad --- /dev/null +++ b/browser/fxr/content/assets/icon-backward.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="context-fill"><path d="M87.53,174a8.23,8.23,0,0,1-5.61-2.19L11.3,106.63a8.28,8.28,0,0,1,0-12.18L81.92,29.31A8.28,8.28,0,0,1,93.15,41.48l-64,59.06,64,59.06A8.28,8.28,0,0,1,87.53,174Z"/><path d="M182.39,108.82H16.91a8.28,8.28,0,0,1,0-16.56H182.39a8.28,8.28,0,1,1,0,16.56Z"/></svg>
diff --git a/browser/fxr/content/assets/icon-forward.svg b/browser/fxr/content/assets/icon-forward.svg new file mode 100644 index 0000000000..0c7bc478e1 --- /dev/null +++ b/browser/fxr/content/assets/icon-forward.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="context-fill"><path d="M112,174.23a8.28,8.28,0,0,1-5.62-14.37l64-59.06-64-59.06a8.28,8.28,0,0,1,11.23-12.17l70.62,65.14a8.28,8.28,0,0,1,0,12.18L117.65,172A8.29,8.29,0,0,1,112,174.23Z"/><path d="M182.65,109.08H17.18a8.28,8.28,0,1,1,0-16.56H182.65a8.28,8.28,0,1,1,0,16.56Z"/></svg>
diff --git a/browser/fxr/content/assets/icon-home.svg b/browser/fxr/content/assets/icon-home.svg new file mode 100644 index 0000000000..5643dffad0 --- /dev/null +++ b/browser/fxr/content/assets/icon-home.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="context-fill"><path d="M161.53,192.17H125a8.28,8.28,0,0,1-8.28-8.28v-49H83.63v49a8.28,8.28,0,0,1-8.28,8.28H38.8a8.28,8.28,0,0,1-8.28-8.28V120H18.15a8.29,8.29,0,0,1-6.28-13.69l82-95.21a8.28,8.28,0,0,1,12.55,0l82,95.21A8.29,8.29,0,0,1,182.18,120H169.81v63.94A8.28,8.28,0,0,1,161.53,192.17Zm-28.27-16.56h20V111.67a8.28,8.28,0,0,1,8.28-8.28h2.59l-64-74.25L36.21,103.39H38.8a8.28,8.28,0,0,1,8.28,8.28v63.94h20v-49a8.27,8.27,0,0,1,8.28-8.28H125a8.27,8.27,0,0,1,8.28,8.28Z"/></svg>
diff --git a/browser/fxr/content/assets/icon-logo-settings-preview.png b/browser/fxr/content/assets/icon-logo-settings-preview.png Binary files differnew file mode 100644 index 0000000000..0e516aaa7b --- /dev/null +++ b/browser/fxr/content/assets/icon-logo-settings-preview.png diff --git a/browser/fxr/content/assets/icon-refresh.svg b/browser/fxr/content/assets/icon-refresh.svg new file mode 100644 index 0000000000..afcd3baab2 --- /dev/null +++ b/browser/fxr/content/assets/icon-refresh.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="context-fill"><path d="M182.14,25.22a8.43,8.43,0,0,0-9.07-8.38,8.72,8.72,0,0,0-8.38,9.06l1.67,43A78.45,78.45,0,1,0,93.91,177.18a78,78,0,0,0,62.83-31.39,8.73,8.73,0,1,0-14-10.5,61.07,61.07,0,1,1,9.47-54.82c.1.3.32.52.44.8H110.36a8.73,8.73,0,0,0,0,17.46h64.31a8.09,8.09,0,0,0,1.09-.22c.05,0,.09,0,.14,0h.35a8.72,8.72,0,0,0,8.38-9.06Z"/></svg>
diff --git a/browser/fxr/content/assets/icon-reportissue.svg b/browser/fxr/content/assets/icon-reportissue.svg new file mode 100644 index 0000000000..4c563c848c --- /dev/null +++ b/browser/fxr/content/assets/icon-reportissue.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 21 20" fill="context-fill"><path d="M11.62,9.51a1.13,1.13,0,1,1-2.25,0V5a1.13,1.13,0,1,1,2.25,0ZM10.5,15.14a1.13,1.13,0,1,1,0-2.25,1.13,1.13,0,0,1,0,2.25ZM10.5.52C4.91.52.38,4.62.38,9.66A8.52,8.52,0,0,0,2,14.6a1.91,1.91,0,0,1,.17.24,1.34,1.34,0,0,1,.16.6c0,.08-.95,3.43-.95,3.43a.5.5,0,0,0,.4.59.32.32,0,0,0,.13,0,1.38,1.38,0,0,0,.2,0l.08,0,2.67-1.3.53-.23A1.63,1.63,0,0,1,6,17.77a1.61,1.61,0,0,1,.54.09,0,0,0,0,1,0,0l.08,0a11.15,11.15,0,0,0,4,.61c5.59,0,10.07-3.8,10.07-8.85S16.09.52,10.5.52Z"/></svg>
\ No newline at end of file diff --git a/browser/fxr/content/assets/icon-secure.svg b/browser/fxr/content/assets/icon-secure.svg new file mode 100644 index 0000000000..d0231acca3 --- /dev/null +++ b/browser/fxr/content/assets/icon-secure.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="context-fill"><rect x="44.73" y="90.19" width="110.54" height="76.6" rx="8.91"/><path d="M102.1,52.28A16.14,16.14,0,0,1,118,68.57v32.3a16.14,16.14,0,0,1-15.93,16.29H97.9A16.13,16.13,0,0,1,82,100.87V68.57A16.13,16.13,0,0,1,97.9,52.28h4.2m0-19.07H97.9c-19,0-34.57,15.91-34.57,35.36v32.3c0,19.45,15.55,35.36,34.57,35.36h4.2c19,0,34.58-15.91,34.58-35.36V68.57c0-19.45-15.56-35.36-34.58-35.36Z"/></svg>
diff --git a/browser/fxr/content/assets/icon-settings.svg b/browser/fxr/content/assets/icon-settings.svg new file mode 100644 index 0000000000..f719544a06 --- /dev/null +++ b/browser/fxr/content/assets/icon-settings.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. --> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 19 19" fill="context-fill"><path d="M17.57,9.5l.95-1.24a1.82,1.82,0,0,0,.25-1.81L18.21,5.1A1.85,1.85,0,0,0,16.76,4l-1.55-.2L15,2.24A1.85,1.85,0,0,0,13.9.79L12.55.23a1.86,1.86,0,0,0-.7-.14,1.77,1.77,0,0,0-1.11.39l-1.24,1L8.26.48A1.77,1.77,0,0,0,7.15.09a1.86,1.86,0,0,0-.7.14L5.1.79A1.85,1.85,0,0,0,4,2.24l-.2,1.55L2.24,4A1.85,1.85,0,0,0,.79,5.1L.23,6.45A1.84,1.84,0,0,0,.47,8.26l1,1.24-1,1.24a1.84,1.84,0,0,0-.24,1.81L.79,13.9A1.85,1.85,0,0,0,2.24,15l1.55.2L4,16.76A1.85,1.85,0,0,0,5.1,18.21l1.35.56a1.86,1.86,0,0,0,.7.14,1.82,1.82,0,0,0,1.11-.38l1.24-1,1.24.95a1.77,1.77,0,0,0,1.11.39,1.86,1.86,0,0,0,.7-.14l1.35-.56A1.85,1.85,0,0,0,15,16.76l.2-1.55,1.55-.2a1.85,1.85,0,0,0,1.45-1.11l.56-1.35a1.82,1.82,0,0,0-.25-1.81Zm-.49,2.35-.56,1.35-2.22.28-.82.82-.28,2.22-1.35.56-1.77-1.37H8.92L7.15,17.08,5.8,16.52,5.52,14.3l-.82-.82L2.48,13.2l-.56-1.35,1.37-1.77V8.92L1.92,7.15,2.48,5.8,4.7,5.52l.82-.82L5.8,2.48l1.35-.56L8.92,3.29h1.16l1.77-1.37,1.35.56.28,2.22.82.82,2.22.28.56,1.35L15.71,8.92v1.16Z"/><path d="M9.5,5.2a4.3,4.3,0,1,0,4.3,4.3A4.31,4.31,0,0,0,9.5,5.2ZM9.5,12A2.48,2.48,0,1,1,12,9.5,2.48,2.48,0,0,1,9.5,12Z"/></svg>
\ No newline at end of file diff --git a/browser/fxr/content/assets/icon-stop-reload.svg b/browser/fxr/content/assets/icon-stop-reload.svg new file mode 100644 index 0000000000..662810ab3d --- /dev/null +++ b/browser/fxr/content/assets/icon-stop-reload.svg @@ -0,0 +1,4 @@ +<!-- 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 https://mozilla.org/MPL/2.0/. -->
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" fill="context-fill"><path d="M163.4,170.84a8,8,0,0,1-5.66-2.33L30.93,42.92A8,8,0,0,1,42.26,31.49L169.07,157.08a8,8,0,0,1-5.67,13.76Z"/><path d="M36.86,171.11a8,8,0,0,1-5.68-13.74L157.45,31.25a8,8,0,1,1,11.37,11.38L42.55,168.76A8.06,8.06,0,0,1,36.86,171.11Z"/></svg>
diff --git a/browser/fxr/content/assets/icon-toggle-off.png b/browser/fxr/content/assets/icon-toggle-off.png Binary files differnew file mode 100644 index 0000000000..398a42c9bf --- /dev/null +++ b/browser/fxr/content/assets/icon-toggle-off.png diff --git a/browser/fxr/content/assets/icon-toggle-on.png b/browser/fxr/content/assets/icon-toggle-on.png Binary files differnew file mode 100644 index 0000000000..f22f231628 --- /dev/null +++ b/browser/fxr/content/assets/icon-toggle-on.png diff --git a/browser/fxr/content/common.css b/browser/fxr/content/common.css new file mode 100644 index 0000000000..512be5aee7 --- /dev/null +++ b/browser/fxr/content/common.css @@ -0,0 +1,44 @@ +/* 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/. */ + + * { + margin: 0; + padding: 0; + user-select: none; + + font-family: 'Open Sans', sans-serif; +} + +.modal_mask:not([hidden]) { + background-color: #00CC00; + + height: 100%; + width: 100%; + + position: absolute; + top: 0; + left: 0; +} + +.modal_container:not([hidden]) { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + + height: 100%; + width: 100%; + + position: absolute; + top: 0; + left :0; +} + +.modal_hide { + display: none; +} + +.modal_content { + position: absolute; +} diff --git a/browser/fxr/content/common.js b/browser/fxr/content/common.js new file mode 100644 index 0000000000..292c0a916a --- /dev/null +++ b/browser/fxr/content/common.js @@ -0,0 +1,48 @@ +/* -*- 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/. */ + +// Creates a modal container, if it doesn't exist, and adds the provided +// content element to it +function showModalContainer(content) { + var container = document.getElementById("eModalContainer"); + if (container == null) { + container = document.createElement("div"); + container.id = "eModalContainer"; + container.classList.add("modal_container"); + + var mask = document.createElement("div"); + mask.id = "eModalMask"; + mask.classList.add("modal_mask"); + + document.body.appendChild(mask); + document.body.appendChild(container); + } else { + container.hidden = false; + document.getElementById("eModalMask").hidden = false; + } + + container.appendChild(content); + if (content.classList.contains("modal_hide")) { + content.classList.replace("modal_hide", "modal_content"); + } else { + content.classList.add("modal_content"); + } +} + +// Hides the modal container, and returns the contents back to the caller. +// The caller can choose to use the return value to move the contents to +// another part of the DOM, or ignore the return value so that the nodes +// can be garbage collected. +function clearModalContainer() { + var container = document.getElementById("eModalContainer"); + container.hidden = true; + document.getElementById("eModalMask").hidden = true; + + var content = container.firstElementChild; + container.removeChild(content); + content.classList.replace("modal_content", "modal_hide"); + + return content; +} diff --git a/browser/fxr/content/fxr-fullScreen.js b/browser/fxr/content/fxr-fullScreen.js new file mode 100644 index 0000000000..dd7a933d89 --- /dev/null +++ b/browser/fxr/content/fxr-fullScreen.js @@ -0,0 +1,64 @@ +/* -*- 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/. */ + +// This file is loaded into the browser window scope. +/* eslint-env mozilla/browser-window */ + +// fxr-fullScreen.js is a provisional, stripped-down clone of +// browser\base\content\browser-fullScreenAndPointerLock.js +// that is adapted for Firefox Reality on Desktop. +// The bug to track its removal is +// Bug 1587946 - Rationalize the fork of browser-fullScreenAndPointerLock.js + +var FullScreen = { + init() { + // Called when the Firefox window go into fullscreen. + addEventListener("fullscreen", this, true); + + if (window.fullScreen) { + this.toggle(); + } + }, + + toggle() { + var enterFS = window.fullScreen; + if (enterFS) { + document.documentElement.setAttribute("inFullscreen", true); + } else { + document.documentElement.removeAttribute("inFullscreen"); + } + }, + + handleEvent(event) { + if (event.type === "fullscreen") { + this.toggle(); + } + }, + + enterDomFullscreen(aBrowser, aActor) { + if (!document.fullscreenElement) { + return; + } + + // If it is a remote browser, send a message to ask the content + // to enter fullscreen state. We don't need to do so if it is an + // in-process browser, since all related document should have + // entered fullscreen state at this point. + // This should be done before the active tab check below to ensure + // that the content document handles the pending request. Doing so + // before the check is fine since we also check the activeness of + // the requesting document in content-side handling code. + if (aBrowser.isRemoteBrowser) { + aActor.sendAsyncMessage("DOMFullscreen:Entered", {}); + } + + document.documentElement.setAttribute("inDOMFullscreen", true); + }, + + cleanupDomFullscreen(aActor) { + aActor.sendAsyncMessage("DOMFullscreen:CleanUp", {}); + document.documentElement.removeAttribute("inDOMFullscreen"); + }, +}; diff --git a/browser/fxr/content/fxrui.css b/browser/fxr/content/fxrui.css new file mode 100644 index 0000000000..42eee7f5ab --- /dev/null +++ b/browser/fxr/content/fxrui.css @@ -0,0 +1,146 @@ +/* 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 xul url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"); + +html, body { + height: 100%; +} + + +body { + display: flex; + flex-direction: column; +} + +.navbar_container { + width: 100%; + + margin-block: 15px; + + display: flex; + flex-direction: row; +} + +/* Sizing and positioning properties for all icons + Color is determined by enabled/disabled classes below + */ +.icon_container { + width: 44px; + height: 44px; + margin: 5px; + + border-radius: 22px; + border-width: 2px; + border-style: solid; + + background-size: 22px; + background-repeat: no-repeat; + background-position: center; + background-color: inherit; + + -moz-context-properties: fill; + + transition-property: transform; + transition-duration: 200ms; +} + +.icon_container:hover { + transform: scale(1.25); +} + +.icon_container:disabled { + transform: unset; +} + +.icon_disabled_hide:disabled { + display:none; +} +.icon_backward { + background-image: url("assets/icon-backward.svg"); + margin-inline-start: 10px; +} +.icon_forward { + background-image: url("assets/icon-forward.svg"); +} +.icon_refresh { + background-image: url("assets/icon-refresh.svg"); +} +.icon_stop { + background-image: url("assets/icon-stop-reload.svg"); +} +.icon_home { + background-image: url("assets/icon-home.svg"); +} +.icon_prefs { + background-image: url("assets/icon-settings.svg"); + margin-inline-end: 10px; +} + +.urlbar_container { + height: 40px; + flex-grow: 1; + + padding: 0; + margin: 5px; + + border-radius: 22px; + border: 2px solid transparent; + + vertical-align: top; + + display: flex; + flex-direction: row; +} + +.urlbar_secure_icon { + -moz-context-properties: fill; + + height: 32px; + padding: 2px; + + visibility: hidden; + + display: inline-block; +} + +.urlbar_input { + background-color: transparent; + + flex-grow: 1; + + border: none; + + font-size: 18px; + font-family: 'Open Sans', sans-serif; + + mask-image: linear-gradient(to left, transparent, black 8ch); +} + +.browser_container { + width: 100%; + flex-grow: 1; +} + +.browser_instance { + width: 100%; + height: 100%; +} + +/* Hide the navbar when in fullscreen so that <browser> can + * fill the entire viewport + */ +:root[inFullScreen] .navbar_container { + display:none; +} + +.browser_settings { + width: 600px; + height: 400px; + border-radius: 20px; +} + +.modal_mask:not([hidden]) { + background-color: var(--num13_alpha); +} diff --git a/browser/fxr/content/fxrui.html b/browser/fxr/content/fxrui.html new file mode 100644 index 0000000000..17273d8e77 --- /dev/null +++ b/browser/fxr/content/fxrui.html @@ -0,0 +1,54 @@ +<!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/. --> + +<!-- + This file contains the HTML UI for the 2D window of Firefox Reality on Desktop +--> + +<!-- 1280x720 chosen for default 16:9 ratio --> +<html width="1280" height="720"> + <head> + <title>Firefox Reality</title> + <link rel="stylesheet" href="common.css" /> + <link rel="stylesheet" href="fxrui.css" /> + <link rel="stylesheet" href="fxrui_blue.css" /> + <script src="common.js"></script> + <script src="permissions.js"></script> + <script src="fxrui.js"></script> + </head> + + <body> + <div id="eBrowserContainer" class="browser_container"></div> + + <div class="navbar_container"> + <button id="eBack" class="icon_container icon_backward"></button> + <button id="eForward" class="icon_container icon_forward"></button> + <button + id="eRefresh" + class="icon_container icon_refresh icon_disabled_hide" + ></button> + <button + id="eStop" + class="icon_container icon_stop icon_disabled_hide" + disabled + ></button> + <button id="eHome" class="icon_container icon_home"></button> + + <div + class="urlbar_container urlbar_container_normal" + id="eUrlBarContainer" + > + <img + class="urlbar_secure_icon" + id="eUrlSecure" + src="assets/icon-secure.svg" + alt="Secure" + /> + <input class="urlbar_input" id="eUrlInput" type="text" value="" /> + </div> + <button id="ePrefs" class="icon_container icon_prefs"></button> + </div> + </body> +</html> diff --git a/browser/fxr/content/fxrui.js b/browser/fxr/content/fxrui.js new file mode 100644 index 0000000000..b9b7c58e22 --- /dev/null +++ b/browser/fxr/content/fxrui.js @@ -0,0 +1,288 @@ +/* -*- 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/. */ + +/* import-globals-from common.js */ +/* import-globals-from permissions.js */ + +// Configuration vars +let homeURL = "https://webxr.today/"; +// Bug 1586294 - Localize the privacy policy URL (Services.urlFormatter?) +let privacyPolicyURL = "https://www.mozilla.org/en-US/privacy/firefox/"; +let reportIssueURL = "https://mzl.la/fxr"; +let licenseURL = + "https://mixedreality.mozilla.org/FirefoxRealityPC/license.html"; + +// https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XUL/browser +let browser = null; +// Keep track of the current Permissions request to only allow one outstanding +// request/prompt at a time. +let currentPermissionRequest = null; +// And, keep a queue of pending Permissions requests to resolve when the +// current request finishes +let pendingPermissionRequests = []; +// The following variable map to UI elements whose behavior changes depending +// on some state from the browser control +let urlInput = null; +let secureIcon = null; +let backButton = null; +let forwardButton = null; +let refreshButton = null; +let stopButton = null; + +const { PrivateBrowsingUtils } = ChromeUtils.importESModule( + "resource://gre/modules/PrivateBrowsingUtils.sys.mjs" +); +const { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); +const { XPCOMUtils } = ChromeUtils.importESModule( + "resource://gre/modules/XPCOMUtils.sys.mjs" +); + +// Note: FxR UI uses a fork of browser-fullScreenAndPointerLock.js which removes +// the dependencies on browser.js. +// Bug 1587946 - Rationalize the fork of browser-fullScreenAndPointerLock.js +XPCOMUtils.defineLazyScriptGetter( + this, + "FullScreen", + "chrome://fxr/content/fxr-fullScreen.js" +); +XPCOMUtils.defineLazyGetter(this, "gSystemPrincipal", () => + Services.scriptSecurityManager.getSystemPrincipal() +); + +window.addEventListener( + "DOMContentLoaded", + () => { + urlInput = document.getElementById("eUrlInput"); + secureIcon = document.getElementById("eUrlSecure"); + backButton = document.getElementById("eBack"); + forwardButton = document.getElementById("eForward"); + refreshButton = document.getElementById("eRefresh"); + stopButton = document.getElementById("eStop"); + + setupBrowser(); + setupNavButtons(); + setupUrlBar(); + }, + { once: true } +); + +// Create XUL browser object +function setupBrowser() { + // Note: createXULElement is undefined when this page is not loaded + // via chrome protocol + if (document.createXULElement) { + browser = document.createXULElement("browser"); + browser.setAttribute("type", "content"); + browser.setAttribute("remote", "true"); + browser.classList.add("browser_instance"); + document.getElementById("eBrowserContainer").appendChild(browser); + + browser.loadUrlWithSystemPrincipal = function (url) { + this.loadURI(url, { triggeringPrincipal: gSystemPrincipal }); + }; + + // Expose this function for Permissions to be used on this browser element + // in other parts of the frontend + browser.fxrPermissionPrompt = permissionPrompt; + + urlInput.value = homeURL; + browser.loadUrlWithSystemPrincipal(homeURL); + + browser.addProgressListener( + { + QueryInterface: ChromeUtils.generateQI([ + "nsIWebProgressListener", + "nsISupportsWeakReference", + ]), + onLocationChange(aWebProgress, aRequest, aLocation, aFlags) { + // When URL changes, update the URL in the URL bar and update + // whether the back/forward buttons are enabled. + urlInput.value = browser.currentURI.spec; + + backButton.disabled = !browser.canGoBack; + forwardButton.disabled = !browser.canGoForward; + }, + onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) { + if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) { + // Network requests are complete. Disable (hide) the stop button + // and enable (show) the refresh button + refreshButton.disabled = false; + stopButton.disabled = true; + } else { + // Network requests are outstanding. Disable (hide) the refresh + // button and enable (show) the stop button + refreshButton.disabled = true; + stopButton.disabled = false; + } + }, + onSecurityChange(aWebProgress, aRequest, aState) { + // Update the Secure Icon when the security status of the + // content changes + if (aState & Ci.nsIWebProgressListener.STATE_IS_SECURE) { + secureIcon.style.visibility = "visible"; + } else { + secureIcon.style.visibility = "hidden"; + } + }, + }, + Ci.nsIWebProgress.NOTIFY_LOCATION | + Ci.nsIWebProgress.NOTIFY_SECURITY | + Ci.nsIWebProgress.NOTIFY_STATE_REQUEST + ); + + FullScreen.init(); + + // Send this notification to start and allow background scripts for + // WebExtensions, since this FxR UI doesn't participate in typical + // startup activities + Services.obs.notifyObservers(window, "extensions-late-startup"); + } +} + +function setupNavButtons() { + let aryNavButtons = [ + "eBack", + "eForward", + "eRefresh", + "eStop", + "eHome", + "ePrefs", + ]; + + function navButtonHandler(e) { + if (!this.disabled) { + switch (this.id) { + case "eBack": + browser.goBack(); + break; + + case "eForward": + browser.goForward(); + break; + + case "eRefresh": + browser.reload(); + break; + + case "eStop": + browser.stop(); + break; + + case "eHome": + browser.loadUrlWithSystemPrincipal(homeURL); + break; + + case "ePrefs": + openSettings(); + break; + } + } + } + + for (let btnName of aryNavButtons) { + let elem = document.getElementById(btnName); + elem.addEventListener("click", navButtonHandler); + } +} + +function setupUrlBar() { + // Navigate to new value when the user presses "Enter" + urlInput.addEventListener("keypress", async function (e) { + if (e.key == "Enter") { + // Use the URL Fixup Service in case the user wants to search instead + // of directly navigating to a location. + await Services.search.init(); + + let valueToFixUp = urlInput.value; + let flags = + Services.uriFixup.FIXUP_FLAG_FIX_SCHEME_TYPOS | + Services.uriFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP; + if (PrivateBrowsingUtils.isWindowPrivate(window)) { + flags |= Services.uriFixup.FIXUP_FLAG_PRIVATE_CONTEXT; + } + let { preferredURI } = Services.uriFixup.getFixupURIInfo( + valueToFixUp, + flags + ); + + browser.loadUrlWithSystemPrincipal(preferredURI.spec); + browser.focus(); + } + }); + + // Upon focus, highlight the whole URL + urlInput.addEventListener("focus", function () { + urlInput.select(); + }); +} + +// +// Code to manage Settings UI +// + +function openSettings() { + let browserSettingsUI = document.createXULElement("browser"); + browserSettingsUI.setAttribute("type", "chrome"); + browserSettingsUI.classList.add("browser_settings"); + + showModalContainer(browserSettingsUI); + + browserSettingsUI.loadURI("chrome://fxr/content/prefs.html", { + triggeringPrincipal: gSystemPrincipal, + }); +} + +function closeSettings() { + clearModalContainer(); +} + +function showPrivacyPolicy() { + closeSettings(); + browser.loadUrlWithSystemPrincipal(privacyPolicyURL); +} + +function showLicenseInfo() { + closeSettings(); + browser.loadUrlWithSystemPrincipal(licenseURL); +} + +function showReportIssue() { + closeSettings(); + browser.loadUrlWithSystemPrincipal(reportIssueURL); +} + +// +// Code to manage Permissions UI +// + +function permissionPrompt(aRequest) { + let newPrompt; + if (aRequest instanceof Ci.nsIContentPermissionRequest) { + newPrompt = new FxrContentPrompt(aRequest, this, finishPrompt); + } else { + newPrompt = new FxrWebRTCPrompt(aRequest, this, finishPrompt); + } + + if (currentPermissionRequest) { + // There is already an outstanding request running. Cache this new request + // to be prompted later + pendingPermissionRequests.push(newPrompt); + } else { + currentPermissionRequest = newPrompt; + currentPermissionRequest.showPrompt(); + } +} + +function finishPrompt() { + if (pendingPermissionRequests.length) { + // Prompt the next request + currentPermissionRequest = pendingPermissionRequests.shift(); + currentPermissionRequest.showPrompt(); + } else { + currentPermissionRequest = null; + } +} diff --git a/browser/fxr/content/fxrui_blue.css b/browser/fxr/content/fxrui_blue.css new file mode 100644 index 0000000000..c65e7e7919 --- /dev/null +++ b/browser/fxr/content/fxrui_blue.css @@ -0,0 +1,72 @@ +/* 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/. */ + + /* These not-very-descriptive variable names for colors come + * from the design documents and are maintained to make it + * easier to map between the stylesheet and the design doc. + */ +:root { + --num01: #1D2E3B; + --num02: #4C495C; + --num03: #7D8896; + --num04: #B3BECC; + --num06: #FFFFFF; + --num07: #00B3E3; + --num08: #56D9F6; + --num09: #0968B6; + --num13: #2C3A50; + --num13_alpha: #2C3A5080; + --num14: #596677; + --num14_alpha: #59667780; + --num16: #E7EAF0; + --num19: #FFFFFF; + + --secure: #f7ce4d; +} + +body { + background-color: var(--num01); + color: var(--num06); +} + +.icon_container { + background-color: var(--num02); + border-color: transparent; + fill: var(--num06); +} +.icon_container:hover { + background-color: var(--num01); + fill: var(--num06); +} +.icon_container:active { + background-color: var(--num04); + fill: var(--num06); +} +.icon_container:disabled { + background-color: var(--num16); + fill: var(--num14); +} + +.urlbar_container { + border-color: transparent; + background-color: var(--num02); +} +.urlbar_container:hover { + border-color:var(--num04); +} +.urlbar_container:focus-within { + border-color:var(--num08); + background-color: var(--num01); +} + +.urlbar_secure_icon { + fill: var(--secure); +} + +.urlbar_input { + color: var(--num06); +} +.urlbar_input::selection { + background-color: var(--num08); +} diff --git a/browser/fxr/content/permissions.js b/browser/fxr/content/permissions.js new file mode 100644 index 0000000000..a8f7ece131 --- /dev/null +++ b/browser/fxr/content/permissions.js @@ -0,0 +1,146 @@ +/* -*- 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/. */ + +/* import-globals-from fxrui.js */ + +/** + * Code to manage Permissions UI + * + * FxR on Desktop only supports granting permission for + * - Location + * - Camera + * - Microphone + * Any other permissions are automatically denied. + * + */ + +// Base class for managing permissions in FxR on PC +class FxrPermissionPromptPrototype { + constructor(aRequest, aBrowser, aCallback) { + this.request = aRequest; + this.targetBrowser = aBrowser; + this.responseCallback = aCallback; + } + + showPrompt() { + // For now, all permissions default to denied. Testing for allow must be + // done manually until UI is finished: + // Bug 1594840 - Add UI for Web Permissions in FxR for Desktop + this.defaultDeny(); + } + + defaultDeny() { + this.handleResponse(false); + } + + handleResponse(allowed) { + if (allowed) { + this.allow(); + } else { + this.deny(); + } + + this.responseCallback(); + } +} + +// WebRTC-specific class implementation +class FxrWebRTCPrompt extends FxrPermissionPromptPrototype { + showPrompt() { + for (let typeName of this.request.requestTypes) { + if (typeName !== "Microphone" && typeName !== "Camera") { + // Only Microphone and Camera requests are allowed. Automatically deny + // any other request. + this.defaultDeny(); + return; + } + } + + super.showPrompt(); + } + + allow() { + let { audioDevices, videoDevices } = this.request; + + let principal = + Services.scriptSecurityManager.createContentPrincipalFromOrigin( + this.request.origin + ); + + // For now, collect the first audio and video device by default. User + // selection will be enabled later: + // Bug 1594841 - Add UI to select device for WebRTC in FxR for Desktop + let allowedDevices = []; + + if (audioDevices.length) { + allowedDevices.push(audioDevices[0].deviceIndex); + } + + if (videoDevices.length) { + Services.perms.addFromPrincipal( + principal, + "MediaManagerVideo", + Services.perms.ALLOW_ACTION, + Services.perms.EXPIRE_SESSION + ); + allowedDevices.push(videoDevices[0].deviceIndex); + } + + // WebRTCChild doesn't currently care which actor + // this is sent to and just uses the windowID. + this.targetBrowser.sendMessageToActor( + "webrtc:Allow", + { + callID: this.request.callID, + windowID: this.request.windowID, + devices: allowedDevices, + }, + "WebRTC" + ); + } + + deny() { + this.targetBrowser.sendMessageToActor( + "webrtc:Deny", + { + callID: this.request.callID, + windowID: this.request.windowID, + }, + "WebRTC" + ); + } +} + +// Implementation for other, non-WebRTC web permission prompts +class FxrContentPrompt extends FxrPermissionPromptPrototype { + showPrompt() { + // Only allow exactly one permission request here. + let types = this.request.types.QueryInterface(Ci.nsIArray); + if (types.length != 1) { + this.defaultDeny(); + return; + } + + // Only Location is supported from this type of request + let type = types.queryElementAt(0, Ci.nsIContentPermissionType).type; + if (type !== "geolocation") { + this.defaultDeny(); + return; + } + + // Override type so that it can be more easily interpreted by the code + // for the prompt. + type = "Location"; + super.showPrompt(); + } + + allow() { + this.request.allow(); + } + + deny() { + this.request.cancel(); + } +} diff --git a/browser/fxr/content/prefs.css b/browser/fxr/content/prefs.css new file mode 100644 index 0000000000..dd7099df24 --- /dev/null +++ b/browser/fxr/content/prefs.css @@ -0,0 +1,185 @@ +/* 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/. */ + +body { + margin: 10px 50px; + background-color: var(--num06); + color: var(--num13); +} + +.plain_button { + border: none; + background-color: transparent; +} + +.settings_header{ + display: flex; + flex-direction: row; +} + +.icon_backward { + height: 30px; + width: 30px; + + margin: 20px 70px 70px 20px; + + background-image: url("assets/icon-backward.svg"); + -moz-context-properties: fill; + fill: var(--num14); +} +.icon_backward:hover { + height: 40px; + width: 40px; + margin: 15px 65px 65px 15px; + + fill: var(--num07); +} +.icon_backward:active { + height: 40px; + width: 40px; + margin: 15px 65px 65px 15px; + + fill: var(--num09); +} + +.about_container { + flex-grow: 1; + text-align: center; +} + +.fxr_logo { + width: 72px; + height: 72px; +} + +.version_text { + font-size: 10px; + color: var(--num14); +} + +.button_report_issue { + height: 50px; + width: 110px; + margin: 20px 0px 70px 10px; + + font-size: 10px; + text-align: start; + + -moz-context-properties: fill; + fill: var(--num14); +} +.button_report_issue:hover { + fill: var(--num07); +} +.button_report_issue:active { + fill: var(--num09); +} + +.button_report_issue_icon { + float:left; + height: 30px; + margin: 5px; +} + +.button_report_text { + color: var(--num13); +} + +.button_report_url { + color: var(--num14); +} + +.settings_title { + font-size: 22px; + font-weight: bold; + color: var(--num13); + text-align: center; + + flex-grow: 1; +} + +.divider { + border-style: solid; + border-width: 1px; + border-color: var(--num04); + margin: 10px; +} + +.setting_container { + display: flex; + flex-direction: row; + + margin: 10px 5px; +} + +.setting_name { + font-size: 14px; + color: var(--num13); + + flex-grow: 1; +} + +.setting_description { + font-size: 10px; + color: var(--num14); + margin: 0 10px; +} + +.setting_control { + min-width: 120px; + margin: 5px 0px; +} + +button.setting_control { + height: 30px; + + font-size: 14px; + font-weight: bold; + + border: 2px solid var(--num08); + border-radius: 5px; + + background-color: var(--num06); +} +button.setting_control:hover { + background-color: var(--num07); + border-color: var(--num07); +} +button.setting_control:active { + background-color: var(--num09); + border-color: var(--num09); + color: var(--num19); +} +button.setting_control:disabled { + background-color: var(--num06); + border-color: var(--num08); + color: var(--num03); +} + +.clear_confirmation { + padding: 20px; + background-color: var(--num19); + border-radius: 20px; +} + +.setting_control_chk { + opacity: 0; +} +.setting_control_chk + label { + background-position: right; + background-size: contain; + background-repeat: no-repeat; + + background-image: url("assets/icon-toggle-off.png"); +} +.setting_control_chk:checked + label { + background-image: url("assets/icon-toggle-on.png"); +} +.setting_control_chk:disabled + label { + filter: grayscale(1); +} + +.modal_mask:not([hidden]) { + background-color: var(--num14_alpha); +} diff --git a/browser/fxr/content/prefs.html b/browser/fxr/content/prefs.html new file mode 100644 index 0000000000..d536219901 --- /dev/null +++ b/browser/fxr/content/prefs.html @@ -0,0 +1,80 @@ +<!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/. --> + +<!-- + This file contains the Settings UI for Firefox Reality on Desktop +--> + +<html width="800" height="400"> + <head> + <title>Firefox Reality Settings</title> + <link rel="stylesheet" href="common.css" /> + <link rel="stylesheet" href="fxrui_blue.css" /> + <link rel="stylesheet" href="prefs.css" /> + <script src="prefs.js"></script> + <script src="common.js"></script> + </head> + + <body> + <div class="settings_header"> + <button id="eCloseSettings" class="plain_button icon_backward"></button> + <div class="about_container"> + <img class="fxr_logo" src="assets/icon-logo-settings-preview.png" /> + <div class="version_text" id="eFxrVersion"></div> + <div class="version_text" id="eFxrDate"></div> + <div class="version_text" id="eFxVersion"></div> + </div> + <button id="eReportIssue" class="plain_button button_report_issue"> + <img + class="button_report_issue_icon" + src="assets/icon-reportissue.svg" + /> + <span class="button_report_text">Report an issue</span> + <br /> + <span class="button_report_url">mzl.la/fxr</span> + </button> + </div> + + <div class="settings_title">Settings</div> + <hr class="divider" /> + + <div class="setting_container"> + <div class="setting_name">Mozilla's Privacy Policy Page</div> + <button id="ePrivacyPolicy" class="setting_control">Open</button> + </div> + + <div class="setting_container"> + <div class="setting_name">Firefox Reality Licensing Information</div> + <button id="eLicenseInfo" class="setting_control">Open</button> + </div> + + <div class="setting_container"> + <div class="setting_name"> + Cookies, Site Data, and Cached Web Content + <div class="setting_description"> + Clearing may sign you out of websites and will require websites to + reload images and data. + </div> + </div> + <button class="setting_control" id="eClearTry">Clear Data</button> + </div> + <div class="clear_confirmation modal_hide" id="eClearPrompt"> + Are you sure you want to clear all data? + <div> + <button id="eClearCancel" class="setting_control">Cancel</button> + <button id="eClearConfirm" class="setting_control">Clear</button> + </div> + </div> + + <div class="setting_container"> + <div class="setting_name"> + Allow Firefox to Anonymously Collect and Use Technical and Interaction + Data + </div> + <input id="eCrashConfig" type="checkbox" class="setting_control_chk" /> + <label class="setting_control" for="eCrashConfig"></label> + </div> + </body> +</html> diff --git a/browser/fxr/content/prefs.js b/browser/fxr/content/prefs.js new file mode 100644 index 0000000000..a5a8c954d9 --- /dev/null +++ b/browser/fxr/content/prefs.js @@ -0,0 +1,118 @@ +/* -*- 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/. */ + +/* import-globals-from common.js */ + +var { AppConstants } = ChromeUtils.importESModule( + "resource://gre/modules/AppConstants.sys.mjs" +); + +const PREF_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; + +window.addEventListener( + "DOMContentLoaded", + () => { + initAboutInfo(); + initClearAllData(); + initSubmitHealthReport(); + initParentDependencies(); + }, + { once: true } +); + +function initAboutInfo() { + // Bug 1586294 - Update FxR Desktop Settings to use Fluent + document.getElementById("eFxrVersion").textContent = "version 0.9"; + document.getElementById("eFxrDate").textContent = "(2019-12-17)"; + document.getElementById("eFxVersion").textContent = + "Firefox version " + Services.appinfo.version; +} + +function initClearAllData() { + let eClearPrompt = document.getElementById("eClearPrompt"); + + let eClearTry = document.getElementById("eClearTry"); + eClearTry.addEventListener("click", () => { + showModalContainer(eClearPrompt); + }); + + // Note: the calls to clearModalContainer below return eClearPrompt + // back to <body> to be reused later, because it is moved into anothe + // element in showModalContainer. + + let eClearCancel = document.getElementById("eClearCancel"); + eClearCancel.addEventListener("click", () => { + document.body.appendChild(clearModalContainer()); + }); + + // When the confirm option is visible, do the work to actually clear the data + document.getElementById("eClearConfirm").addEventListener("click", () => { + Services.clearData.deleteData( + Ci.nsIClearDataService.CLEAR_ALL, + function (aFailedFlags) { + if (aFailedFlags == 0) { + eClearTry.textContent = "Data cleared"; + eClearTry.disabled = true; + document.body.appendChild(clearModalContainer()); + } else { + eClearTry.textContent = "Error"; + } + } + ); + }); +} + +// Based on https://searchfox.org/mozilla-central/source/browser/components/preferences/privacy.js +function initSubmitHealthReport() { + let checkbox = document.getElementById("eCrashConfig"); + + // Telemetry is only sending data if MOZ_TELEMETRY_REPORTING is defined. + // We still want to display the preferences panel if that's not the case, but + // we want it to be disabled and unchecked. + if ( + Services.prefs.prefIsLocked(PREF_UPLOAD_ENABLED) || + !AppConstants.MOZ_TELEMETRY_REPORTING + ) { + checkbox.disabled = true; + } else { + checkbox.addEventListener("change", updateSubmitHealthReport); + + checkbox.checked = + Services.prefs.getBoolPref(PREF_UPLOAD_ENABLED) && + AppConstants.MOZ_TELEMETRY_REPORTING; + } +} + +/** + * Update the health report preference with state from checkbox. + */ +function updateSubmitHealthReport() { + let checkbox = document.getElementById("eCrashConfig"); + Services.prefs.setBoolPref(PREF_UPLOAD_ENABLED, checkbox.checked); +} + +// prefs.html can be loaded as another <browser> from fxrui.html. In this +// scenario, some actions are propogated to the parent +function initParentDependencies() { + if (window.parent != window) { + // Close the <browser> instance that loaded this page + document.getElementById("eCloseSettings").addEventListener("click", () => { + window.parent.closeSettings(); + }); + + // Load the relevant URLs into the top UI's <browser> + document.getElementById("ePrivacyPolicy").addEventListener("click", () => { + window.parent.showPrivacyPolicy(); + }); + + document.getElementById("eLicenseInfo").addEventListener("click", () => { + window.parent.showLicenseInfo(); + }); + + document.getElementById("eReportIssue").addEventListener("click", () => { + window.parent.showReportIssue(); + }); + } +} diff --git a/browser/fxr/jar.mn b/browser/fxr/jar.mn new file mode 100644 index 0000000000..89f4da5702 --- /dev/null +++ b/browser/fxr/jar.mn @@ -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/. + +#if defined(NIGHTLY_BUILD) +browser.jar: +% content fxr %content/browser/fxr/ + content/browser/fxr/common.css (content/common.css) + content/browser/fxr/common.js (content/common.js) + content/browser/fxr/fxrui.html (content/fxrui.html) + content/browser/fxr/fxrui.css (content/fxrui.css) + content/browser/fxr/fxrui_blue.css (content/fxrui_blue.css) + content/browser/fxr/fxrui.js (content/fxrui.js) + content/browser/fxr/fxr-fullScreen.js (content/fxr-fullScreen.js) + content/browser/fxr/permissions.js (content/permissions.js) + content/browser/fxr/prefs.html (content/prefs.html) + content/browser/fxr/prefs.css (content/prefs.css) + content/browser/fxr/prefs.js (content/prefs.js) + + content/browser/fxr/assets/icon-backward.svg (content/assets/icon-backward.svg) + content/browser/fxr/assets/icon-forward.svg (content/assets/icon-forward.svg) + content/browser/fxr/assets/icon-home.svg (content/assets/icon-home.svg) + content/browser/fxr/assets/icon-logo-settings-preview.png (content/assets/icon-logo-settings-preview.png) + content/browser/fxr/assets/icon-refresh.svg (content/assets/icon-refresh.svg) + content/browser/fxr/assets/icon-reportissue.svg (content/assets/icon-reportissue.svg) + content/browser/fxr/assets/icon-secure.svg (content/assets/icon-secure.svg) + content/browser/fxr/assets/icon-settings.svg (content/assets/icon-settings.svg) + content/browser/fxr/assets/icon-stop-reload.svg (content/assets/icon-stop-reload.svg) + content/browser/fxr/assets/icon-toggle-off.png (content/assets/icon-toggle-off.png) + content/browser/fxr/assets/icon-toggle-on.png (content/assets/icon-toggle-on.png) +#endif diff --git a/browser/fxr/moz.build b/browser/fxr/moz.build new file mode 100644 index 0000000000..d988c0ff9b --- /dev/null +++ b/browser/fxr/moz.build @@ -0,0 +1,7 @@ +# -*- 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/. + +JAR_MANIFESTS += ["jar.mn"] |