Path: blob/master/sites/bitcoin/socialwidgets.js
777 views
/*1* This file is part of Privacy Badger <https://www.eff.org/privacybadger>2* Copyright (C) 2014 Electronic Frontier Foundation3* Derived from ShareMeNot4* Copyright (C) 2011-2014 University of Washington5*6* Privacy Badger is free software: you can redistribute it and/or modify7* it under the terms of the GNU General Public License version 3 as8* published by the Free Software Foundation.9*10* Privacy Badger is distributed in the hope that it will be useful,11* but WITHOUT ANY WARRANTY; without even the implied warranty of12* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the13* GNU General Public License for more details.14*15* You should have received a copy of the GNU General Public License16* along with Privacy Badger. If not, see <http://www.gnu.org/licenses/>.17*/1819/*20* ShareMeNot is licensed under the MIT license:21* http://www.opensource.org/licenses/mit-license.php22*23* Copyright (c) 2011-2014 University of Washington24*25* Permission is hereby granted, free of charge, to any person obtaining a26* copy of this software and associated documentation files (the27* "Software"), to deal in the Software without restriction, including28* without limitation the rights to use, copy, modify, merge, publish,29* distribute, sublicense, and/or sell copies of the Software, and to30* permit persons to whom the Software is furnished to do so, subject to31* the following conditions:32*33* The above copyright notice and this permission notice shall be included34* in all copies or substantial portions of the Software.35*36* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS37* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF38* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.39* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY40* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,41* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE42* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.43*/4445/**46* Social widget tracker data, read from file.47*/48let trackerInfo;4950let i18n = chrome.i18n;515253/**54* Initializes the content script.55*/56function initialize() {57// Get tracker info and check for initial blocks (that happened58// before content script was attached)59getTrackerData(function (trackers, trackerButtonsToReplace) {60trackerInfo = trackers;61replaceInitialTrackerButtonsHelper(trackerButtonsToReplace);62});6364// Set up listener for blocks that happen after initial check65chrome.runtime.onMessage.addListener(function(request/*, sender, sendResponse*/) {66if (request.replaceSocialWidget) {67replaceSubsequentTrackerButtonsHelper(request.trackerDomain);68}69});70}7172/**73* Creates a replacement button element for the given tracker.74*75* @param {Tracker} tracker the Tracker object for the button76*77* @param {Element} trackerElem the tracking element that we are replacing78*79* @param {Function} callback called with the replacement button element for the tracker80*/81function createReplacementButtonImage(tracker, trackerElem, callback) {82var buttonData = tracker.replacementButton;8384// already have replace button image URI cached85if (buttonData.buttonUrl) {86return setTimeout(function () {87_createReplacementButtonImageCallback(tracker, trackerElem, callback);88}, 0);89}9091if (buttonData.loading) {92return setTimeout(function () {93createReplacementButtonImage(tracker, trackerElem, callback);94}, 10);95}9697// don't have image data cached yet, get it from the background page98buttonData.loading = true;99chrome.runtime.sendMessage({100getReplacementButton: buttonData.imagePath101}, function (response) {102buttonData.buttonUrl = response; // cache image data103_createReplacementButtonImageCallback(tracker, trackerElem, callback);104});105}106107function _createReplacementButtonImageCallback(tracker, trackerElem, callback) {108var buttonData = tracker.replacementButton;109110var button = document.createElement("img");111112var buttonUrl = buttonData.buttonUrl;113var buttonType = buttonData.type;114var details = buttonData.details;115116button.setAttribute("src", buttonUrl);117button.setAttribute("title", i18n.getMessage("social_tooltip_pb_has_replaced") +118tracker.name + i18n.getMessage("social_tooltip_button"));119button.setAttribute(120"style",121"border: none !important; cursor: pointer !important; height: auto !important; width: auto !important;"122);123124switch (buttonType) {125case 0: // normal button type; just open a new window when clicked126var popupUrl = details + encodeURIComponent(window.location.href);127128button.addEventListener("click", function() {129window.open(popupUrl);130});131132break;133134// in place button type; replace the existing button135// with an iframe when clicked136case 1:137var iframeUrl = details + encodeURIComponent(window.location.href);138139button.addEventListener("click", function() {140replaceButtonWithIframeAndUnblockTracker(button, buttonData.unblockDomains, iframeUrl);141}, { once: true });142143break;144145// in place button type; replace the existing button with code146// specified in the Trackers file147case 2:148button.addEventListener("click", function() {149replaceButtonWithHtmlCodeAndUnblockTracker(button, buttonData.unblockDomains, details);150}, { once: true });151break;152153case 3:154button.addEventListener("click", function() {155replaceButtonWithHtmlCodeAndUnblockTracker(button, buttonData.unblockDomains, trackerElem);156}, { once: true });157break;158159default:160throw "Invalid button type specified: " + buttonType;161}162163callback(button);164}165166167/**168* Unblocks the given tracker and replaces the given button with an iframe169* pointing to the given URL.170*171* @param {Element} button the DOM element of the button to replace172* @param {Tracker} tracker the Tracker object for the tracker that should be173* unblocked174* @param {String} iframeUrl the URL of the iframe to replace the button175*/176function replaceButtonWithIframeAndUnblockTracker(button, tracker, iframeUrl) {177unblockTracker(tracker, function() {178// check is needed as for an unknown reason this callback function is179// executed for buttons that have already been removed; we are trying180// to prevent replacing an already removed button181if (button.parentNode !== null) {182var iframe = document.createElement("iframe");183184iframe.setAttribute("src", iframeUrl);185iframe.setAttribute("style", "border: none !important; height: 1.5em !important;");186187button.parentNode.replaceChild(iframe, button);188}189});190}191192/**193* Unblocks the given tracker and replaces the given button with the194* HTML code defined in the provided Tracker object.195*196* @param {Element} button the DOM element of the button to replace197* @param {Tracker} tracker the Tracker object for the tracker that should be198* unblocked199* @param {(String|Element)} html an HTML string or DOM Element that should replace the button200*/201function replaceButtonWithHtmlCodeAndUnblockTracker(button, tracker, html) {202unblockTracker(tracker, function() {203// check is needed as for an unknown reason this callback function is204// executed for buttons that have already been removed; we are trying205// to prevent replacing an already removed button206if (button.parentNode !== null) {207var codeContainer = document.createElement("div");208if (typeof html == "string") {209codeContainer.innerHTML = html;210} else {211codeContainer.innerHTML = html.outerHTML;212}213214button.parentNode.replaceChild(codeContainer, button);215216replaceScriptsRecurse(codeContainer);217}218});219}220221/**222* Dumping scripts into innerHTML won't execute them, so replace them223* with executable scripts.224*/225function replaceScriptsRecurse(node) {226if (node.getAttribute && node.getAttribute("type") == "text/javascript") {227var script = document.createElement("script");228script.text = node.innerHTML;229script.src = node.src;230node.parentNode.replaceChild(script, node);231} else {232var i = 0;233var children = node.childNodes;234while (i < children.length) {235replaceScriptsRecurse(children[i]);236i++;237}238}239return node;240}241242243/**244* Replaces all tracker buttons on the current web page with the internal245* replacement buttons, respecting the user's blocking settings.246*247* @param {Object} a map of Tracker names to Boolean values saying whether248* those trackers' buttons should be replaced249*/250function replaceInitialTrackerButtonsHelper(trackerButtonsToReplace) {251trackerInfo.forEach(function(tracker) {252var replaceTrackerButtons = trackerButtonsToReplace[tracker.name];253if (replaceTrackerButtons) {254replaceIndividualButton(tracker);255}256});257}258259/**260* Individually replaces tracker buttons blocked after initial check.261*/262function replaceSubsequentTrackerButtonsHelper(trackerDomain) {263if (!trackerInfo) { return; }264trackerInfo.forEach(function(tracker) {265var replaceTrackerButtons = (tracker.domain == trackerDomain);266if (replaceTrackerButtons) {267replaceIndividualButton(tracker);268}269});270}271272/**273* Actually do the work of replacing the button.274*/275function replaceIndividualButton(tracker) {276277// makes a comma separated list of CSS selectors that specify278// buttons for the current tracker; used for document.querySelectorAll279var buttonSelectorsString = tracker.buttonSelectors.toString();280var buttonsToReplace =281document.querySelectorAll(buttonSelectorsString);282283buttonsToReplace.forEach(function (buttonToReplace) {284console.log("Replacing social widget for " + tracker.name);285286createReplacementButtonImage(tracker, buttonToReplace, function (button) {287buttonToReplace.parentNode.replaceChild(button, buttonToReplace);288});289});290}291292/**293* Gets data about which tracker buttons need to be replaced from the main294* extension and passes it to the provided callback function.295*296* @param {Function} callback the function to call when the tracker data is297* received; the arguments passed are the folder298* containing the content script, the tracker299* data, and a mapping of tracker names to300* whether those tracker buttons need to be301* replaced302*/303function getTrackerData(callback) {304chrome.runtime.sendMessage({checkReplaceButton:document.location.hostname}, function(response) {305if (response) {306var trackers = response.trackers;307var trackerButtonsToReplace = response.trackerButtonsToReplace;308callback(trackers, trackerButtonsToReplace);309}310});311}312313/**314* Unblocks the tracker with the given name from the page. Calls the315* provided callback function after the tracker has been unblocked.316*317* @param {String} trackerName the name of the tracker to unblock318* @param {Function} callback the function to call after the tracker has319* been unblocked320*/321function unblockTracker(buttonUrls, callback) {322var request = {323"unblockSocialWidget" : true,324"buttonUrls": buttonUrls325};326chrome.runtime.sendMessage(request, callback);327}328329// END FUNCTION DEFINITIONS ///////////////////////////////////////////////////330331(function () {332333// don't inject into non-HTML documents (such as XML documents)334// but do inject into XHTML documents335if (document instanceof HTMLDocument === false && (336document instanceof XMLDocument === false ||337document.createElement('div') instanceof HTMLDivElement === false338)) {339return;340}341342chrome.runtime.sendMessage({343checkSocialWidgetReplacementEnabled: true344}, function (checkSocialWidgetReplacementEnabled) {345if (!checkSocialWidgetReplacementEnabled) {346return;347}348initialize();349});350351}());352353354