Skip to content

Loading…

UI for Fennec (Firefox for Android) #956

Merged
merged 14 commits into from

3 participants

@AlexVallat
Collaborator

As per gorhill/uBlock#524 (comment)

Now wrapped up in a pull request for convenience.

@Deathamns
  • The observer you added to bootstrap.js should go into vapi-backgournd.js (unless you have a good reason to leave it there), also registering and unregistering should go into their own methods on the observer object (consider it as a convention)
  • vAPI.getURL is unnecessary with vAPI.tabs.open
  • Try to follow the project's code styling, also you have some missing semi-colons (jshint)
  • I would prefer not to have two large indented block for the toolbarbutton code, so instead of doing that, you could create two separate files, and load the appropriate one into the vAPI object with Services.scriptloader.loadSubScript

Since I can't test it, maybe provide a screenshot of how it looks, I'll believe that it works.

And rebase next time, instead of merging the master to your branch, so we don't see the changes that we made, keeps the history cleaner, and it's easier to review.

@AlexVallat AlexVallat added a commit to AlexVallat/uBlock that referenced this pull request
@AlexVallat AlexVallat Changes following review at gorhill/uBlock#956 (comment) d40b2bb
@AlexVallat
Collaborator

Thanks for your feedback, I have made the following changes:

  • Options observer moved into vapi-background.js
  • redundant vAPI.getURL calls for openTab removed
  • Semi-colons added after functions

The toolbar button code can't be simply moved into a loadSubScript because it uses variables declared in vapi-background, like cleanupTasks. I have unindented it for cleaner diffing, though.

I couldn't figure out how to get rebasing to work, so it's had to be another merge, sorry about that.

Here are some screenshots:
fennec-ui-options
fennec-ui-menu
fennec-ui-popup
fennec-ui-dashboard

@Deathamns Deathamns merged commit 415846e into chrisaljoudi:master
@Deathamns

I'll merge it, and I'll fix later what I think should be...

@Deathamns

@AlexVallat I wanted to separate the code into files, but then I decided that it doesn't worth it, and I ended up with what it is now. However, I may reorganized more than it needed, so if you have time, please test it if it's still working.
Also, if it does, then it means that we have Fennec support, can I enable it next time on AMO?

@gorhill

So, should I add an official "Fennec support" as part of the new stuff for current dev build?

@chengsun chengsun pushed a commit to chengsun/uBlock that referenced this pull request
@Deathamns Deathamns replacing documentElement is enough 9fe4546
@AlexVallat
Collaborator

I've just grabbed the latest code changes now, and it still seems to be working fine. I have been running with (although not with your latest changes, of course) it for day-to-day use for several days now and have not encountered any issues. I would say it is certainly usable enough under Fennec to be worth enabling support for it in AMO, and listing it as a supported platform in the documentation.

The only caveat is that the element picker is only barely usable on touch screens (no hovering, and no Ctrl clicking). Should you ever decide to redesign the element picker, you might like to keep touch screens in mind when doing so.

@gorhill

you might like to keep touch screens in mind when doing so

How usable is dynamic filtering -- if at all? It also needs hovering.

@AlexVallat
Collaborator

How usable is dynamic filtering -- if at all? It also needs hovering.

Functional, but only if you already know how to use it. Tapping on the third of the box to toggle the state for that filter works, but there's no clue that that's what's expected from you. To be fair, it's pretty un-obvious how to use it on the desktop too, you really need to read the documentation to understand what's going on.

@Deathamns

you really need to read the documentation to understand

This is why it's an advanced feature.

