Richard Pospesel pushed to branch maint-12.0 at The Tor Project / Applications / torbutton

Commits:

1 changed file:

Changes:

  • components/dragDropFilter.js
    ... ... @@ -13,6 +13,7 @@ const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
    13 13
     XPCOMUtils.defineLazyModuleGetters(this, {
    
    14 14
       ComponentUtils: "resource://gre/modules/ComponentUtils.jsm",
    
    15 15
     });
    
    16
    +XPCOMUtils.defineLazyGlobalGetters(this, ["crypto"]);
    
    16 17
     
    
    17 18
     // Module specific constants
    
    18 19
     const kMODULE_NAME = "Torbutton Drag and Drop Handler";
    
    ... ... @@ -28,50 +29,55 @@ const URLISH_TYPES = Object.freeze([
    28 29
       "application/x-moz-file-promise-url",
    
    29 30
     ]);
    
    30 31
     
    
    31
    -/*
    
    32
    -  Returns true if the text resembles a URL or even just a hostname
    
    33
    -  in a way that may prompt the O.S. or other applications to send out a
    
    34
    -  validation DNS query, if not a full request (e.g. " torproject.org",
    
    35
    -  even with the leading whitespace).
    
    36
    -*/
    
    37
    -function isURLish(text) {
    
    38
    -  // Ignore leading whitespace.
    
    39
    -  text = text.trim();
    
    40
    -
    
    41
    -  // Without any protocol or dot in the first chunk, this is unlikely
    
    42
    -  // to be considered URLish (exception: localhost, but we don't care).
    
    43
    -  if (!/^[a-z][a-z0-9+-]*:\/\//i.test(text)) {
    
    44
    -    // no protocol
    
    45
    -    if (!/^[^.\s\/]+\.[^.\s\/]/.test(text)) {
    
    46
    -      // no dot
    
    47
    -      return false;
    
    32
    +const MAIN_PROCESS =
    
    33
    +  Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
    
    34
    +
    
    35
    +const EMPTY_PAYLOAD = {};
    
    36
    +const OpaqueDrag = {
    
    37
    +  listening: false,
    
    38
    +  payload: EMPTY_PAYLOAD,
    
    39
    +  store(value, type) {
    
    40
    +    let opaqueKey = crypto.randomUUID();
    
    41
    +    this.payload = { opaqueKey, value, type };
    
    42
    +    if (!this.listening && MAIN_PROCESS) {
    
    43
    +      Services.ppmm.addMessageListener(
    
    44
    +        "DragDropFilter:GetOpaqueDrag",
    
    45
    +        () => this.payload
    
    46
    +      );
    
    47
    +      this.listening = true;
    
    48 48
         }
    
    49
    -    // Prepare for hostname validation via relative URL building.
    
    50
    -    text = `//${text}`;
    
    51
    -  }
    
    52
    -  // Validate URL or hostname.
    
    53
    -  try {
    
    54
    -    new URL(text, "https://localhost");
    
    55
    -    return true;
    
    56
    -  } catch (e) {
    
    57
    -    // invalid URL, bail out
    
    58
    -  }
    
    59
    -  return false;
    
    60
    -}
    
    61
    -
    
    62
    -// Returns true if any chunk of text is URLish
    
    63
    -const hasURLish = text => text.split(/[^\p{L}_.-:\/%~@$-]+/u).some(isURLish);
    
    49
    +    return opaqueKey;
    
    50
    +  },
    
    51
    +  retrieve(key) {
    
    52
    +    let { opaqueKey, value, type } = this.payload;
    
    53
    +    if (opaqueKey === key) {
    
    54
    +      return { value, type };
    
    55
    +    }
    
    56
    +    if (!MAIN_PROCESS) {
    
    57
    +      this.payload = Services.cpmm.sendSyncMessage(
    
    58
    +        "DragDropFilter:GetOpaqueDrag"
    
    59
    +      )[0];
    
    60
    +      if (key === this.payload.opaqueKey) {
    
    61
    +        return this.retrieve(key);
    
    62
    +      }
    
    63
    +    }
    
    64
    +    return EMPTY_PAYLOAD;
    
    65
    +  },
    
    66
    +};
    
    64 67
     
    
    65 68
     function DragDropFilter() {
    
    66 69
       this.logger = Cc["@torproject.org/torbutton-logger;1"].getService(
    
    67 70
         Ci.nsISupports
    
    68 71
       ).wrappedJSObject;
    
    69 72
       this.logger.log(3, "Component Load 0: New DragDropFilter.");
    
    70
    -
    
    71
    -  try {
    
    72
    -    Services.obs.addObserver(this, "on-datatransfer-available");
    
    73
    -  } catch (e) {
    
    74
    -    this.logger.log(5, "Failed to register drag observer");
    
    73
    +  if (MAIN_PROCESS) {
    
    74
    +    // We want to update our status in the main process only, in order to
    
    75
    +    // serve the same opaque drag payload in every process.
    
    76
    +    try {
    
    77
    +      Services.obs.addObserver(this, "on-datatransfer-available");
    
    78
    +    } catch (e) {
    
    79
    +      this.logger.log(5, "Failed to register drag observer");
    
    80
    +    }
    
    75 81
       }
    
    76 82
     }
    
    77 83
     
    
    ... ... @@ -109,23 +115,38 @@ DragDropFilter.prototype = {
    109 115
           const types = aDataTransfer.mozTypesAt(i);
    
    110 116
           for (const type of types) {
    
    111 117
             this.logger.log(3, `Type is: ${type}.`);
    
    112
    -        if (
    
    113
    -          URLISH_TYPES.includes(type) ||
    
    114
    -          ((type === "text/plain" || type === "text/html") &&
    
    115
    -            hasURLish(aDataTransfer.getData(type)))
    
    116
    -        ) {
    
    118
    +        if (URLISH_TYPES.includes(type)) {
    
    117 119
               this.logger.log(
    
    118 120
                 3,
    
    119
    -            `Removing transfer data ${aDataTransfer.getData(type)}`
    
    121
    +            `Removing transfer data ${aDataTransfer.mozGetDataAt(type, i)}`
    
    120 122
               );
    
    123
    +          const urlType = "text/x-moz-url";
    
    124
    +          // Fallback url type, to be parsed by this browser but not externally
    
    125
    +          const INTERNAL_FALLBACK = "application/x-torbrowser-opaque";
    
    126
    +          if (types.contains(urlType)) {
    
    127
    +            const link = aDataTransfer.mozGetDataAt(urlType, i);
    
    128
    +            const opaqueKey = OpaqueDrag.store(link, urlType);
    
    129
    +            aDataTransfer.mozSetDataAt(INTERNAL_FALLBACK, opaqueKey, i);
    
    130
    +          }
    
    121 131
               for (const type of types) {
    
    122
    -            aDataTransfer.clearData(type);
    
    132
    +            if (
    
    133
    +              type !== INTERNAL_FALLBACK &&
    
    134
    +              type !== "text/x-moz-place" // don't touch bookmarks
    
    135
    +            ) {
    
    136
    +              aDataTransfer.mozClearDataAt(type, i);
    
    137
    +            }
    
    123 138
               }
    
    124 139
               break;
    
    125 140
             }
    
    126 141
           }
    
    127 142
         }
    
    128 143
       },
    
    144
    +
    
    145
    +  opaqueDrag: {
    
    146
    +    get(opaqueKey) {
    
    147
    +      return OpaqueDrag.retrieve(opaqueKey);
    
    148
    +    },
    
    149
    +  },
    
    129 150
     };
    
    130 151
     
    
    131 152
     // Assign factory to global object.