From fbffc5b07e1e9069e0a5427f1df1ae67f4e2a2e9 Mon Sep 17 00:00:00 2001 From: Deathamns Date: Mon, 20 Oct 2014 19:05:12 +0200 Subject: [PATCH] Use HTML5 download instead of extension API Benefits: - Cross browser solution (however only for relatively new browsers) - Doesn't need extra permission in Chrome If the browser doesn't suppor the download attribute, then a new tab will be opened with the exported data. Other changes: - Start the download only if the data is not empty (previously the download started anyway) - Reorder code in vapi-client.js for Safari, so unnecessary code doesn't run on extension pages --- meta/crx/manifest.json | 1 - src/js/1p-filters.js | 13 ++-- src/js/about.js | 11 ++- src/js/vapi-client.js | 168 ++++++++++++++++++++--------------------- src/js/vapi-common.js | 27 ++++++- src/js/whitelist.js | 13 ++-- src/manifest.json | 1 - 7 files changed, 133 insertions(+), 101 deletions(-) diff --git a/meta/crx/manifest.json b/meta/crx/manifest.json index 22641fa83..be0a7081b 100644 --- a/meta/crx/manifest.json +++ b/meta/crx/manifest.json @@ -21,7 +21,6 @@ "permissions": [ "contextMenus", - "downloads", "storage", "tabs", "unlimitedStorage", diff --git a/src/js/1p-filters.js b/src/js/1p-filters.js index 171620588..33978569a 100644 --- a/src/js/1p-filters.js +++ b/src/js/1p-filters.js @@ -99,11 +99,14 @@ var startImportFilePicker = function() { /******************************************************************************/ function exportUserFiltersToFile() { - chrome.downloads.download({ - 'url': 'data:text/plain,' + encodeURIComponent(uDom('#userFilters').val()), - 'filename': 'my-ublock-filters.txt', - 'saveAs': true - }); + var val = uDom('#userFilters').val().trim(); + + if (val) { + vAPI.download({ + 'url': 'data:text/plain,' + encodeURIComponent(val), + 'filename': 'my-ublock-filters.txt' + }); + } } /******************************************************************************/ diff --git a/src/js/about.js b/src/js/about.js index c8b41bbc0..d7407c63a 100644 --- a/src/js/about.js +++ b/src/js/about.js @@ -34,11 +34,14 @@ var messager = vAPI.messaging.channel('about.js'); var exportToFile = function() { var onUserDataReady = function(userData) { - chrome.downloads.download({ + if (!userData) { + return; + } + + vAPI.download({ 'url': 'data:text/plain,' + encodeURIComponent(JSON.stringify(userData)), - 'filename': 'ublock-backup.txt', - 'saveAs': true -}); + 'filename': 'ublock-backup.txt' + }); }; messager.send({ what: 'getUserData' }, onUserDataReady); diff --git a/src/js/vapi-client.js b/src/js/vapi-client.js index 8367fb6e4..f7c7a091d 100644 --- a/src/js/vapi-client.js +++ b/src/js/vapi-client.js @@ -95,6 +95,88 @@ if (window.chrome) { } }; } else if (window.safari) { + // relevant? + // https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/MessagesandProxies/MessagesandProxies.html#//apple_ref/doc/uid/TP40009977-CH14-SW12 + vAPI.messaging = { + requestId: 0, + listeners: {}, + channels: {}, + connector: messagingConnector, + setup: function() { + this._connector = function(msg) { + vAPI.messaging.connector(msg.message); + }; + safari.self.addEventListener('message', this._connector, false); + + this.channels['vAPI'] = { + listener: function(msg) { + if (msg.cmd === 'runScript' && msg.details.code) { + Function(msg.details.code).call(window); + } + } + }; + }, + close: function() { + if (this._connector) { + safari.self.removeEventListener('message', this._connector, false); + this.channels = this.listeners = null; + } + }, + channel: function(name, callback) { + if (!name) { + return; + } + + if (!this._connector) { + this.setup(); + } + + this.channels[name] = { + portName: name, + listener: typeof callback === 'function' ? callback : null, + send: function(message, callback) { + message = { + portName: this.portName, + msg: message + }; + + if (callback) { + message.requestId = ++vAPI.messaging.requestId; + vAPI.messaging.listeners[message.requestId] = callback; + } + + if (safari.extension.globalPage) { + // popover content doesn't know messaging... + safari.extension.globalPage.contentWindow + .vAPI.messaging.connector({ + name: 'message', + message: message, + target: { + page: { + dispatchMessage: function(name, msg) { + vAPI.messaging.connector(msg); + } + } + } + }); + } + else { + safari.self.tab.dispatchMessage('message', message); + } + }, + close: function() { + delete vAPI.messaging.channels[this.portName]; + } + }; + + return this.channels[name]; + } + }; + + if (location.protocol === "safari-extension:") { + return; + } + var linkHelper = document.createElement('a'); var onBeforeLoad = function(e, details) { if (e.url && e.url.slice(0, 5) === 'data:') { @@ -176,12 +258,10 @@ if (window.chrome) { } }; + document.addEventListener('beforeload', onBeforeLoad, true); + // intercepting xhr requests setTimeout(function() { - if (location.protocol === "safari-extension:") { - return; - } - var randomEventName = parseInt(Math.random() * 1e15, 10).toString(36); var beforeLoadEvent = document.createEvent('Event'); beforeLoadEvent.initEvent('beforeload'); @@ -225,86 +305,6 @@ if (window.chrome) { "})();"].join('')); }, 0); - document.addEventListener('beforeload', onBeforeLoad, true); - - // relevant? - // https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/MessagesandProxies/MessagesandProxies.html#//apple_ref/doc/uid/TP40009977-CH14-SW12 - vAPI.messaging = { - requestId: 0, - listeners: {}, - channels: {}, - connector: messagingConnector, - setup: function() { - this._connector = function(msg) { - vAPI.messaging.connector(msg.message); - }; - safari.self.addEventListener('message', this._connector, false); - - this.channels['vAPI'] = { - listener: function(msg) { - if (msg.cmd === 'runScript' && msg.details.code) { - Function(msg.details.code).call(window); - } - } - }; - }, - close: function() { - if (this._connector) { - safari.self.removeEventListener('message', this._connector, false); - this.channels = this.listeners = null; - } - }, - channel: function(name, callback) { - if (!name) { - return; - } - - if (!this._connector) { - this.setup(); - } - - this.channels[name] = { - portName: name, - listener: typeof callback === 'function' ? callback : null, - send: function(message, callback) { - message = { - portName: this.portName, - msg: message - }; - - if (callback) { - message.requestId = ++vAPI.messaging.requestId; - vAPI.messaging.listeners[message.requestId] = callback; - } - - if (safari.extension.globalPage) { - // popover content doesn't know messaging... - safari.extension.globalPage.contentWindow - .vAPI.messaging.connector({ - name: 'message', - message: message, - target: { - page: { - dispatchMessage: function(name, msg) { - vAPI.messaging.connector(msg); - } - } - } - }); - } - else { - safari.self.tab.dispatchMessage('message', message); - } - }, - close: function() { - delete vAPI.messaging.channels[this.portName]; - } - }; - - return this.channels[name]; - } - }; - var onContextMenu = function(e) { var details = { tagName: e.target.tagName.toLowerCase(), diff --git a/src/js/vapi-common.js b/src/js/vapi-common.js index 5d22bb0e8..5cd1449de 100644 --- a/src/js/vapi-common.js +++ b/src/js/vapi-common.js @@ -1,10 +1,35 @@ -// only for background and other extension pages +// could be used for background and other extension pages (function() { 'use strict'; window.vAPI = window.vAPI || {}; +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')); + } + else { + var messager = vAPI.messaging.channel('download'); + messager.send({ + what: 'gotoURL', + details: { + url: a.target.href, + index: -1 + } + }); + messager.close(); + } +}; + if (window.chrome) { var chrome = window.chrome; diff --git a/src/js/whitelist.js b/src/js/whitelist.js index e6dcecbd5..0cd8dac1c 100644 --- a/src/js/whitelist.js +++ b/src/js/whitelist.js @@ -93,11 +93,14 @@ var startImportFilePicker = function() { /******************************************************************************/ var exportWhitelistToFile = function() { - chrome.downloads.download({ - 'url': 'data:text/plain,' + encodeURIComponent(uDom('#whitelist').val()), - 'filename': 'my-ublock-whitelist.txt', - 'saveAs': true - }); + var val = uDom('#whitelist').val().trim(); + + if (val) { + vAPI.download({ + 'url': 'data:text/plain,' + encodeURIComponent(val), + 'filename': 'my-ublock-whitelist.txt' + }); + } }; /******************************************************************************/ diff --git a/src/manifest.json b/src/manifest.json index 38f1938df..4fc5f9144 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -21,7 +21,6 @@ "permissions": [ "contextMenus", - "downloads", "storage", "tabs", "unlimitedStorage",