browser/devtools/shared/observable-object.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     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/. */
     5 /**
     6  * ObservableObject
     7  *
     8  * An observable object is a JSON-like object that throws
     9  * events when its direct properties or properties of any
    10  * contained objects, are getting accessed or set.
    11  *
    12  * Inherits from EventEmitter.
    13  *
    14  * Properties:
    15  * ⬩ object: JSON-like object
    16  *
    17  * Events:
    18  * ⬩ "get" / path (array of property names)
    19  * ⬩ "set" / path / new value
    20  *
    21  * Example:
    22  *
    23  *   let emitter = new ObservableObject({ x: { y: [10] } });
    24  *   emitter.on("set", console.log);
    25  *   emitter.on("get", console.log);
    26  *   let obj = emitter.object;
    27  *   obj.x.y[0] = 50;
    28  *
    29  */
    31 "use strict";
    33 const EventEmitter = require("devtools/toolkit/event-emitter");
    35 function ObservableObject(object = {}) {
    36   EventEmitter.decorate(this);
    37   let handler = new Handler(this);
    38   this.object = new Proxy(object, handler);
    39   handler._wrappers.set(this.object, object);
    40   handler._paths.set(object, []);
    41 }
    43 module.exports = ObservableObject;
    45 function isObject(x) {
    46   if (typeof x === "object")
    47     return x !== null;
    48   return typeof x === "function";
    49 }
    51 function Handler(emitter) {
    52   this._emitter = emitter;
    53   this._wrappers = new WeakMap();
    54   this._values = new WeakMap();
    55   this._paths = new WeakMap();
    56 }
    58 Handler.prototype = {
    59   wrap: function(target, key, value) {
    60     let path;
    61     if (!isObject(value)) {
    62       path = this._paths.get(target).concat(key);
    63     } else if (this._wrappers.has(value)) {
    64       path = this._paths.get(value);
    65     } else if (this._paths.has(value)) {
    66       path = this._paths.get(value);
    67       value = this._values.get(value);
    68     } else {
    69       path = this._paths.get(target).concat(key);
    70       this._paths.set(value, path);
    71       let wrapper = new Proxy(value, this);
    72       this._wrappers.set(wrapper, value);
    73       this._values.set(value, wrapper);
    74       value = wrapper;
    75     }
    76     return [value, path];
    77   },
    78   unwrap: function(target, key, value) {
    79     if (!isObject(value) || !this._wrappers.has(value)) {
    80       return [value, this._paths.get(target).concat(key)];
    81     }
    82     return [this._wrappers.get(value), this._paths.get(target).concat(key)];
    83   },
    84   get: function(target, key) {
    85     let value = target[key];
    86     let [wrapped, path] = this.wrap(target, key, value);
    87     this._emitter.emit("get", path, value);
    88     return wrapped;
    89   },
    90   set: function(target, key, value) {
    91     let [wrapped, path] = this.unwrap(target, key, value);
    92     target[key] = value;
    93     this._emitter.emit("set", path, value);
    94   },
    95   getOwnPropertyDescriptor: function(target, key) {
    96     let desc = Object.getOwnPropertyDescriptor(target, key);
    97     if (desc) {
    98       if ("value" in desc) {
    99         let [wrapped, path] = this.wrap(target, key, desc.value);
   100         desc.value = wrapped;
   101         this._emitter.emit("get", path, desc.value);
   102       } else {
   103         if ("get" in desc) {
   104           [desc.get] = this.wrap(target, "get "+key, desc.get);
   105         }
   106         if ("set" in desc) {
   107           [desc.set] = this.wrap(target, "set "+key, desc.set);
   108         }
   109       }
   110     }
   111     return desc;
   112   },
   113   defineProperty: function(target, key, desc) {
   114     if ("value" in desc) {
   115       let [unwrapped, path] = this.unwrap(target, key, desc.value);
   116       desc.value = unwrapped;
   117       Object.defineProperty(target, key, desc);
   118       this._emitter.emit("set", path, desc.value);
   119     } else {
   120       if ("get" in desc) {
   121         [desc.get] = this.unwrap(target, "get "+key, desc.get);
   122       }
   123       if ("set" in desc) {
   124         [desc.set] = this.unwrap(target, "set "+key, desc.set);
   125       }
   126       Object.defineProperty(target, key, desc);
   127     }
   128   }
   129 };

mercurial