@AlexVallat AlexVallat added a commit to AlexVallat/uBlock that referenced this pull request
@AlexVallat AlexVallat Changes following review at gorhill/uBlock#956 (comment) b899fa8
@AlexVallat AlexVallat added a commit to AlexVallat/uBlock that referenced this pull request
@AlexVallat AlexVallat Changes following review at gorhill/uBlock#956 (comment) 7d472ca
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
3 platform/firefox/install.rdf
@@ -10,8 +10,7 @@
<type>2</type>
<bootstrap>true</bootstrap>
<multiprocessCompatible>true</multiprocessCompatible>
- <optionsType>3</optionsType>
- <optionsURL>chrome://ublock/content/dashboard.html</optionsURL>
+ <optionsType>2</optionsType>
{localized}
<!-- Firefox -->
View
9 platform/firefox/options.xul
@@ -0,0 +1,9 @@
+<?xml version="1.0" ?>
+<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <setting type="control">
+ <vbox>
+ <button id="showDashboardButton"/>
+ <button id="showNetworkLogButton"/>
+ </vbox>
+ </setting>
+</vbox>
View
190 platform/firefox/vapi-background.js
@@ -272,21 +272,17 @@ var windowWatcher = {
if ( tabBrowser.deck ) {
// Fennec
tabContainer = tabBrowser.deck;
- tabContainer.addEventListener(
- 'DOMTitleChanged',
- tabWatcher.onFennecLocationChange
- );
} else if ( tabBrowser.tabContainer ) {
// desktop Firefox
tabContainer = tabBrowser.tabContainer;
tabBrowser.addTabsProgressListener(tabWatcher);
+ vAPI.contextMenu.register(this.document);
} else {
return;
}
tabContainer.addEventListener('TabClose', tabWatcher.onTabClose);
tabContainer.addEventListener('TabSelect', tabWatcher.onTabSelect);
- vAPI.contextMenu.register(this.document);
// when new window is opened TabSelect doesn't run on the selected tab?
},
@@ -353,28 +349,6 @@ var tabWatcher = {
url: location.asciiSpec
});
},
-
- onFennecLocationChange: function({target: doc}) {
- // Fennec "equivalent" to onLocationChange
- // note that DOMTitleChanged is selected as it fires very early
- // (before DOMContentLoaded), and it does fire even if there is no title
-
- var win = doc.defaultView;
- if ( win !== win.top ) {
- return;
- }
-
- var loc = win.location;
- /*if ( loc.protocol === 'http' || loc.protocol === 'https' ) {
- return;
- }*/
-
- vAPI.tabs.onNavigation({
- frameId: 0,
- tabId: getOwnerWindow(win).BrowserApp.getTabForWindow(win).id,
- url: Services.io.newURI(loc.href, null, null).asciiSpec
- });
- }
};
/******************************************************************************/
@@ -449,10 +423,6 @@ vAPI.tabs.registerListeners = function() {
if ( tabBrowser.deck ) {
// Fennec
tabContainer = tabBrowser.deck;
- tabContainer.removeEventListener(
- 'DOMTitleChanged',
- tabWatcher.onFennecLocationChange
- );
} else if ( tabBrowser.tabContainer ) {
tabContainer = tabBrowser.tabContainer;
tabBrowser.removeTabsProgressListener(tabWatcher);
@@ -773,6 +743,24 @@ vAPI.tabs.reload = function(tabId) {
/******************************************************************************/
+vAPI.tabs.select = function(tabId) {
+ var tab = this.get(tabId);
+
+ if ( !tab ) {
+ return;
+ }
+
+ var tabBrowser = getTabBrowser(getOwnerWindow(tab));
+
+ if (vAPI.fennec) {
+ tabBrowser.selectTab(tab);
+ } else {
+ tabBrowser.selectedTab = tab;
+ }
+};
+
+/******************************************************************************/
+
vAPI.tabs.injectScript = function(tabId, details, callback) {
var tab = this.get(tabId);
@@ -785,7 +773,7 @@ vAPI.tabs.injectScript = function(tabId, details, callback) {
}
details.file = vAPI.getURL(details.file);
- tab.linkedBrowser.messageManager.sendAsyncMessage(
+ getBrowserForTab(tab).messageManager.sendAsyncMessage(
location.host + ':broadcast',
JSON.stringify({
broadcast: true,
@@ -823,23 +811,7 @@ vAPI.setIcon = function(tabId, iconStatus, badge) {
return;
}
- var button = win.document.getElementById(tb.id);
-
- if ( !button ) {
- return;
- }
-
- var icon = tb.tabs[tabId];
- button.setAttribute('badge', icon && icon.badge || '');
-
- if ( !icon || !icon.img ) {
- icon = '';
- }
- else {
- icon = 'url(' + vAPI.getURL('img/browsericons/icon16.svg') + ')';
- }
-
- button.style.listStyleImage = icon;
+ tb.updateState(win, tabId);
};
/******************************************************************************/
@@ -1260,13 +1232,13 @@ var httpObserver = {
return;
}
- /*if ( vAPI.fennec && lastRequest.type === this.MAIN_FRAME ) {
+ if ( vAPI.fennec && lastRequest.type === this.MAIN_FRAME && lastRequest.frameId === 0 ) {
vAPI.tabs.onNavigation({
frameId: 0,
tabId: lastRequest.tabId,
url: URI.asciiSpec
});
- }*/
+ }
// If request is not handled we may use the data in on-modify-request
if ( channel instanceof Ci.nsIWritablePropertyBag ) {
@@ -1412,8 +1384,67 @@ vAPI.toolbarButton = {
tabs: {/*tabId: {badge: 0, img: boolean}*/}
};
-/******************************************************************************/
+if (vAPI.fennec) {
+ // Menu UI
+ vAPI.toolbarButton.menuItemIds = new WeakMap();
+
+ vAPI.toolbarButton.getMenuItemLabel = function(tabId) {
+ var label = this.label;
+ if (tabId !== undefined) {
+ var tabDetails = this.tabs[tabId];
+ if (tabDetails) {
+ if (tabDetails.img) {
+ if (tabDetails.badge) {
+ label = label + " (" + tabDetails.badge + ")";
+ }
+ } else {
+ label = label + " (" + vAPI.i18n("fennecMenuItemBlockingOff") + ")";
+ }
+ }
+ }
+ return label;
+ };
+
+ vAPI.toolbarButton.init = function() {
+ // Only actually expecting one window under Fennec (note, not tabs, windows)
+ for (var win of vAPI.tabs.getWindows()) {
+ this.addToWindow(win, this.getMenuItemLabel());
+ }
+
+ cleanupTasks.push(this.cleanUp);
+ };
+
+ vAPI.toolbarButton.addToWindow = function(win, label) {
+ var id = win.NativeWindow.menu.add({
+ name: label,
+ callback: this.onClick
+ });
+ this.menuItemIds.set(win, id);
+ };
+
+ vAPI.toolbarButton.removeFromWindow = function(win) {
+ var id = this.menuItemIds.get(win);
+ if (id) {
+ win.NativeWindow.menu.remove(id);
+ this.menuItemIds.delete(win);
+ }
+ };
+ vAPI.toolbarButton.updateState = function(win, tabId) {
+ var id = this.menuItemIds.get(win);
+ if (!id) {
+ return;
+ }
+ win.NativeWindow.menu.update(id, { name: this.getMenuItemLabel(tabId) });
+ };
+
+ vAPI.toolbarButton.onClick = function() {
+ var win = Services.wm.getMostRecentWindow('navigator:browser');
+ var curTabId = vAPI.tabs.getTabId(getTabBrowser(win).selectedTab);
+ vAPI.tabs.open({ url: "popup.html?tabId=" + curTabId, index: -1, select: true });
+ };
+} else {
+// Toolbar button UI
vAPI.toolbarButton.init = function() {
var CustomizableUI;
try {
@@ -1622,6 +1653,27 @@ vAPI.toolbarButton.onViewHiding = function({target}) {
target.firstChild.setAttribute('src', 'about:blank');
};
+vAPI.toolbarButton.updateState = function(win, tabId) {
+ var button = win.document.getElementById(this.id);
+
+ if ( !button ) {
+ return;
+ }
+
+ var icon = this.tabs[tabId];
+ button.setAttribute('badge', icon && icon.badge || '');
+
+ if ( !icon || !icon.img ) {
+ icon = '';
+ }
+ else {
+ icon = 'url(' + vAPI.getURL('img/browsericons/icon16.svg') + ')';
+ }
+
+ button.style.listStyleImage = icon;
+};
+}
+
/******************************************************************************/
vAPI.toolbarButton.init();
@@ -1818,6 +1870,40 @@ vAPI.punycodeURL = function(url) {
/******************************************************************************/
+vAPI.optionsObserver = {
+ register: function () {
+ var obs = Components.classes['@mozilla.org/observer-service;1'].getService(Components.interfaces.nsIObserverService);
+ obs.addObserver(this, "addon-options-displayed", false);
+
+ cleanupTasks.push(this.unregister.bind(this));
+ },
+
+ observe: function (aSubject, aTopic, aData) {
+ if (aTopic === "addon-options-displayed" && aData === "{2b10c1c8-a11f-4bad-fe9c-1c11e82cac42}") {
+ var doc = aSubject;
+ this.setupOptionsButton(doc, "showDashboardButton", "dashboard.html");
+ this.setupOptionsButton(doc, "showNetworkLogButton", "devtools.html");
+ }
+ },
+ setupOptionsButton: function (doc, id, page) {
+ var button = doc.getElementById(id);
+ button.addEventListener("command", function () {
+ vAPI.tabs.open({ url: page, index: -1 });
+ });
+ button.label = vAPI.i18n(id);
+ },
+
+ unregister: function () {
+ var obs = Components.classes['@mozilla.org/observer-service;1'].getService(Components.interfaces.nsIObserverService);
+ obs.removeObserver(this, "addon-options-displayed");
+ },
+};
+
+vAPI.optionsObserver.register();
+
+/******************************************************************************/
+
+
// clean up when the extension is disabled
window.addEventListener('unload', function() {
View
12 src/_locales/en/messages.json
@@ -547,6 +547,18 @@
"message":"{{value}} days ago",
"description":"English: {{value}} days ago"
},
+ "showDashboardButton":{
+ "message":"Show Dashboard",
+ "description":"English: Show Dashboard"
+ },
+ "showNetworkLogButton":{
+ "message":"Show Network Request Log",
+ "description":"English: Show Network Request Log"
+ },
+ "fennecMenuItemBlockingOff": {
+ "message": "off",
+ "description": "Appears as µBlock (off)"
+ },
"dummy":{
"message":"This entry must be the last one",
"description":"so we dont need to deal with comma for last entry"
View
2 src/dashboard.html
@@ -2,6 +2,8 @@
<html>
<head>
<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1">
+
<title data-i18n="dashboardName"></title>
<link href="css/dashboard.css" rel="stylesheet" type="text/css">
<link href="css/common.css" rel="stylesheet" type="text/css">
View
2 src/devtools.html
@@ -2,6 +2,8 @@
<html>
<head>
<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1">
+
<title data-i18n="statsPageName"></title>
<link rel="stylesheet" type="text/css" href="css/common.css">
<link rel="stylesheet" type="text/css" href="css/devtools.css">
View
15 src/js/messaging.js
@@ -74,6 +74,9 @@ var onMessage = function(request, sender, callback) {
case 'reloadTab':
if ( vAPI.isNoTabId(request.tabId) === false ) {
vAPI.tabs.reload(request.tabId);
+ if (request.select && vAPI.tabs.select) {
+ vAPI.tabs.select(request.tabId);
+ }
}
break;
@@ -187,7 +190,7 @@ var getFirewallRules = function(srcHostname, desHostnames) {
/******************************************************************************/
-var getStats = function(tabId) {
+var getStats = function(tabId, tabTitle) {
var r = {
advancedUserEnabled: µb.userSettings.advancedUserEnabled,
appName: vAPI.app.name,
@@ -201,7 +204,8 @@ var getStats = function(tabId) {
pageURL: '',
pageAllowedRequestCount: 0,
pageBlockedRequestCount: 0,
- tabId: tabId
+ tabId: tabId,
+ tabTitle: tabTitle
};
var pageStore = µb.pageStoreFromTabId(tabId);
if ( pageStore ) {
@@ -269,8 +273,8 @@ var onMessage = function(request, sender, callback) {
// Async
switch ( request.what ) {
case 'getPopupData':
- vAPI.tabs.get(null, function(tab) {
- callback(getStats(getTargetTabId(tab)));
+ vAPI.tabs.get(request.tabId, function(tab) {
+ callback(getStats(getTargetTabId(tab), tab.title));
});
return;
@@ -288,6 +292,9 @@ var onMessage = function(request, sender, callback) {
µb.contextMenuClientX = -1;
µb.contextMenuClientY = -1;
µb.elementPickerExec(request.tabId);
+ if (request.select && vAPI.tabs.select) {
+ vAPI.tabs.select(request.tabId);
+ }
break;
case 'hasPopupContentChanged':
View
30 src/js/popup.js
@@ -363,7 +363,10 @@ var renderPrivacyExposure = function() {
// Assume everything has to be done incrementally.
-var renderPopup = function() {
+var renderPopup = function () {
+ if (popupData.tabTitle) {
+ document.title = popupData.appName + " - " + popupData.tabTitle;
+ }
uDom('#appname').text(popupData.appName);
uDom('#version').text(popupData.appVersion);
uDom('body').toggleClass('advancedUser', popupData.advancedUserEnabled);
@@ -449,8 +452,9 @@ var toggleNetFilteringSwitch = function(ev) {
var gotoPick = function() {
messager.send({
what: 'gotoPick',
- tabId: popupData.tabId
- });
+ tabId: popupData.tabId,
+ select: true
+});
vAPI.closePopup();
};
@@ -577,7 +581,7 @@ var setFirewallRuleHandler = function(ev) {
/******************************************************************************/
var reloadTab = function() {
- messager.send({ what: 'reloadTab', tabId: popupData.tabId });
+ messager.send({ what: 'reloadTab', tabId: popupData.tabId, select: true });
// Polling will take care of refreshing the popup content
@@ -651,7 +655,7 @@ var pollForContentChange = (function() {
var queryCallback = function(response) {
if ( response ) {
- getPopupData();
+ getPopupData(popupData.tabId);
return;
}
poll();
@@ -669,22 +673,30 @@ var pollForContentChange = (function() {
/******************************************************************************/
-var getPopupData = function() {
+var getPopupData = function(tabId) {
var onDataReceived = function(response) {
cachePopupData(response);
renderPopup();
hashFromPopupData(true);
pollForContentChange();
};
- messager.send({ what: 'getPopupData' }, onDataReceived);
+ messager.send({ what: 'getPopupData', tabId: tabId }, onDataReceived);
};
/******************************************************************************/
// Make menu only when popup html is fully loaded
-uDom.onLoad(function() {
- getPopupData();
+uDom.onLoad(function () {
+ var tabId = null; //If there's no tab ID specified in the query string, it will default to current tab.
+
+ // Extract the tab id of the page this popup is for
+ var matches = window.location.search.match(/[\?&]tabId=([^&]+)/);
+ if (matches && matches.length === 2) {
+ tabId = matches[1];
+ }
+
+ getPopupData(tabId);
uDom('#switch').on('click', toggleNetFilteringSwitch);
uDom('#gotoPick').on('click', gotoPick);
uDom('a[href]').on('click', gotoURL);
View
3 src/popup.html
@@ -1,7 +1,8 @@
-<!DOCTYPE html>
+<!DOCTYPE html>
<html>
<head>
+<meta name="viewport" content="width=470"> <!-- When showing as a tab in fennec, scale to full size (150px + 320px)-->
<meta charset="utf-8">
<link rel="stylesheet" href="css/common.css" type="text/css">
<link rel="stylesheet" href="css/popup.css" type="text/css">
View
1 tools/make-firefox.sh
@@ -22,6 +22,7 @@ cp platform/firefox/bootstrap.js $DES/
cp platform/firefox/frame*.js $DES/
cp platform/firefox/chrome.manifest $DES/
cp platform/firefox/install.rdf $DES/
+cp platform/firefox/*.xul $DES/
cp LICENSE.txt $DES/
echo "*** uBlock.firefox: Generating meta..."
Something went wrong with that request. Please try again.