#639: site-specific user agent exposed interface plus locale changes

This commit is contained in:
Cameron Kaiser 2021-03-10 19:59:09 -08:00
parent 7b403249b0
commit 355d707412
8 changed files with 518 additions and 10 deletions

View File

@ -11,17 +11,29 @@ var gTenFourFoxPane = {
init: function ()
{
function setEventListener(aId, aEventType, aCallback) /* future expansion */
function setEventListener(aId, aEventType, aCallback)
{
document.getElementById(aId)
.addEventListener(aEventType, aCallback.bind(gTenFourFoxPane));
}
/* setEventListener("historyDontRememberClear", "click", function () {
gPrivacyPane.clearPrivateDataNow(true);
return false;
}); */
setEventListener("siteSpecificUAs", "command", gTenFourFoxPane.showSSUAs);
},
showSSUAs: function ()
{
let bundle = document.getElementById("tenFourFoxBundle");
let params = { blockVisible : true,
sessionVisible : true,
allowVisible : true,
prefilledHost : "",
type : "ssua",
windowTitle : bundle.getString("TFFsiteSpecificUAs.title"),
introText : bundle.getString("TFFsiteSpecificUAs.prompt") };
gSubDialog.open("chrome://browser/content/preferences/tenfourfox-ssua.xul",
null, params);
},
// We have to invert the sense for the pdfjs.disabled pref, since true equals DISabled.
@ -37,6 +49,7 @@ var gTenFourFoxPane = {
},
// Find and set the appropriate UA string based on the UA template.
// Keep in sync with tenfourfox-ssua.xul and tenfourfox.xul
validUA : {
"fx" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:52.0) Gecko/20100101 Firefox/52.0",
"fx60" : "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:60.0) Gecko/20100101 Firefox/60.0",
@ -48,7 +61,6 @@ var gTenFourFoxPane = {
"android" : "Mozilla/5.0 (Linux; Android 8.1.0; Pixel XL Build/OPM1.171019.021) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.109 Mobile Safari/537.36",
"ipad" : "Mozilla/5.0 (iPhone; CPU iPhone OS 11_2_6 like Mac OS X) AppleWebKit/604.5.6 (KHTML, like Gecko) Version/11.0 Mobile/15D100 Safari/604.1"
},
_prefSvc: Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch),
readUA: function ()
{
var pref = document.getElementById("tenfourfox.ua.template");
@ -57,7 +69,7 @@ var gTenFourFoxPane = {
// Synchronize the pref on entry in case it's stale.
pref = pref.value;
if (this.validUA[pref]) {
this._prefSvc.setCharPref("general.useragent.override", this.validUA[pref]);
Services.prefs.setCharPref("general.useragent.override", this.validUA[pref]);
return pref;
}
return "";
@ -66,10 +78,10 @@ var gTenFourFoxPane = {
{
var nupref = document.getElementById("uaBox").value;
if (this.validUA[nupref]) {
this._prefSvc.setCharPref("general.useragent.override", this.validUA[nupref]);
Services.prefs.setCharPref("general.useragent.override", this.validUA[nupref]);
return nupref;
}
this._prefSvc.clearUserPref("general.useragent.override");
Services.prefs.clearUserPref("general.useragent.override");
return "";
},
};

View File

@ -24,6 +24,9 @@
</preferences>
<stringbundle id="tenFourFoxBundle"
src="chrome://browser/locale/TenFourFox.properties"/>
<hbox id="header-tenfourfox"
class="header"
hidden="true"
@ -74,6 +77,8 @@
<menuitem label="iOS Safari (iPad)" value="ipad"/>
</menupopup>
</menulist>
<spacer flex="1"/>
<button id="siteSpecificUAs" label="&TFFsiteSpecificUAs.label;"/>
</hbox>
</groupbox>

View File

@ -24,5 +24,7 @@ browser.jar:
content/browser/preferences/sanitize.js
content/browser/preferences/selectBookmark.xul
content/browser/preferences/selectBookmark.js
content/browser/preferences/tenfourfox-ssua.xul
* content/browser/preferences/tenfourfox-ssua.js
content/browser/preferences/translation.xul
content/browser/preferences/translation.js

View File

@ -0,0 +1,343 @@
/* 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");
const UA_PREF_BRANCH = "general.useragent.override.";
function SSUA(domain, ua)
{
this.domain = domain;
this.ua = ua;
}
var gTenFourFoxSSUAManager = {
_type : "",
_SSUAs : [],
_SSUAsToAdd : new Map(), // also includes changes, necessarily
_SSUAsToDelete : new Map(),
_bundle : null,
_pbundle : null,
_tree : null,
_prefBranch : null,
_view: {
_rowCount: 0,
get rowCount()
{
return this._rowCount;
},
getCellText: function (aRow, aColumn)
{
if (aColumn.id == "domainCol")
return gTenFourFoxSSUAManager._SSUAs[aRow].domain;
else if (aColumn.id == "uaCol")
return gTenFourFoxSSUAManager._SSUAs[aRow].ua;
return "";
},
isSeparator: function(aIndex) { return false; },
isSorted: function() { return false; },
isContainer: function(aIndex) { return false; },
setTree: function(aTree){},
getImageSrc: function(aRow, aColumn) {},
getProgressMode: function(aRow, aColumn) {},
getCellValue: function(aRow, aColumn) {},
cycleHeader: function(column) {},
getRowProperties: function(row){ return ""; },
getColumnProperties: function(column){ return ""; },
getCellProperties: function(row,column){
if (column.element.getAttribute("id") == "domainCol")
return "ltr";
return "";
}
},
addSSUA: function ()
{
var textbox = document.getElementById("domain");
var uabox = document.getElementById("ua");
var input_dom = textbox.value.replace(/^\s*/, ""); // trim any leading space
input_dom = input_dom.replace(/\s*$/,"");
input_dom = input_dom.replace("http://", "");
input_dom = input_dom.replace("https://", "");
input_dom = input_dom.replace(/\//g, "");
var ua = uabox.value.replace(/^\s*/, "");
ua = ua.replace(/\s*$/, "");
try {
// Block things like hostname:port by making a URL and seeing if it rejects it.
let uri = Services.io.newURI("http://"+input_dom+":80/", null, null);
if (!uri.host) throw "as if";
} catch(ex) {
var message = this._pbundle.getString("invalidURI");
var title = this._pbundle.getString("invalidURITitle");
Services.prompt.alert(window, title, message);
return;
}
// check whether the entry already exists, and if not, add it
let already = false;
for (var i = 0; i < this._SSUAs.length; ++i) {
if (this._SSUAs[i].domain == input_dom) {
already = true;
this._SSUAs[i].ua = ua;
this._SSUAsToAdd.set(input_dom, ua); // can go through same path
this._resortSSUAs();
break;
}
}
if (!already) {
this._SSUAsToAdd.set(input_dom, ua);
this._addSSUAToList(input_dom, ua);
++this._view._rowCount;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, 1);
this._resortSSUAs();
}
textbox.value = "";
uabox.value = "";
textbox.focus();
// covers a case where the site exists already, so the buttons don't disable
this.onHostInput(textbox);
// enable "remove all" button as needed
document.getElementById("removeAllSSUAs").disabled = this._SSUAs.length == 0;
},
_removeSSUA: function(ssua)
{
for (let i = 0; i < this._SSUAs.length; ++i) {
if (this._SSUAs[i].domain == ssua.domain) {
this._SSUAs.splice(i, 1);
this._view._rowCount--;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
this._tree.treeBoxObject.invalidate();
break;
}
}
// If this was added during this session, let's remove
// it from the pending adds list to prevent extra work.
let isnew = this._SSUAsToAdd.delete(ssua.domain);
if (!isnew) {
this._SSUAsToDelete.set(ssua.domain, ssua);
}
},
_resortSSUAs: function()
{
gTreeUtils.sort(this._tree, this._view, this._SSUAs,
this._lastSSUAsortColumn,
this._SSUAsComparator,
this._lastSSUAsortColumn,
!this._lastSSUAsortAscending); // keep sort direction
this._tree.treeBoxObject.invalidate();
},
onHostInput: function ()
{
let w = document.getElementById("domain").value;
let x = document.getElementById("ua").value;
document.getElementById("btnAdd").disabled = !w || !x || !w.length || !x.length
|| w.length == 0 || x.length == 0;
},
onWindowKeyPress: function (aEvent)
{
if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE)
window.close();
},
onHostKeyPress: function (aEvent)
{
if (aEvent.keyCode == KeyEvent.DOM_VK_RETURN)
document.getElementById("btnAdd").click();
},
onLoad: function ()
{
this._bundle = document.getElementById("tenFourFoxPreferences");
this._pbundle = document.getElementById("bundlePreferences");
var params = window.arguments[0];
this.init(params);
},
init: function (aParams)
{
if (this._type) {
// reusing an open dialog, clear the old observer
this.uninit();
}
this._type = aParams.type;
_prefBranch = Services.prefs.getBranch(UA_PREF_BRANCH);
var SSUAsText = document.getElementById("ssuaText");
while (SSUAsText.hasChildNodes())
SSUAsText.removeChild(SSUAsText.firstChild);
SSUAsText.appendChild(document.createTextNode(aParams.introText));
document.title = aParams.windowTitle;
this.onHostInput();
let treecols = document.getElementsByTagName("treecols")[0];
treecols.addEventListener("click", event => {
if (event.target.nodeName != "treecol" || event.button != 0) {
return;
}
let sortField = event.target.getAttribute("data-field-name");
if (!sortField) {
return;
}
gTenFourFoxSSUAManager.onSSUAsort(sortField);
});
this._loadSSUAs();
_prefBranch.addObserver("", this._loadSSUAs, false); // XXX: make this better
document.getElementById("domain").focus();
},
uninit: function ()
{
_prefBranch.removeObserver("", this._loadSSUAs); // XXX
this._type = "";
},
onSSUAselected: function ()
{
var hasSelection = this._tree.view.selection.count > 0;
var hasRows = this._tree.view.rowCount > 0;
document.getElementById("removeSSUA").disabled = !hasRows || !hasSelection;
document.getElementById("removeAllSSUAs").disabled = !hasRows;
},
onSSUADeleted: function ()
{
if (!this._view.rowCount)
return;
var removedSSUAs = [];
gTreeUtils.deleteSelectedItems(this._tree, this._view, this._SSUAs, removedSSUAs);
for (var i = 0; i < removedSSUAs.length; ++i) {
var p = removedSSUAs[i];
this._removeSSUA(p);
}
document.getElementById("removeSSUA").disabled = !this._SSUAs.length;
document.getElementById("removeAllSSUAs").disabled = !this._SSUAs.length;
},
onAllSSUAsDeleted: function ()
{
if (!this._view.rowCount)
return;
var removedSSUAs = [];
gTreeUtils.deleteAll(this._tree, this._view, this._SSUAs, removedSSUAs);
for (var i = 0; i < removedSSUAs.length; ++i) {
var p = removedSSUAs[i];
this._removeSSUA(p);
}
document.getElementById("removeSSUA").disabled = true;
document.getElementById("removeAllSSUAs").disabled = true;
},
onSSUAKeyPress: function (aEvent)
{
if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE
#ifdef XP_MACOSX
|| aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE
#endif
)
this.onSSUADeleted();
},
_lastSSUAsortColumn: "",
_lastSSUAsortAscending: false,
_SSUAsComparator : function (a, b)
{
return a.toLowerCase().localeCompare(b.toLowerCase());
},
onSSUAsort: function (aColumn)
{
this._lastSSUAsortAscending = gTreeUtils.sort(this._tree,
this._view,
this._SSUAs,
aColumn,
this._SSUAsComparator,
this._lastSSUAsortColumn,
this._lastSSUAsortAscending);
this._lastSSUAsortColumn = aColumn;
},
onApplyChanges: function()
{
// Stop observing changes since we are about
// to write out the pending adds/deletes and don't need
// to update the UI.
this.uninit();
// Create and clear prefs out of whole cloth; don't use the
// pref branch because it may not exist yet.
for (let i of this._SSUAsToAdd.keys()) {
Services.prefs.setCharPref(UA_PREF_BRANCH+i, this._SSUAsToAdd.get(i));
}
for (let i of this._SSUAsToDelete.keys()) {
Services.prefs.clearUserPref(UA_PREF_BRANCH+i);
}
window.close();
},
fillUA: function(popup)
{
if (popup.value == "")
document.getElementById("ua").value = "";
else
document.getElementById("ua").value = gTenFourFoxPane.validUA[popup.value];
this.onHostInput();
},
_loadSSUAs: function ()
{
this._tree = document.getElementById("SSUAsTree");
this._SSUAs = [];
// load SSUAs into a table
let count = 0;
let domains = _prefBranch.getChildList("");
for (let domain of domains) {
this._addSSUAToList(domain, _prefBranch.getCharPref(domain));
}
this._view._rowCount = this._SSUAs.length;
// sort and display the table
this._tree.view = this._view;
this.onSSUAsort("domain");
// disable "remove all" button if there are none
document.getElementById("removeAllSSUAs").disabled = this._SSUAs.length == 0;
},
_addSSUAToList: function (domain, ua)
{
var p = new SSUA(domain, ua);
this._SSUAs.push(p);
},
};
function initWithParams(aParams)
{
gTenFourFoxSSUAManager.init(aParams);
}

