Bug 1302504 - use mozbrowser to add support for options_ui on Android r?mixedpuppy
MozReview-Commit-ID: IsTOC3pNxJP
--- a/mobile/android/chrome/content/aboutAddons.js
+++ b/mobile/android/chrome/content/aboutAddons.js
@@ -98,17 +98,17 @@ var ContextMenus = {
}
function init() {
window.addEventListener("popstate", onPopState);
AddonManager.addInstallListener(Addons);
AddonManager.addAddonListener(Addons);
Addons.init();
- showList();
+ showAddons();
ContextMenus.init();
}
function uninit() {
AddonManager.removeInstallListener(Addons);
AddonManager.removeAddonListener(Addons);
}
@@ -123,29 +123,38 @@ function onPopState(aEvent) {
if (aEvent.state) {
// Show the detail page for an addon
Addons.showDetails(Addons._getElementForAddon(aEvent.state.id));
} else {
// Clear any previous detail addon
let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.addon = null;
- showList();
+ showAddons();
}
}
-function showList() {
- // Hide the detail page and show the list
+function showAddons() {
+ // Hide the addon options and show the addons list
let details = document.querySelector("#addons-details");
- details.style.display = "none";
+ details.classList.add("hidden");
let list = document.querySelector("#addons-list");
- list.style.display = "block";
+ list.classList.remove("hidden");
document.documentElement.removeAttribute("details");
}
+function showAddonOptions() {
+ // Hide the addon list and show the addon options
+ let list = document.querySelector("#addons-list");
+ list.classList.add("hidden");
+ let details = document.querySelector("#addons-details");
+ details.classList.remove("hidden");
+ document.documentElement.setAttribute("details", "true");
+}
+
var Addons = {
_restartCount: 0,
_createItem: function _createItem(aAddon) {
let outer = document.createElement("div");
outer.setAttribute("addonID", aAddon.id);
outer.className = "addon-item list-item";
outer.setAttribute("role", "button");
@@ -222,21 +231,16 @@ var Addons = {
_createItemForAddon: function _createItemForAddon(aAddon) {
let appManaged = (aAddon.scope == AddonManager.SCOPE_APPLICATION);
let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
let updateable = (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) > 0;
let uninstallable = (aAddon.permissions & AddonManager.PERM_CAN_UNINSTALL) > 0;
let optionsURL = aAddon.optionsURL || "";
- if (aAddon.optionsType == AddonManager.OPTIONS_TYPE_INLINE_BROWSER) {
- // Ignore OPTIONS_TYPE_INLINE_BROWSER until support is added in bug 1302504.
- optionsURL = "";
- }
-
let blocked = "";
switch(aAddon.blocklistState) {
case Ci.nsIBlocklistService.STATE_BLOCKED:
blocked = "blocked";
break;
case Ci.nsIBlocklistService.STATE_SOFTBLOCKED:
blocked = "softBlocked";
break;
@@ -304,32 +308,16 @@ var Addons = {
if (aOperations & AddonManager.PENDING_ENABLE)
return "needs-enable";
if (aOperations & AddonManager.PENDING_DISABLE)
return "needs-disable";
return "";
},
showDetails: function showDetails(aListItem) {
- // This function removes and returns the text content of aNode without
- // removing any child elements. Removing the text nodes ensures any XBL
- // bindings apply properly.
- function stripTextNodes(aNode) {
- var text = "";
- for (var i = 0; i < aNode.childNodes.length; i++) {
- if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) {
- text += aNode.childNodes[i].textContent;
- aNode.removeChild(aNode.childNodes[i--]);
- } else {
- text += stripTextNodes(aNode.childNodes[i]);
- }
- }
- return text;
- }
-
let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
detailItem.setAttribute("isUnsigned", aListItem.getAttribute("isUnsigned"));
detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
let addon = detailItem.addon = aListItem.addon;
let favicon = document.querySelector("#addons-details > .addon-item .icon");
@@ -350,61 +338,99 @@ var Addons = {
let uninstallBtn = document.getElementById("uninstall-btn");
if (addon.scope == AddonManager.SCOPE_APPLICATION) {
uninstallBtn.setAttribute("disabled", "true");
} else {
uninstallBtn.removeAttribute("disabled");
}
- let box = document.querySelector("#addons-details > .addon-item .options-box");
- box.innerHTML = "";
+ let addonItem = document.querySelector("#addons-details > .addon-item");
+ let optionsBox = addonItem.querySelector(".options-box");
+ let optionsURL = aListItem.getAttribute("optionsURL");
+ switch (parseInt(addon.optionsType)) {
+ case AddonManager.OPTIONS_TYPE_INLINE_BROWSER:
+ this.createWebExtensionOptions(optionsBox, optionsURL, addon.optionsBrowserStyle);
+ break;
+ case AddonManager.OPTIONS_TYPE_INLINE:
+ this.createInlineOptions(optionsBox, optionsURL);
+ break;
+ }
+
+ showAddonOptions();
+ },
+
+ createWebExtensionOptions: async function(destination, optionsURL, browserStyle) {
+ destination.innerHTML = "";
- // Retrieve the extensions preferences
+ let frame = document.createElement("iframe");
+ frame.setAttribute("id", "addon-options");
+ frame.setAttribute("mozbrowser", "true");
+ destination.appendChild(frame);
+ // Loading the URL this way prevents the native back
+ // button from applying to the iframe.
+ frame.contentWindow.location.replace(optionsURL);
+ },
+
+ createInlineOptions(destination, optionsURL) {
+ destination.innerHTML = "";
+
+ // This function removes and returns the text content of aNode without
+ // removing any child elements. Removing the text nodes ensures any XBL
+ // bindings apply properly.
+ function stripTextNodes(aNode) {
+ var text = "";
+ for (var i = 0; i < aNode.childNodes.length; i++) {
+ if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) {
+ text += aNode.childNodes[i].textContent;
+ aNode.removeChild(aNode.childNodes[i--]);
+ } else {
+ text += stripTextNodes(aNode.childNodes[i]);
+ }
+ }
+ return text;
+ }
+
try {
- let optionsURL = aListItem.getAttribute("optionsURL");
let xhr = new XMLHttpRequest();
xhr.open("GET", optionsURL, true);
xhr.onload = function(e) {
if (xhr.responseXML) {
// Only allow <setting> for now
let settings = xhr.responseXML.querySelectorAll(":root > setting");
if (settings.length > 0) {
for (let i = 0; i < settings.length; i++) {
var setting = settings[i];
var desc = stripTextNodes(setting).trim();
if (!setting.hasAttribute("desc")) {
setting.setAttribute("desc", desc);
}
- box.appendChild(setting);
+ destination.appendChild(setting);
}
// Send an event so add-ons can prepopulate any non-preference based
// settings
let event = document.createEvent("Events");
event.initEvent("AddonOptionsLoad", true, false);
window.dispatchEvent(event);
} else {
// Reset the options URL to hide the options header if there are no
// valid settings to show.
+ let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.setAttribute("optionsURL", "");
}
// Also send a notification to match the behavior of desktop Firefox
let id = aListItem.getAttribute("addonID");
Services.obs.notifyObservers(document, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, id);
}
}
xhr.send(null);
- } catch (e) { }
-
- let list = document.querySelector("#addons-list");
- list.style.display = "none";
- let details = document.querySelector("#addons-details");
- details.style.display = "block";
- document.documentElement.setAttribute("details", "true");
+ } catch (e) {
+ Cu.reportError(e);
+ }
},
setEnabled: function setEnabled(aValue, aAddon) {
let detailItem = document.querySelector("#addons-details > .addon-item");
let addon = aAddon || detailItem.addon;
if (!addon)
return;
--- a/mobile/android/chrome/content/aboutAddons.xhtml
+++ b/mobile/android/chrome/content/aboutAddons.xhtml
@@ -28,28 +28,27 @@
<menuitem id="contextmenu-enable" label="&addonAction.enable;"></menuitem>
<menuitem id="contextmenu-disable" label="&addonAction.disable;" ></menuitem>
<menuitem id="contextmenu-uninstall" label="&addonAction.uninstall;" ></menuitem>
</menu>
<div id="addons-header" class="header">
<div>&aboutAddons.header2;</div>
</div>
- <div id="addons-list" class="list">
+ <div id="addons-list" class="list hidden">
</div>
- <div id="addons-details" class="list">
+ <div id="addons-details" class="list hidden">
<div class="addon-item list-item">
<img class="icon"/>
<div class="inner">
<div class="details">
<div class="title"></div><div class="version"></div>
</div>
<div class="description-full"></div>
- <div class="options-header">&aboutAddons.options;</div>
<div class="options-box"></div>
</div>
<div class="warn-unsigned">&addonUnsigned.message; <a id="unsigned-learn-more">&addonUnsigned.learnMore;</a></div>
<div class="status status-uninstalled show-on-uninstall"></div>
<div class="buttons">
<button id="enable-btn" class="show-on-disable hide-on-enable hide-on-uninstall" >&addonAction.enable;</button>
<button id="disable-btn" class="show-on-enable hide-on-disable hide-on-uninstall" >&addonAction.disable;</button>
<button id="uninstall-btn" class="hide-on-uninstall" >&addonAction.uninstall;</button>
--- a/mobile/android/locales/en-US/chrome/aboutAddons.dtd
+++ b/mobile/android/locales/en-US/chrome/aboutAddons.dtd
@@ -1,15 +1,14 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!ENTITY aboutAddons.title2 "Add-ons">
<!ENTITY aboutAddons.header2 "Your Add-ons">
-<!ENTITY aboutAddons.options "Options">
<!ENTITY addonAction.enable "Enable">
<!ENTITY addonAction.disable "Disable">
<!ENTITY addonAction.uninstall "Uninstall">
<!ENTITY addonAction.undo "Undo">
<!ENTITY addonUnsigned.message "This add-on could not be verified by &brandShortName;.">
<!ENTITY addonUnsigned.learnMore "Learn more">
--- a/mobile/android/themes/core/aboutAddons.css
+++ b/mobile/android/themes/core/aboutAddons.css
@@ -3,16 +3,22 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
@import "defines.css";
html[details] {
background-color: var(--color_about_item);
}
+iframe {
+ padding: 0;
+ margin: 0;
+ border:none;
+}
+
a {
text-decoration: none;
color: #0096DD;
}
a:active {
color: #0082C6;
}
@@ -20,16 +26,20 @@ a:active {
.details {
width: 100%;
}
.details > div {
display: inline;
}
+.hidden {
+ display: none;
+}
+
.version {
/* title is not localized, so keep the margin on the left side */
margin-left: .67em;
}
.description {
width: 100%;
overflow: hidden;
@@ -54,22 +64,16 @@ a:active {
.status {
border-top: 1px solid var(--color_about_item_border);
font-weight: bold;
padding: 0.5em;
width: 100%;
}
-.options-header {
- font-weight: bold;
- text-transform: uppercase;
- margin-top: 1em;
-}
-
.addon-item[isDisabled="true"] .options-header,
.addon-item[optionsURL=""] .options-header,
.addon-item[isDisabled="true"] .options-box,
.addon-item[optionsURL=""] .options-box {
display: none;
}
#addons-details > .list-item {
@@ -322,17 +326,13 @@ div[opType="needs-restart"] .hide-on-res
div[opType="needs-uninstall"] .hide-on-uninstall,
div[isDisabled="true"][opType="needs-uninstall"],
div[opType="needs-install"] .hide-on-install,
div[opType="needs-enable"] .hide-on-enable,
div[opType="needs-disable"] .hide-on-disable {
display: none;
}
-#addons-list, #addons-details {
- display: none;
-}
-
#browse-title:dir(rtl) {
background-position: left;
background-image: url("chrome://browser/skin/images/chevron-rtl.png");
}
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -3781,16 +3781,17 @@ var gDetailView = {
detailViewBoxObject.scrollTo(0, top);
}
},
async createOptionsBrowser(parentNode) {
let browser = document.createElement("browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
+ browser.setAttribute("id", "addon-options");
browser.setAttribute("class", "inline-options-browser");
let {optionsURL} = this._addon;
let remote = !E10SUtils.canLoadURIInProcess(optionsURL, Services.appinfo.PROCESS_TYPE_DEFAULT);
let readyPromise;
if (remote) {
browser.setAttribute("remote", "true");