addon-sdk/source/lib/sdk/content/loader.js

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/addon-sdk/source/lib/sdk/content/loader.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,207 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +
     1.8 +"use strict";
     1.9 +
    1.10 +module.metadata = {
    1.11 +  "stability": "unstable"
    1.12 +};
    1.13 +
    1.14 +const { EventEmitter } = require('../deprecated/events');
    1.15 +const { validateOptions } = require('../deprecated/api-utils');
    1.16 +const { isValidURI, URL } = require('../url');
    1.17 +const file = require('../io/file');
    1.18 +const { contract } = require('../util/contract');
    1.19 +const { isString, instanceOf } = require('../lang/type');
    1.20 +
    1.21 +const LOCAL_URI_SCHEMES = ['resource', 'data'];
    1.22 +
    1.23 +// Returns `null` if `value` is `null` or `undefined`, otherwise `value`.
    1.24 +function ensureNull(value) value == null ? null : value
    1.25 +
    1.26 +// map of property validations
    1.27 +const valid = {
    1.28 +  contentURL: {
    1.29 +    map: function(url) !url ? ensureNull(url) : url.toString(), 
    1.30 +    is: ['undefined', 'null', 'string'],
    1.31 +    ok: function (url) {
    1.32 +      if (url === null)
    1.33 +        return true;
    1.34 +      return isValidURI(url);
    1.35 +    },
    1.36 +    msg: 'The `contentURL` option must be a valid URL.'
    1.37 +  },
    1.38 +  contentScriptFile: {
    1.39 +    is: ['undefined', 'null', 'string', 'array', 'object'],
    1.40 +    map: ensureNull,
    1.41 +    ok: function(value) {
    1.42 +      if (value === null)
    1.43 +        return true;
    1.44 +
    1.45 +      value = [].concat(value);
    1.46 +
    1.47 +      // Make sure every item is a string or an
    1.48 +      // URL instance, and also a local file URL.
    1.49 +      return value.every(function (item) {
    1.50 +
    1.51 +        if (!isString(item) && !(item instanceof URL)) 
    1.52 +          return false;
    1.53 +
    1.54 +        try {
    1.55 +          return ~LOCAL_URI_SCHEMES.indexOf(URL(item).scheme);
    1.56 +        }
    1.57 +        catch(e) {
    1.58 +          return false;
    1.59 +        }
    1.60 +      });
    1.61 +
    1.62 +    },
    1.63 +    msg: 'The `contentScriptFile` option must be a local URL or an array of URLs.'
    1.64 +  },
    1.65 +  contentScript: {
    1.66 +    is: ['undefined', 'null', 'string', 'array'],
    1.67 +    map: ensureNull,
    1.68 +    ok: function(value) {
    1.69 +      return !Array.isArray(value) || value.every(
    1.70 +        function(item) { return typeof item === 'string' }
    1.71 +      );
    1.72 +    },
    1.73 +    msg: 'The `contentScript` option must be a string or an array of strings.'
    1.74 +  },
    1.75 +  contentScriptWhen: {
    1.76 +    is: ['string'],
    1.77 +    ok: function(value) { return ~['start', 'ready', 'end'].indexOf(value) },
    1.78 +    map: function(value) {
    1.79 +      return value || 'end';
    1.80 +    },
    1.81 +    msg: 'The `contentScriptWhen` option must be either "start", "ready" or "end".'
    1.82 +  },
    1.83 +  contentScriptOptions: {
    1.84 +    ok: function(value) {
    1.85 +      if ( value === undefined ) { return true; }
    1.86 +      try { JSON.parse( JSON.stringify( value ) ); } catch(e) { return false; }
    1.87 +      return true;
    1.88 +    },
    1.89 +    map: function(value) 'undefined' === getTypeOf(value) ? null : value,
    1.90 +    msg: 'The contentScriptOptions should be a jsonable value.'
    1.91 +  }
    1.92 +};
    1.93 +exports.validationAttributes = valid;
    1.94 +
    1.95 +/**
    1.96 + * Shortcut function to validate property with validation.
    1.97 + * @param {Object|Number|String} suspect
    1.98 + *    value to validate
    1.99 + * @param {Object} validation
   1.100 + *    validation rule passed to `api-utils`
   1.101 + */
   1.102 +function validate(suspect, validation) validateOptions(
   1.103 +  { $: suspect },
   1.104 +  { $: validation }
   1.105 +).$
   1.106 +
   1.107 +function Allow(script) ({
   1.108 +  get script() script,
   1.109 +  set script(value) script = !!value
   1.110 +})
   1.111 +
   1.112 +/**
   1.113 + * Trait is intended to be used in some composition. It provides set of core
   1.114 + * properties and bounded validations to them. Trait is useful for all the
   1.115 + * compositions providing high level APIs for interaction with content.
   1.116 + * Property changes emit `"propertyChange"` events on instances.
   1.117 + */
   1.118 +const Loader = EventEmitter.compose({
   1.119 +  /**
   1.120 +   * Permissions for the content, with the following keys:
   1.121 +   * @property {Object} [allow = { script: true }]
   1.122 +   * @property {Boolean} [allow.script = true]
   1.123 +   *    Whether or not to execute script in the content.  Defaults to true.
   1.124 +   */
   1.125 +  get allow() this._allow || (this._allow = Allow(true)),
   1.126 +  set allow(value) this.allow.script = value && value.script,
   1.127 +  _allow: null,
   1.128 +  /**
   1.129 +   * The content to load. Either a string of HTML or a URL.
   1.130 +   * @type {String}
   1.131 +   */
   1.132 +  get contentURL() this._contentURL,
   1.133 +  set contentURL(value) {
   1.134 +    value = validate(value, valid.contentURL);
   1.135 +    if (this._contentURL != value) {
   1.136 +      this._emit('propertyChange', {
   1.137 +        contentURL: this._contentURL = value
   1.138 +      });
   1.139 +    }
   1.140 +  },
   1.141 +  _contentURL: null,
   1.142 +  /**
   1.143 +   * When to load the content scripts.
   1.144 +   * Possible values are "end" (default), which loads them once all page
   1.145 +   * contents have been loaded, "ready", which loads them once DOM nodes are
   1.146 +   * ready (ie like DOMContentLoaded event), and "start", which loads them once
   1.147 +   * the `window` object for the page has been created, but before any scripts
   1.148 +   * specified by the page have been loaded.
   1.149 +   * Property change emits `propertyChange` event on instance with this key
   1.150 +   * and new value.
   1.151 +   * @type {'start'|'ready'|'end'}
   1.152 +   */
   1.153 +  get contentScriptWhen() this._contentScriptWhen,
   1.154 +  set contentScriptWhen(value) {
   1.155 +    value = validate(value, valid.contentScriptWhen);
   1.156 +    if (value !== this._contentScriptWhen) {
   1.157 +      this._emit('propertyChange', {
   1.158 +        contentScriptWhen: this._contentScriptWhen = value
   1.159 +      });
   1.160 +    }
   1.161 +  },
   1.162 +  _contentScriptWhen: 'end',
   1.163 +  /**
   1.164 +   * Options avalaible from the content script as `self.options`.
   1.165 +   * The value of options can be of any type (object, array, string, etc.)
   1.166 +   * but only jsonable values will be available as frozen objects from the
   1.167 +   * content script.
   1.168 +   * Property change emits `propertyChange` event on instance with this key
   1.169 +   * and new value.
   1.170 +   * @type {Object}
   1.171 +   */
   1.172 +  get contentScriptOptions() this._contentScriptOptions,
   1.173 +  set contentScriptOptions(value) this._contentScriptOptions = value,
   1.174 +  _contentScriptOptions: null,
   1.175 +  /**
   1.176 +   * The URLs of content scripts.
   1.177 +   * Property change emits `propertyChange` event on instance with this key
   1.178 +   * and new value.
   1.179 +   * @type {String[]}
   1.180 +   */
   1.181 +  get contentScriptFile() this._contentScriptFile,
   1.182 +  set contentScriptFile(value) {
   1.183 +    value = validate(value, valid.contentScriptFile);
   1.184 +    if (value != this._contentScriptFile) {
   1.185 +      this._emit('propertyChange', {
   1.186 +        contentScriptFile: this._contentScriptFile = value
   1.187 +      });
   1.188 +    }
   1.189 +  },
   1.190 +  _contentScriptFile: null,
   1.191 +  /**
   1.192 +   * The texts of content script.
   1.193 +   * Property change emits `propertyChange` event on instance with this key
   1.194 +   * and new value.
   1.195 +   * @type {String|undefined}
   1.196 +   */
   1.197 +  get contentScript() this._contentScript,
   1.198 +  set contentScript(value) {
   1.199 +    value = validate(value, valid.contentScript);
   1.200 +    if (value != this._contentScript) {
   1.201 +      this._emit('propertyChange', {
   1.202 +        contentScript: this._contentScript = value
   1.203 +      });
   1.204 +    }
   1.205 +  },
   1.206 +  _contentScript: null
   1.207 +});
   1.208 +exports.Loader = Loader;
   1.209 +
   1.210 +exports.contract = contract(valid);

mercurial