From 67f18d023ce220a9c6fc11c7fa0b64618b2e9cca Mon Sep 17 00:00:00 2001 From: Deathamns Date: Mon, 24 Nov 2014 20:00:27 +0100 Subject: [PATCH] Initial Firefox port (base, messaging, locales) --- platform/firefox/bootstrap.js | 46 +++++++ platform/firefox/chrome.manifest | 31 +++++ platform/firefox/frameScript.js | 32 +++++ platform/firefox/install.rdf | 34 ++++++ platform/firefox/vapi-background.js | 181 ++++++++++++++++++++++++++++ platform/firefox/vapi-client.js | 148 +++++++++++++++++++++++ platform/firefox/vapi-common.js | 96 +++++++++++++++ platform/safari/Settings.plist | 32 ++--- platform/safari/Update.plist | 2 +- tools/make-firefox.sh | 28 +++++ tools/xpi-convert-locale.py | 49 ++++++++ 11 files changed, 662 insertions(+), 17 deletions(-) create mode 100644 platform/firefox/bootstrap.js create mode 100644 platform/firefox/chrome.manifest create mode 100644 platform/firefox/frameScript.js create mode 100644 platform/firefox/install.rdf create mode 100644 platform/firefox/vapi-background.js create mode 100644 platform/firefox/vapi-client.js create mode 100644 platform/firefox/vapi-common.js create mode 100644 tools/make-firefox.sh create mode 100644 tools/xpi-convert-locale.py diff --git a/platform/firefox/bootstrap.js b/platform/firefox/bootstrap.js new file mode 100644 index 000000000..f9fae1756 --- /dev/null +++ b/platform/firefox/bootstrap.js @@ -0,0 +1,46 @@ +/* global Services, APP_STARTUP, APP_SHUTDOWN */ +/* exported startup, shutdown, install, uninstall */ + +'use strict'; + +var bgProcess; +Components.utils['import']('resource://gre/modules/Services.jsm'); + +function startup(data, reason) { + bgProcess = function(ev) { + if (ev) { + this.removeEventListener('load', bgProcess); + } + + bgProcess = Services.appShell.hiddenDOMWindow.document; + bgProcess = bgProcess.documentElement.appendChild( + bgProcess.createElementNS('http://www.w3.org/1999/xhtml', 'iframe') + ); + bgProcess.setAttribute('src', 'chrome://ublock/content/background.html'); + }; + + if (reason === APP_STARTUP) { + Services.ww.registerNotification({ + observe: function(subject) { + Services.ww.unregisterNotification(this); + subject.addEventListener('load', bgProcess); + } + }); + } + else { + bgProcess(); + } +} + +function shutdown(data, reason) { + if (reason !== APP_SHUTDOWN) { + bgProcess.parentNode.removeChild(bgProcess); + } +} + +function install() { + // https://bugzil.la/719376 + Services.strings.flushBundles(); +} + +function uninstall() {} \ No newline at end of file diff --git a/platform/firefox/chrome.manifest b/platform/firefox/chrome.manifest new file mode 100644 index 000000000..1206c29cd --- /dev/null +++ b/platform/firefox/chrome.manifest @@ -0,0 +1,31 @@ +content ublock ./ +locale ublock en ./locale/en/ +locale ublock ar ./locale/ar/ +locale ublock cs ./locale/cs/ +locale ublock da ./locale/da/ +locale ublock de ./locale/de/ +locale ublock el ./locale/el/ +locale ublock es ./locale/es/ +locale ublock et ./locale/et/ +locale ublock fi ./locale/fi/ +locale ublock fr ./locale/fr/ +locale ublock he ./locale/he/ +locale ublock hi ./locale/hi/ +locale ublock hr ./locale/hr/ +locale ublock hu ./locale/hu/ +locale ublock id ./locale/id/ +locale ublock it ./locale/it/ +locale ublock ja ./locale/ja/ +locale ublock mr ./locale/mr/ +locale ublock nb ./locale/nb/ +locale ublock nl ./locale/nl/ +locale ublock pl ./locale/pl/ +locale ublock pt-BR ./locale/pt-BR/ +locale ublock pt-PT ./locale/pt-PT/ +locale ublock ro ./locale/ro/ +locale ublock ru ./locale/ru/ +locale ublock sv ./locale/sv/ +locale ublock tr ./locale/tr/ +locale ublock uk ./locale/uk/ +locale ublock vi ./locale/vi/ +locale ublock zh-CN ./locale/zh-CN/ \ No newline at end of file diff --git a/platform/firefox/frameScript.js b/platform/firefox/frameScript.js new file mode 100644 index 000000000..30dd94acb --- /dev/null +++ b/platform/firefox/frameScript.js @@ -0,0 +1,32 @@ +/* globals Services, sendAsyncMessage, addMessageListener, removeMessageListener, content */ + +(function() { + +'use strict'; + +var + app_name = 'ublock', + app_baseURI = 'chrome://' + app_name + '/content/js/', + listeners = {}, + _addMessageListener = function(id, fn) { + _removeMessageListener(id); + listeners[id] = function(msg) { + fn(msg.data); + }; + addMessageListener(id, listeners[id]); + }, + _removeMessageListener = function(id) { + if (listeners[id]) { + removeMessageListener(id, listeners[id]); + } + + delete listeners[id]; + }; + +addMessageListener('µBlock:broadcast', function(msg) { + for (var id in listeners) { + listeners[id](msg.data); + } +}); + +})(); \ No newline at end of file diff --git a/platform/firefox/install.rdf b/platform/firefox/install.rdf new file mode 100644 index 000000000..bc2f7b3b5 --- /dev/null +++ b/platform/firefox/install.rdf @@ -0,0 +1,34 @@ + + + + {2b10c1c8-a11f-4bad-fe9c-1c11e82cac42} + 0.7.0.11 + µBlock + Finally, an efficient blocker. Easy on CPU and memory. + https://github.com/gorhill/uBlock + Raymond Hill + 2 + true + true + 3 + chrome://ublock/content/dashboard.html + + + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 24.0 + 37.0 + + + + + + + {92650c4d-4b8e-4d2a-b7eb-24ecf4f6b63a} + 2.21 + 2.34 + + + + diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js new file mode 100644 index 000000000..a80bc51e1 --- /dev/null +++ b/platform/firefox/vapi-background.js @@ -0,0 +1,181 @@ +/******************************************************************************* + + µBlock - a Chromium browser extension to block requests. + Copyright (C) 2014 The µBlock authors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +/* global Services */ + +// For background page + +/******************************************************************************/ + +(function() { + +'use strict'; + +/******************************************************************************/ + +const {classes: Cc, interfaces: Ci, utils: Cu} = Components; + +Cu['import']('resource://gre/modules/Services.jsm'); + +/******************************************************************************/ + +self.vAPI = self.vAPI || {}; + +vAPI.firefox = true; + +/******************************************************************************/ + +vAPI.messaging = { + gmm: Cc['@mozilla.org/globalmessagemanager;1'].getService(Ci.nsIMessageListenerManager), + frameScript: 'chrome://ublock/content/frameScript.js', + listeners: {}, + defaultHandler: null, + NOOPFUNC: function(){}, + UNHANDLED: 'vAPI.messaging.notHandled' +}; + +/******************************************************************************/ + +vAPI.messaging.gmm.loadFrameScript(vAPI.messaging.frameScript, true); + +/******************************************************************************/ + +vAPI.messaging.listen = function(listenerName, callback) { + this.listeners[listenerName] = callback; +}; + +/******************************************************************************/ + +vAPI.messaging.onMessage = function(request) { + var messageManager = request.target.messageManager; + var listenerId = request.data.portName.split('|'); + var portName = listenerId[1]; + listenerId = listenerId[0]; + + var callback = vAPI.messaging.NOOPFUNC; + if ( request.data.requestId !== undefined ) { + callback = function(response) { + messageManager.sendAsyncMessage( + listenerId, + JSON.stringify({ + requestId: request.data.requestId, + portName: portName, + msg: response !== undefined ? response : null + }) + ); + }; + } + + // TODO: + var sender = { + tab: { + id: 0 + } + }; + + // Specific handler + var r = vAPI.messaging.UNHANDLED; + var listener = vAPI.messaging.listeners[portName]; + if ( typeof listener === 'function' ) { + r = listener(request.data.msg, sender, callback); + } + if ( r !== vAPI.messaging.UNHANDLED ) { + return; + } + + // Default handler + r = vAPI.messaging.defaultHandler(request.data.msg, sender, callback); + if ( r !== vAPI.messaging.UNHANDLED ) { + return; + } + + console.error('µBlock> messaging > unknown request: %o', request.data); + + // Unhandled: + // Need to callback anyways in case caller expected an answer, or + // else there is a memory leak on caller's side + callback(); +}; + +/******************************************************************************/ + +vAPI.messaging.setup = function(defaultHandler) { + // Already setup? + if ( this.defaultHandler !== null ) { + return; + } + + if ( typeof defaultHandler !== 'function' ) { + defaultHandler = function(){ return vAPI.messaging.UNHANDLED; }; + } + this.defaultHandler = defaultHandler; + + this.gmm.addMessageListener(vAPI.app.name + ':background', this.onMessage); +}; + +/******************************************************************************/ + +vAPI.messaging.broadcast = function(msg) { + this.gmm.broadcastAsyncMessage(vAPI.app.name + ':broadcast', msg); +}; + +/******************************************************************************/ + +vAPI.lastError = function() { + return null; +}; + +/******************************************************************************/ + +// clean up when the extension is disabled + +window.addEventListener('unload', function() { + vAPI.messaging.gmm.removeMessageListener( + app.name + ':background', + vAPI.messaging.postMessage + ); + vAPI.messaging.gmm.removeDelayedFrameScript(vAPI.messaging.frameScript); + + // close extension tabs + var enumerator = Services.wm.getEnumerator('navigator:browser'); + var host = 'ublock'; + var gBrowser, tabs, i, extURI; + + while (enumerator.hasMoreElements()) { + gBrowser = enumerator.getNext().gBrowser; + tabs = gBrowser.tabs; + i = tabs.length; + + while (i--) { + extURI = tabs[i].linkedBrowser.currentURI; + + if (extURI.scheme === 'chrome' && extURI.host === host) { + gBrowser.removeTab(tabs[i]); + } + } + } +}); + +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js new file mode 100644 index 000000000..871996dea --- /dev/null +++ b/platform/firefox/vapi-client.js @@ -0,0 +1,148 @@ +/******************************************************************************* + + µBlock - a Chromium browser extension to block requests. + Copyright (C) 2014 The µBlock authors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +// For non background pages + +/******************************************************************************/ + +(function() { + +'use strict'; + +/******************************************************************************/ + +self.vAPI = self.vAPI || {}; +vAPI.firefox = true; + +/******************************************************************************/ + +var messagingConnector = function(response) { + if ( !response ) { + return; + } + + var channels = vAPI.messaging.channels; + var channel, listener; + + if ( response.broadcast === true ) { + for ( channel in channels ) { + if ( channels.hasOwnProperty(channel) === false ) { + continue; + } + listener = channels[channel].listener; + if ( typeof listener === 'function' ) { + listener(response.msg); + } + } + return; + } + + if ( response.requestId ) { + listener = vAPI.messaging.listeners[response.requestId]; + delete vAPI.messaging.listeners[response.requestId]; + delete response.requestId; + } + + if ( !listener ) { + channel = channels[response.portName]; + listener = channel && channel.listener; + } + + if ( typeof listener === 'function' ) { + listener(response.msg); + } +}; + +/******************************************************************************/ + +var uniqueId = function() { + return parseInt(Math.random() * 1e10, 10).toString(36); +}; + +/******************************************************************************/ + +vAPI.messaging = { + channels: {}, + listeners: {}, + requestId: 1, + connectorId: uniqueId(), + + setup: function() { + this.connector = function(msg) { + messagingConnector(JSON.parse(msg)); + }; + addMessageListener(this.connectorId, this.connector); + }, + + close: function() { + if (this.connector) { + removeMessageListener(this.connectorId, this.connector); + this.connector = null; + this.channels = {}; + this.listeners = {}; + } + }, + + channel: function(channelName, callback) { + if ( !channelName ) { + return; + } + + this.channels[channelName] = { + portName: channelName, + listener: typeof callback === 'function' ? callback : null, + send: function(message, callback) { + if ( !vAPI.messaging.connector ) { + vAPI.messaging.setup(); + } + + message = { + portName: vAPI.messaging.connectorId + '|' + this.portName, + msg: message + }; + + if ( callback ) { + message.requestId = vAPI.messaging.requestId++; + vAPI.messaging.listeners[message.requestId] = callback; + } + + sendAsyncMessage('µBlock:background', message); + }, + close: function() { + delete vAPI.messaging.channels[this.portName]; + } + }; + + return this.channels[channelName]; + } +}; + +/******************************************************************************/ + +vAPI.canExecuteContentScript = function() { + return true; +}; + +/******************************************************************************/ + +})(); + +/******************************************************************************/ diff --git a/platform/firefox/vapi-common.js b/platform/firefox/vapi-common.js new file mode 100644 index 000000000..45825e77d --- /dev/null +++ b/platform/firefox/vapi-common.js @@ -0,0 +1,96 @@ +/******************************************************************************* + + µBlock - a Chromium browser extension to block requests. + Copyright (C) 2014 The µBlock authors + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see {http://www.gnu.org/licenses/}. + + Home: https://github.com/gorhill/uBlock +*/ + +// For background page or non-background pages + +/******************************************************************************/ + +(function() { + +'use strict'; + +self.vAPI = self.vAPI || {}; + +/******************************************************************************/ + +// http://www.w3.org/International/questions/qa-scripts#directions + +var setScriptDirection = function(language) { + document.body.setAttribute( + 'dir', + ['ar', 'he', 'fa', 'ps', 'ur'].indexOf(language) !== -1 ? 'rtl' : 'ltr' + ); +}; + +/******************************************************************************/ + +vAPI.download = function(details) { + if ( !details.url ) { + return; + } + + var a = document.createElement('a'); + + if ( 'download' in a ) { + a.href = details.url; + a.setAttribute('download', details.filename || ''); + a.dispatchEvent(new MouseEvent('click')); + return; + } + var messager = vAPI.messaging.channel('_download'); + messager.send({ + what: 'gotoURL', + details: { + url: details.url, + index: -1 + } + }); + messager.close(); +}; + +/******************************************************************************/ + +vAPI.getURL = function(path) { + return 'chrome://ublock/content/' + path.replace(/^\/+/, ''); +}; + +vAPI.i18n = (function() { + var stringBundle = Components.classes['@mozilla.org/intl/stringbundle;1'] + .getService(Components.interfaces.nsIStringBundleService) + .createBundle('chrome://ublock/locale/messages.properties'); + + return function(s) { + try { + return stringBundle.GetStringFromName(s); + } catch (ex) { + return s; + } + }; +})(); + +setScriptDirection(navigator.language); + +/******************************************************************************/ + +})(); + +/******************************************************************************/ + diff --git a/platform/safari/Settings.plist b/platform/safari/Settings.plist index 6c80bbb43..40cc1ff16 100644 --- a/platform/safari/Settings.plist +++ b/platform/safari/Settings.plist @@ -2,21 +2,21 @@ - - DefaultValue - - FalseValue - - Key - open_prefs - Secure - - Title - Click to see the Preferences - TrueValue - - Type - CheckBox - + + DefaultValue + + FalseValue + + Key + open_prefs + Secure + + Title + Click to see the Preferences + TrueValue + + Type + CheckBox + diff --git a/platform/safari/Update.plist b/platform/safari/Update.plist index 969fe646e..1dea60702 100644 --- a/platform/safari/Update.plist +++ b/platform/safari/Update.plist @@ -14,7 +14,7 @@ CFBundleVersion {buildNumber} URL - https://.../uBlock.safariextz + https://github.com/gorhill/uBlock/releases/download/0.7.0.11/uBlock-0.7.0.11.safariextz diff --git a/tools/make-firefox.sh b/tools/make-firefox.sh new file mode 100644 index 000000000..aeb6c31b7 --- /dev/null +++ b/tools/make-firefox.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# +# This script assumes a linux environment + +echo "*** uBlock_xpi: Copying files" +# use underscore instead of a dot! +DES=dist/build/uBlock_xpi +rm -r $DES +mkdir -p $DES +cp -R assets $DES/ +rm $DES/assets/*.sh +cp -R src/css $DES/ +cp -R src/img $DES/ +cp -R src/js $DES/ +cp -R src/lib $DES/ +cp -R src/_locales $DES/ +cp src/*.html $DES/ +cp src/img/icon_128.png $DES/icon.png +cp platform/vapi-appinfo.js $DES/js/ +cp platform/firefox/vapi-*.js $DES/js/ +cp platform/firefox/bootstrap.js $DES/ +cp platform/firefox/frameScript.js $DES/ +cp platform/firefox/chrome.manifest $DES/ +cp platform/firefox/install.rdf $DES/ + +python tools/xpi-convert-locale.py $DES/ + +echo "*** uBlock_xpi: Package done." diff --git a/tools/xpi-convert-locale.py b/tools/xpi-convert-locale.py new file mode 100644 index 000000000..1467f3612 --- /dev/null +++ b/tools/xpi-convert-locale.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +import os +import json +import sys +from shutil import rmtree as rmt +from collections import OrderedDict + +if not sys.argv[1]: + raise SystemExit('Build dir missing.') + +osp = os.path +pj = osp.join + + +def rmtree(path): + if osp.exists(path): + rmt(path) + + +def mkdirs(path): + try: + os.makedirs(path) + finally: + return osp.exists(path) + + +build_dir = osp.abspath(sys.argv[1]) +source_locale_dir = pj(build_dir, '_locales') +target_locale_dir = pj(build_dir, 'locale') + +for alpha2 in os.listdir(source_locale_dir): + locale_path = pj(source_locale_dir, alpha2, 'messages.json') + with open(locale_path, encoding='utf-8') as f: + string_data = json.load(f, object_pairs_hook=OrderedDict) + + alpha2 = alpha2.replace('_', '-') + + mkdirs(pj(target_locale_dir, alpha2)) + + locale_path = pj(target_locale_dir, alpha2, 'messages.properties') + with open(locale_path, 'wt', encoding='utf-8', newline='\n') as f: + for string_name in string_data: + f.write(string_name) + f.write('=') + f.write(string_data[string_name]['message'].replace('\n', r'\n')) + f.write('\n') + +rmtree(source_locale_dir)