// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @fileoverview This file contains the implementation of the Chrome extension * user script APIs for the CEEE Firefox add-on. This file is loaded by the * overlay.xul file, and requires that overlay.js has already been loaded. * * @supported Firefox 3.x */ /** * Constructor for user script management API object. * @param {!Object} instance A reference the the main CEEE instance for * this top-level window. * @constructor */ var CEEE_UserScriptManager; (function() { /** A reference to the main CEEE instance for this top-level window. */ var ceee; /** * A map of open ports for this web page for all extensions. The key to the * map is a portId (number) that is local to this web page. This portId is * assigned by the call to OpenChannelToExtension. * * The port is considered opened once ChromeFrame responds with the "channel * opened" event. This event will contain another portId assigned on the * chrome side of the port, referred to here as the remote port Id. Because * Chrome and ChromeFrame do not know about the portIds in this code, the * functions below map from portId to remotePortId and back as needed. */ var ports = CEEE_globals.ports; /** * An array of "content_scripts", as defined by the Chrome Extension API. */ var scripts; /** * Path relative to the add-on root where user-related scripts are found. * @const */ var USER_SCRIPTS_DIR = 'content/us'; /** * Create an nsIURL object from the given string spec. * * @param {!string} spec The string that should be converted into an nsURL * object. */ function makeUrl(spec) { var uri = CEEE_ioService.newURI(spec, null, null); return uri && uri.QueryInterface(Components.interfaces.nsIURL); } /** * Create an nsIURL object from the given nsIFile object. * * @param {!nsIFile} file The file that should be converted into an nsURL * object. */ function makeFileUrl(file) { var uri = CEEE_ioService.newFileURI(file); return uri && uri.QueryInterface(Components.interfaces.nsIURL); } /** * Is the given pattern a valid host pattern, as defined by the Chrome * URLPattern class? There are only 3 possible values: * * - '*', which matches all hosts * - ''+, which matches an exact domain * - '*.'+, which matches a subdomain * * Its assumed that the pattern does not contain an slashes (/). * * @param {!string} pattern String pattern to test. * @return true if the pattern is valid. */ function isValidHostPattern(pattern) { return pattern == '*' || pattern.indexOf('*') == -1 || (pattern.charAt(0) == '*' && pattern.charAt(1) == '.' && pattern.substr(2).indexOf('*') == -1); } /** * Does the given URL match one of the URL patterns given? * * @param {!Location} url URL string to match. * @param {Array} patterns Array of patterns to match. */ function matchesPattern(url, patterns) { // Some URLs may not have a host, like about:blank. In this case, we can // never match. In firefox, this is implemented with the nsSimpleURI class. // The implementation of this class will always throw an error when accessing // the host property, so we first check to see if the protocol is not // "about:" before checking the host. nsSimpleURI is designed specifically // for about: var protocol = url.protocol; if (protocol == 'about:') return false; var host = url.host; var path = url.pathname + url.search; for (var i = 0; i < patterns.length; ++i) { var pattern = patterns[i]; // Make sure protocols match. if (pattern.protocolPattern != protocol) { continue; } // Make sure hosts match. var hostPattern = pattern.hostPattern; if (hostPattern.length > 0) { if (hostPattern.charAt(0) != '*') { if (host != hostPattern) { continue; } } else { // Make sure the host ends with the pattern (minus initial *). var sub = hostPattern.substr(1); if (sub != host.match(sub)) { continue; } } } // Make sure paths match. var pathPattern = pattern.pathPattern; if (path && !pathPattern.test(path)) { continue; } // Found a match! ceee.logInfo('URL=' + url.toString() + ' matches with ' + pattern.protocolPattern + ' ' + pattern.hostPattern + ' ' + pattern.pathPattern); return true; } return false; } /** * Checks whether the given window is the top window. * * @param {!nsIDOMWindow} w The window to test. * @return {boolean} true if the given window is the top window. */ function isTopWindow(w) { return w.top == w.self; } /** * Get a File object for the given user script. Its assumed that the user * script file name is relative to USER_SCRIPTS_DIR in the add-on. * * @param {!string} name Relative file name of the user script. * @return A File object that represents the user script. */ function getScriptFile(name) { var m = Components.classes['@mozilla.org/extensions/manager;1']. getService(Components.interfaces.nsIExtensionManager); var file = m.getInstallLocation(CEEE_globals.ADDON_ID) .getItemFile(CEEE_globals.ADDON_ID, USER_SCRIPTS_DIR); file.append(name); return file; } /** * Get a File object for the given user script. Its assumed that the user * script file name is relative to the Chrome Extension root. * * @param {!string} name Relative file name of the user script. * @return A File object that represents the user script. */ function getUserScriptFile(name) { var file = ceee.getToolstripDir(); if (file) { var parts = name.split('/'); for (var i = 0; i < parts.length; ++i) { file.append(parts[i]); } if (!file.exists() || !file.isFile()) file = null; } if (file) ceee.logInfo('User script: ' + file.path); return file; } /** * Evaluate a user-defined script file in the context of a web page, with * debugging in mind. Instead of evaluating the script in a sandbox, which * Firebug cannot see, a