tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

animation.js (7017B)


      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 
      5 "use strict";
      6 
      7 const {
      8  FrontClassWithSpec,
      9  registerFront,
     10 } = require("resource://devtools/shared/protocol.js");
     11 const {
     12  animationPlayerSpec,
     13  animationsSpec,
     14 } = require("resource://devtools/shared/specs/animation.js");
     15 
     16 class AnimationPlayerFront extends FrontClassWithSpec(animationPlayerSpec) {
     17  constructor(conn, targetFront, parentFront) {
     18    super(conn, targetFront, parentFront);
     19 
     20    this.state = {};
     21    this.before("changed", this.onChanged.bind(this));
     22  }
     23 
     24  form(form) {
     25    this._form = form;
     26    this.state = this.initialState;
     27  }
     28 
     29  /**
     30   * If the AnimationsActor was given a reference to the WalkerActor previously
     31   * then calling this getter will return the animation target NodeFront.
     32   */
     33  get animationTargetNodeFront() {
     34    if (!this._form.animationTargetNodeActorID) {
     35      return null;
     36    }
     37 
     38    return this.conn.getFrontByID(this._form.animationTargetNodeActorID);
     39  }
     40 
     41  /**
     42   * Getter for the initial state of the player. Up to date states can be
     43   * retrieved by calling the getCurrentState method.
     44   */
     45  get initialState() {
     46    return {
     47      type: this._form.type,
     48      startTime: this._form.startTime,
     49      currentTime: this._form.currentTime,
     50      playState: this._form.playState,
     51      playbackRate: this._form.playbackRate,
     52      name: this._form.name,
     53      duration: this._form.duration,
     54      delay: this._form.delay,
     55      endDelay: this._form.endDelay,
     56      iterationCount: this._form.iterationCount,
     57      iterationStart: this._form.iterationStart,
     58      easing: this._form.easing,
     59      fill: this._form.fill,
     60      direction: this._form.direction,
     61      animationTimingFunction: this._form.animationTimingFunction,
     62      isRunningOnCompositor: this._form.isRunningOnCompositor,
     63      propertyState: this._form.propertyState,
     64      documentCurrentTime: this._form.documentCurrentTime,
     65      createdTime: this._form.createdTime,
     66      currentTimeAtCreated: this._form.currentTimeAtCreated,
     67      absoluteValues: this.calculateAbsoluteValues(this._form),
     68      properties: this._form.properties,
     69    };
     70  }
     71 
     72  /**
     73   * Executed when the AnimationPlayerActor emits a "changed" event. Used to
     74   * update the local knowledge of the state.
     75   */
     76  onChanged(partialState) {
     77    const { state } = this.reconstructState(partialState);
     78    this.state = state;
     79  }
     80 
     81  /**
     82   * Refresh the current state of this animation on the client from information
     83   * found on the server. Doesn't return anything, just stores the new state.
     84   */
     85  async refreshState() {
     86    const data = await this.getCurrentState();
     87    if (this.currentStateHasChanged) {
     88      this.state = data;
     89    }
     90  }
     91 
     92  /**
     93   * getCurrentState interceptor re-constructs incomplete states since the actor
     94   * only sends the values that have changed.
     95   */
     96  getCurrentState() {
     97    this.currentStateHasChanged = false;
     98    return super.getCurrentState().then(partialData => {
     99      const { state, hasChanged } = this.reconstructState(partialData);
    100      this.currentStateHasChanged = hasChanged;
    101      return state;
    102    });
    103  }
    104 
    105  reconstructState(data) {
    106    let hasChanged = false;
    107 
    108    for (const key in this.state) {
    109      if (typeof data[key] === "undefined") {
    110        data[key] = this.state[key];
    111      } else if (data[key] !== this.state[key]) {
    112        hasChanged = true;
    113      }
    114    }
    115 
    116    data.absoluteValues = this.calculateAbsoluteValues(data);
    117    return { state: data, hasChanged };
    118  }
    119 
    120  calculateAbsoluteValues(data) {
    121    const {
    122      createdTime,
    123      currentTime,
    124      currentTimeAtCreated,
    125      delay,
    126      duration,
    127      endDelay = 0,
    128      fill,
    129      iterationCount,
    130      playbackRate,
    131    } = data;
    132 
    133    const toRate = v => v / Math.abs(playbackRate);
    134    const isPositivePlaybackRate = playbackRate > 0;
    135    let absoluteDelay = 0;
    136    let absoluteEndDelay = 0;
    137    let isDelayFilled = false;
    138    let isEndDelayFilled = false;
    139 
    140    if (isPositivePlaybackRate) {
    141      absoluteDelay = toRate(delay);
    142      absoluteEndDelay = toRate(endDelay);
    143      isDelayFilled = fill === "both" || fill === "backwards";
    144      isEndDelayFilled = fill === "both" || fill === "forwards";
    145    } else {
    146      absoluteDelay = toRate(endDelay);
    147      absoluteEndDelay = toRate(delay);
    148      isDelayFilled = fill === "both" || fill === "forwards";
    149      isEndDelayFilled = fill === "both" || fill === "backwards";
    150    }
    151 
    152    let endTime = 0;
    153 
    154    if (duration === Infinity) {
    155      // Set endTime so as to enable the scrubber with keeping the consinstency of UI
    156      // even the duration was Infinity. In case of delay is longer than zero, handle
    157      // the graph duration as double of the delay amount. In case of no delay, handle
    158      // the duration as 1ms which is short enough so as to make the scrubber movable
    159      // and the limited duration is prioritized.
    160      endTime = absoluteDelay > 0 ? absoluteDelay * 2 : 1;
    161    } else {
    162      endTime =
    163        absoluteDelay +
    164        toRate(duration * (iterationCount || 1)) +
    165        absoluteEndDelay;
    166    }
    167 
    168    const absoluteCreatedTime = isPositivePlaybackRate
    169      ? createdTime
    170      : createdTime - endTime;
    171    const absoluteCurrentTimeAtCreated = isPositivePlaybackRate
    172      ? currentTimeAtCreated
    173      : endTime - currentTimeAtCreated;
    174    const animationCurrentTime = isPositivePlaybackRate
    175      ? currentTime
    176      : endTime - currentTime;
    177    const absoluteCurrentTime =
    178      absoluteCreatedTime + toRate(animationCurrentTime);
    179    const absoluteStartTime = absoluteCreatedTime + Math.min(absoluteDelay, 0);
    180    const absoluteStartTimeAtCreated =
    181      absoluteCreatedTime + absoluteCurrentTimeAtCreated;
    182    // To show whole graph with endDelay, we add negative endDelay amount to endTime.
    183    const endTimeWithNegativeEndDelay = endTime - Math.min(absoluteEndDelay, 0);
    184    const absoluteEndTime = absoluteCreatedTime + endTimeWithNegativeEndDelay;
    185 
    186    return {
    187      createdTime: absoluteCreatedTime,
    188      currentTime: absoluteCurrentTime,
    189      currentTimeAtCreated: absoluteCurrentTimeAtCreated,
    190      delay: absoluteDelay,
    191      endDelay: absoluteEndDelay,
    192      endTime: absoluteEndTime,
    193      isDelayFilled,
    194      isEndDelayFilled,
    195      startTime: absoluteStartTime,
    196      startTimeAtCreated: absoluteStartTimeAtCreated,
    197    };
    198  }
    199 }
    200 
    201 registerFront(AnimationPlayerFront);
    202 
    203 class AnimationsFront extends FrontClassWithSpec(animationsSpec) {
    204  constructor(client, targetFront, parentFront) {
    205    super(client, targetFront, parentFront);
    206 
    207    // Attribute name from which to retrieve the actorID out of the target actor's form
    208    this.formAttributeName = "animationsActor";
    209  }
    210 
    211  setWalkerActor(walkerFront) {
    212    this.walker = walkerFront;
    213    return super.setWalkerActor(walkerFront);
    214  }
    215 
    216  destroy() {
    217    super.destroy();
    218    this.walker = null;
    219  }
    220 }
    221 
    222 registerFront(AnimationsFront);