View File

@ -0,0 +1,126 @@
<?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/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/skin/preferences/preferences.css" type="text/css"?>
<!DOCTYPE dialog [
<!ENTITY % permissionsDTD SYSTEM "chrome://browser/locale/preferences/permissions.dtd" >
<!ENTITY % tenFourFoxDTD SYSTEM "chrome://browser/locale/TenFourFox.dtd" >
%permissionsDTD;
%tenFourFoxDTD;
]>
<window id="TenFourFoxSSUADialog" class="windowDialog"
windowtype="Browser:TenFourFoxSSUA"
title="&TFFsiteSpecificUAs.title;"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
style="width: &window.width;;"
onload="gTenFourFoxSSUAManager.onLoad();"
onunload="gTenFourFoxSSUAManager.uninit();"
persist="screenX screenY width height"
onkeypress="gTenFourFoxSSUAManager.onWindowKeyPress(event);">
<script src="chrome://global/content/treeUtils.js"/>
<script src="chrome://browser/content/preferences/tenfourfox-ssua.js"/>
<script src="chrome://browser/content/preferences/in-content/tenfourfox.js"/>
<stringbundle id="bundlePreferences"
src="chrome://browser/locale/preferences/preferences.properties"/>
<stringbundle id="tenFourFoxPreferences"
src="chrome://browser/locale/TenFourFox.properties"/>
<keyset>
<key key="&windowClose.key;" modifiers="accel" oncommand="window.close();"/>
</keyset>
<vbox class="contentPane largeDialogContainer" flex="1">
<description id="ssuaText" control="domain"/>
<separator class="thin"/>
<grid>
<columns>
<column/>
<column flex="1"/>
</columns>
<rows>
<row align="baseline">
<hbox pack="end">
<label id="domainLabel" control="domain"
value="&TFFsiteSpecificUAs.domain.l;" accesskey="&address.accesskey;"/>
</hbox>
<textbox id="domain" flex="1"
oninput="gTenFourFoxSSUAManager.onHostInput();"
onkeypress="gTenFourFoxSSUAManager.onHostKeyPress(event);"/>
</row>
<row align="baseline">
<hbox pack="end">
<label>&TFFsiteSpecificUAs.preua;</label>
</hbox>
<menulist id="uaBox" crop="end" flex="1"
oncommand="gTenFourFoxSSUAManager.fillUA(event.target);">
<menupopup>
<menuitem label="" value=""/>
<menuitem label="Firefox 78ESR (Intel)" value="fx78"/>
<menuitem label="Firefox 68ESR (Intel)" value="fx68"/>
<menuitem label="Firefox 60ESR (Intel)" value="fx60"/>
<menuitem label="Firefox 52ESR (Intel)" value="fx"/>
<menuitem label="Classilla 9.3.x" value="classilla"/>
<menuitem label="Internet Explorer 8" value="ie8"/>
<menuitem label="Internet Explorer 11" value="ie11"/>
<menuitem label="Android Chrome" value="android"/>
<menuitem label="iOS Safari (iPad)" value="ipad"/>
</menupopup>
</menulist>
</row>
<row align="baseline">
<hbox pack="end">
<label id="uaLabel" control="ua" value="&TFFsiteSpecificUAs.ua.l;"/>
</hbox>
<textbox id="ua" flex="1"
oninput="gTenFourFoxSSUAManager.onHostInput();"
onkeypress="gTenFourFoxSSUAManager.onHostKeyPress(event);"/>
</row>
</rows>
</grid>
<hbox pack="end">
<button id="btnAdd" disabled="true" label="&TFFsiteSpecificUAs.add;" default="true" accesskey="&allow.accesskey;"
oncommand="gTenFourFoxSSUAManager.addSSUA();"/>
</hbox>
<separator class="thin"/>
<tree id="SSUAsTree" flex="1" style="height: 18em;"
hidecolumnpicker="true"
onkeypress="gTenFourFoxSSUAManager.onSSUAKeyPress(event)"
onselect="gTenFourFoxSSUAManager.onSSUAselected();">
<treecols>
<treecol id="domainCol" label="&TFFsiteSpecificUAs.domain;" flex="1"
data-field-name="domain" persist="width"/>
<splitter class="tree-splitter"/>
<treecol id="uaCol" label="&TFFsiteSpecificUAs.ua;" flex="3"
data-field-name="ua" persist="width"/>
</treecols>
<treechildren/>
</tree>
<hbox class="actionButtons" align="left" flex="1">
<button id="removeSSUA" disabled="true"
accesskey="&removepermission.accesskey;"
icon="remove" label="&removepermission.label;"
oncommand="gTenFourFoxSSUAManager.onSSUADeleted();"/>
<button id="removeAllSSUAs" disabled="true"
icon="clear" label="&removeallpermissions.label;"
accesskey="&removeallpermissions.accesskey;"
oncommand="gTenFourFoxSSUAManager.onAllSSUAsDeleted();"/>
</hbox>
<spacer flex="1"/>
<hbox class="actionButtons" align="right" flex="1">
<button oncommand="close();" icon="close"
label="&button.cancel.label;" accesskey="&button.cancel.accesskey;" />
<button id="btnApplyChanges" oncommand="gTenFourFoxSSUAManager.onApplyChanges();" icon="save"
label="&button.ok.label;" accesskey="&button.ok.accesskey;"/>
</hbox>
</vbox>
</window>

