addon-sdk/source/lib/sdk/ui/state.js

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     4 'use strict';
     6 // The Button module currently supports only Firefox.
     7 // See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps
     8 module.metadata = {
     9   'stability': 'experimental',
    10   'engines': {
    11     'Firefox': '*'
    12   }
    13 };
    15 const { Ci } = require('chrome');
    17 const events = require('../event/utils');
    18 const { events: browserEvents } = require('../browser/events');
    19 const { events: tabEvents } = require('../tab/events');
    20 const { events: stateEvents } = require('./state/events');
    22 const { windows, isInteractive, getFocusedBrowser } = require('../window/utils');
    23 const { getActiveTab, getOwnerWindow } = require('../tabs/utils');
    25 const { ignoreWindow } = require('../private-browsing/utils');
    27 const { freeze } = Object;
    28 const { merge } = require('../util/object');
    29 const { on, off, emit } = require('../event/core');
    31 const { add, remove, has, clear, iterator } = require('../lang/weak-set');
    32 const { isNil } = require('../lang/type');
    34 const { viewFor } = require('../view/core');
    36 const components = new WeakMap();
    38 const ERR_UNREGISTERED = 'The state cannot be set or get. ' +
    39   'The object may be not be registered, or may already have been unloaded.';
    41 const ERR_INVALID_TARGET = 'The state cannot be set or get for this target.' +
    42   'Only window, tab and registered component are valid targets.';
    44 const isWindow = thing => thing instanceof Ci.nsIDOMWindow;
    45 const isTab = thing => thing.tagName && thing.tagName.toLowerCase() === 'tab';
    46 const isActiveTab = thing => isTab(thing) && thing === getActiveTab(getOwnerWindow(thing));
    47 const isEnumerable = window => !ignoreWindow(window);
    48 const browsers = _ =>
    49   windows('navigator:browser', { includePrivate: true }).filter(isInteractive);
    50 const getMostRecentTab = _ => getActiveTab(getFocusedBrowser());
    52 function getStateFor(component, target) {
    53   if (!isRegistered(component))
    54     throw new Error(ERR_UNREGISTERED);
    56   if (!components.has(component))
    57     return null;
    59   let states = components.get(component);
    61   if (target) {
    62     if (isTab(target) || isWindow(target) || target === component)
    63       return states.get(target) || null;
    64     else
    65       throw new Error(ERR_INVALID_TARGET);
    66   }
    68   return null;
    69 }
    70 exports.getStateFor = getStateFor;
    72 function getDerivedStateFor(component, target) {
    73   if (!isRegistered(component))
    74     throw new Error(ERR_UNREGISTERED);
    76   if (!components.has(component))
    77     return null;
    79   let states = components.get(component);
    81   let componentState = states.get(component);
    82   let windowState = null;
    83   let tabState = null;
    85   if (target) {
    86     // has a target
    87     if (isTab(target)) {
    88       windowState = states.get(getOwnerWindow(target), null);
    90       if (states.has(target)) {
    91         // we have a tab state
    92         tabState = states.get(target);
    93       }
    94     }
    95     else if (isWindow(target) && states.has(target)) {
    96       // we have a window state
    97       windowState = states.get(target);
    98     }
    99   }
   101   return freeze(merge({}, componentState, windowState, tabState));
   102 }
   103 exports.getDerivedStateFor = getDerivedStateFor;
   105 function setStateFor(component, target, state) {
   106   if (!isRegistered(component))
   107     throw new Error(ERR_UNREGISTERED);
   109   let isComponentState = target === component;
   110   let targetWindows = isWindow(target) ? [target] :
   111                       isActiveTab(target) ? [getOwnerWindow(target)] :
   112                       isComponentState ? browsers() :
   113                       isTab(target) ? [] :
   114                       null;
   116   if (!targetWindows)
   117     throw new Error(ERR_INVALID_TARGET);
   119   // initialize the state's map
   120   if (!components.has(component))
   121     components.set(component, new WeakMap());
   123   let states = components.get(component);
   125   if (state === null && !isComponentState) // component state can't be deleted
   126     states.delete(target);
   127   else {
   128     let base = isComponentState ? states.get(target) : null;
   129     states.set(target, freeze(merge({}, base, state)));
   130   }
   132   render(component, targetWindows);
   133 }
   134 exports.setStateFor = setStateFor;
   136 function render(component, targetWindows) {
   137   targetWindows = targetWindows ? [].concat(targetWindows) : browsers();
   139   for (let window of targetWindows.filter(isEnumerable)) {
   140     let tabState = getDerivedStateFor(component, getActiveTab(window));
   142     emit(stateEvents, 'data', {
   143       type: 'render',
   144       target: component,
   145       window: window,
   146       state: tabState
   147     });
   149   }
   150 }
   151 exports.render = render;
   153 function properties(contract) {
   154   let { rules } = contract;
   155   let descriptor = Object.keys(rules).reduce(function(descriptor, name) {
   156     descriptor[name] = {
   157       get: function() { return getDerivedStateFor(this)[name] },
   158       set: function(value) {
   159         let changed = {};
   160         changed[name] = value;
   162         setStateFor(this, this, contract(changed));
   163       }
   164     }
   165     return descriptor;
   166   }, {});
   168   return Object.create(Object.prototype, descriptor);
   169 }
   170 exports.properties = properties;
   172 function state(contract) {
   173   return {
   174     state: function state(target, state) {
   175       let nativeTarget = target === 'window' ? getFocusedBrowser()
   176                           : target === 'tab' ? getMostRecentTab()
   177                           : target === this ? null
   178                           : viewFor(target);
   180       if (!nativeTarget && target !== this && !isNil(target))
   181         throw new Error(ERR_INVALID_TARGET);
   183       target = nativeTarget || target;
   185       // jquery style
   186       return arguments.length < 2
   187         ? getDerivedStateFor(this, target)
   188         : setStateFor(this, target, contract(state))
   189     }
   190   }
   191 }
   192 exports.state = state;
   194 const register = (component, state) => {
   195   add(components, component);
   196   setStateFor(component, component, state);
   197 }
   198 exports.register = register;
   200 const unregister = component => {
   201   remove(components, component);
   202 }
   203 exports.unregister = unregister;
   205 const isRegistered = component => has(components, component);
   206 exports.isRegistered = isRegistered;
   208 let tabSelect = events.filter(tabEvents, e => e.type === 'TabSelect');
   209 let tabClose = events.filter(tabEvents, e => e.type === 'TabClose');
   210 let windowOpen = events.filter(browserEvents, e => e.type === 'load');
   211 let windowClose = events.filter(browserEvents, e => e.type === 'close');
   213 let close = events.merge([tabClose, windowClose]);
   214 let activate = events.merge([windowOpen, tabSelect]);
   216 on(activate, 'data', ({target}) => {
   217   let [window, tab] = isWindow(target)
   218                         ? [target, getActiveTab(target)]
   219                         : [getOwnerWindow(target), target];
   221   if (ignoreWindow(window)) return;
   223   for (let component of iterator(components)) {
   224     emit(stateEvents, 'data', {
   225       type: 'render',
   226       target: component,
   227       window: window,
   228       state: getDerivedStateFor(component, tab)
   229     });
   230   }
   231 });
   233 on(close, 'data', function({target}) {
   234   for (let component of iterator(components)) {
   235     components.get(component).delete(target);
   236   }
   237 });

mercurial