tor-browser

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

GeckoViewMediaControl.sys.mjs (6241B)


      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 file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 import { GeckoViewModule } from "resource://gre/modules/GeckoViewModule.sys.mjs";
      6 
      7 export class GeckoViewMediaControl extends GeckoViewModule {
      8  onInit() {
      9    debug`onInit`;
     10  }
     11 
     12  onInitBrowser() {
     13    debug`onInitBrowser`;
     14 
     15    const options = {
     16      mozSystemGroup: true,
     17      capture: false,
     18    };
     19 
     20    this.controller.addEventListener("activated", this, options);
     21    this.controller.addEventListener("deactivated", this, options);
     22    this.controller.addEventListener("supportedkeyschange", this, options);
     23    this.controller.addEventListener("positionstatechange", this, options);
     24    this.controller.addEventListener("metadatachange", this, options);
     25    this.controller.addEventListener("playbackstatechange", this, options);
     26  }
     27 
     28  onDestroyBrowser() {
     29    debug`onDestroyBrowser`;
     30 
     31    this.controller.removeEventListener("activated", this);
     32    this.controller.removeEventListener("deactivated", this);
     33    this.controller.removeEventListener("supportedkeyschange", this);
     34    this.controller.removeEventListener("positionstatechange", this);
     35    this.controller.removeEventListener("metadatachange", this);
     36    this.controller.removeEventListener("playbackstatechange", this);
     37  }
     38 
     39  onEnable() {
     40    debug`onEnable`;
     41 
     42    if (this.controller.isActive) {
     43      this.handleActivated();
     44    }
     45 
     46    this.registerListener([
     47      "GeckoView:MediaSession:Play",
     48      "GeckoView:MediaSession:Pause",
     49      "GeckoView:MediaSession:Stop",
     50      "GeckoView:MediaSession:NextTrack",
     51      "GeckoView:MediaSession:PrevTrack",
     52      "GeckoView:MediaSession:SeekForward",
     53      "GeckoView:MediaSession:SeekBackward",
     54      "GeckoView:MediaSession:SkipAd",
     55      "GeckoView:MediaSession:SeekTo",
     56      "GeckoView:MediaSession:MuteAudio",
     57    ]);
     58  }
     59 
     60  onDisable() {
     61    debug`onDisable`;
     62 
     63    this.unregisterListener();
     64  }
     65 
     66  get controller() {
     67    return this.browser.browsingContext.mediaController;
     68  }
     69 
     70  onEvent(aEvent, aData) {
     71    debug`onEvent: event=${aEvent}, data=${aData}`;
     72 
     73    switch (aEvent) {
     74      case "GeckoView:MediaSession:Play":
     75        this.controller.play();
     76        break;
     77      case "GeckoView:MediaSession:Pause":
     78        this.controller.pause();
     79        break;
     80      case "GeckoView:MediaSession:Stop":
     81        this.controller.stop();
     82        this.eventDispatcher.sendRequest({
     83          type: "GeckoView:MediaSession:Playback:None",
     84        });
     85        break;
     86      case "GeckoView:MediaSession:NextTrack":
     87        this.controller.nextTrack();
     88        break;
     89      case "GeckoView:MediaSession:PrevTrack":
     90        this.controller.prevTrack();
     91        break;
     92      case "GeckoView:MediaSession:SeekForward":
     93        this.controller.seekForward();
     94        break;
     95      case "GeckoView:MediaSession:SeekBackward":
     96        this.controller.seekBackward();
     97        break;
     98      case "GeckoView:MediaSession:SkipAd":
     99        this.controller.skipAd();
    100        break;
    101      case "GeckoView:MediaSession:SeekTo":
    102        this.controller.seekTo(aData.time, aData.fast);
    103        break;
    104      case "GeckoView:MediaSession:MuteAudio":
    105        if (aData.mute) {
    106          this.browser.mute();
    107        } else {
    108          this.browser.unmute();
    109        }
    110        break;
    111    }
    112  }
    113 
    114  // eslint-disable-next-line complexity
    115  handleEvent(aEvent) {
    116    debug`handleEvent: ${aEvent.type}`;
    117 
    118    switch (aEvent.type) {
    119      case "activated":
    120        this.handleActivated();
    121        break;
    122      case "deactivated":
    123        this.handleDeactivated();
    124        break;
    125      case "supportedkeyschange":
    126        this.handleSupportedKeysChanged();
    127        break;
    128      case "positionstatechange":
    129        this.handlePositionStateChanged(aEvent);
    130        break;
    131      case "metadatachange":
    132        this.handleMetadataChanged();
    133        break;
    134      case "playbackstatechange":
    135        this.handlePlaybackStateChanged();
    136        break;
    137      default:
    138        warn`Unknown event type ${aEvent.type}`;
    139        break;
    140    }
    141  }
    142 
    143  handleActivated() {
    144    debug`handleActivated`;
    145 
    146    this.eventDispatcher.sendRequest({
    147      type: "GeckoView:MediaSession:Activated",
    148    });
    149  }
    150 
    151  handleDeactivated() {
    152    debug`handleDeactivated`;
    153 
    154    this.eventDispatcher.sendRequest({
    155      type: "GeckoView:MediaSession:Deactivated",
    156    });
    157  }
    158 
    159  handlePositionStateChanged(aEvent) {
    160    debug`handlePositionStateChanged`;
    161 
    162    this.eventDispatcher.sendRequest({
    163      type: "GeckoView:MediaSession:PositionState",
    164      state: {
    165        duration: aEvent.duration,
    166        playbackRate: aEvent.playbackRate,
    167        position: aEvent.position,
    168      },
    169    });
    170  }
    171 
    172  handleSupportedKeysChanged() {
    173    const supported = this.controller.supportedKeys;
    174 
    175    debug`handleSupportedKeysChanged ${supported}`;
    176 
    177    // Mapping it to a key-value store for compatibility with the JNI
    178    // implementation for now.
    179    const features = new Map();
    180    supported.forEach(key => {
    181      features[key] = true;
    182    });
    183 
    184    this.eventDispatcher.sendRequest({
    185      type: "GeckoView:MediaSession:Features",
    186      features,
    187    });
    188  }
    189 
    190  handleMetadataChanged() {
    191    let metadata = null;
    192    try {
    193      metadata = this.controller.getMetadata();
    194    } catch (e) {
    195      warn`Metadata not available`;
    196    }
    197    debug`handleMetadataChanged ${metadata}`;
    198 
    199    if (metadata) {
    200      this.eventDispatcher.sendRequest({
    201        type: "GeckoView:MediaSession:Metadata",
    202        metadata,
    203      });
    204    }
    205  }
    206 
    207  handlePlaybackStateChanged() {
    208    const state = this.controller.playbackState;
    209    let type = null;
    210 
    211    debug`handlePlaybackStateChanged ${state}`;
    212 
    213    switch (state) {
    214      case "none":
    215        type = "GeckoView:MediaSession:Playback:None";
    216        break;
    217      case "paused":
    218        type = "GeckoView:MediaSession:Playback:Paused";
    219        break;
    220      case "playing":
    221        type = "GeckoView:MediaSession:Playback:Playing";
    222        break;
    223    }
    224 
    225    if (!type) {
    226      return;
    227    }
    228 
    229    this.eventDispatcher.sendRequest({
    230      type,
    231    });
    232  }
    233 }
    234 
    235 const { debug, warn } = GeckoViewMediaControl.initLogging(
    236  "GeckoViewMediaControl"
    237 );