View File

@ -11,3 +11,20 @@
<!ENTITY TFFmseMode.title "MediaSource">
<!ENTITY TFFmseMode.prompt "Enable MSE/media quality options (slower, if available)">
<!ENTITY TFFsiteSpecificUAs.label "Site Specific…">
<!ENTITY TFFsiteSpecificUAs.title "Site Specific User Agents">
<!ENTITY TFFsiteSpecificUAs.add "Add">
<!ENTITY TFFsiteSpecificUAs.domain "Domain">
<!ENTITY TFFsiteSpecificUAs.domain.l "Domain:">
<!ENTITY TFFsiteSpecificUAs.ua "User agent">
<!ENTITY TFFsiteSpecificUAs.ua.l "User agent string:">
<!ENTITY TFFsiteSpecificUAs.preua "Common user agents:">
<!ENTITY TFFautoReaderView.label "Auto Reader View…">
<!ENTITY TFFautoReaderView.title "Site Specific Auto Reader View">
<!-- also uses domain and add from above -->
<!ENTITY TFFautoReaderView.mode "Mode">
<!ENTITY TFFautoReaderView.mode.l "Mode:">
<!ENTITY TFFautoReaderView.mode.all "All pages">
<!ENTITY TFFautoReaderView.mode.sub "Only subpages">

View File

@ -12,3 +12,6 @@ TFFpdfViewMode.prompt.p = Use built-in PDF viewer (slower but safer)
TFFmseMode.title.p = MediaSource
TFFmseMode.prompt.p = Enable MSE/media quality options (slower, if available)
TFFsiteSpecificUAs.title = Site Specific User Agents
TFFsiteSpecificUAs.prompt = This is for advanced users to use a custom user agent for particular domains automatically. Enter a domain and a user agent string, or select a predefined one.

View File

@ -49,7 +49,7 @@
<!ENTITY helpus.getInvolvedLink "get involved!">
<!ENTITY helpus.end "">
<!ENTITY copyright.blurb "Copyright © 2010-2020 Contributors to TenFourFox. All rights reserved.">
<!ENTITY copyright.blurb "Copyright © 2010-2021 Contributors to TenFourFox. All rights reserved.">
<!-- LOCALIZATION NOTE (bottomLinks.license): This is a link title that links to about:license. -->
<!ENTITY bottomLinks.license "Licensing Information">