tor-browser

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

MediaFragmentURIParser.cpp (9004B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "MediaFragmentURIParser.h"
      8 
      9 #include <utility>
     10 
     11 #include "nsCharSeparatedTokenizer.h"
     12 #include "nsEscape.h"
     13 #include "nsIURI.h"
     14 #include "nsTArray.h"
     15 
     16 using std::make_pair;
     17 using std::pair;
     18 
     19 namespace mozilla {
     20 
     21 MediaFragmentURIParser::MediaFragmentURIParser(nsIURI* aURI)
     22    : mClipUnit(eClipUnit_Pixel) {
     23  nsAutoCString ref;
     24  aURI->GetRef(ref);
     25  Parse(ref);
     26 }
     27 
     28 MediaFragmentURIParser::MediaFragmentURIParser(nsCString& aRef)
     29    : mClipUnit(eClipUnit_Pixel) {
     30  Parse(aRef);
     31 }
     32 
     33 bool MediaFragmentURIParser::ParseNPT(nsDependentSubstring aString) {
     34  nsDependentSubstring original(aString);
     35  if (aString.Length() > 4 && aString[0] == 'n' && aString[1] == 'p' &&
     36      aString[2] == 't' && aString[3] == ':') {
     37    aString.Rebind(aString, 4);
     38  }
     39 
     40  if (aString.Length() == 0) {
     41    return false;
     42  }
     43 
     44  double start = -1.0;
     45  double end = -1.0;
     46 
     47  ParseNPTTime(aString, start);
     48 
     49  if (aString.Length() == 0) {
     50    mStart.emplace(start);
     51    return true;
     52  }
     53 
     54  if (aString[0] != ',') {
     55    aString.Rebind(original, 0);
     56    return false;
     57  }
     58 
     59  aString.Rebind(aString, 1);
     60 
     61  if (aString.Length() == 0) {
     62    aString.Rebind(original, 0);
     63    return false;
     64  }
     65 
     66  ParseNPTTime(aString, end);
     67 
     68  if (end <= start || aString.Length() != 0) {
     69    aString.Rebind(original, 0);
     70    return false;
     71  }
     72 
     73  mStart.emplace(start);
     74  mEnd.emplace(end);
     75  return true;
     76 }
     77 
     78 bool MediaFragmentURIParser::ParseNPTTime(nsDependentSubstring& aString,
     79                                          double& aTime) {
     80  if (aString.Length() == 0) {
     81    return false;
     82  }
     83 
     84  return ParseNPTHHMMSS(aString, aTime) || ParseNPTMMSS(aString, aTime) ||
     85         ParseNPTSec(aString, aTime);
     86 }
     87 
     88 // Return true if the given character is a numeric character
     89 static bool IsDigit(nsDependentSubstring::char_type aChar) {
     90  return (aChar >= '0' && aChar <= '9');
     91 }
     92 
     93 // Return the index of the first character in the string that is not
     94 // a numerical digit, starting from 'aStart'.
     95 static uint32_t FirstNonDigit(nsDependentSubstring& aString, uint32_t aStart) {
     96  while (aStart < aString.Length() && IsDigit(aString[aStart])) {
     97    ++aStart;
     98  }
     99  return aStart;
    100 }
    101 
    102 bool MediaFragmentURIParser::ParseNPTSec(nsDependentSubstring& aString,
    103                                         double& aSec) {
    104  nsDependentSubstring original(aString);
    105  if (aString.Length() == 0) {
    106    return false;
    107  }
    108 
    109  uint32_t index = FirstNonDigit(aString, 0);
    110  if (index == 0) {
    111    return false;
    112  }
    113 
    114  nsDependentSubstring n(aString, 0, index);
    115  nsresult ec;
    116  int32_t s = n.ToInteger(&ec);
    117  if (NS_FAILED(ec)) {
    118    return false;
    119  }
    120 
    121  aString.Rebind(aString, index);
    122  double fraction = 0.0;
    123  if (!ParseNPTFraction(aString, fraction)) {
    124    aString.Rebind(original, 0);
    125    return false;
    126  }
    127 
    128  aSec = s + fraction;
    129  return true;
    130 }
    131 
    132 bool MediaFragmentURIParser::ParseNPTMMSS(nsDependentSubstring& aString,
    133                                          double& aTime) {
    134  nsDependentSubstring original(aString);
    135  uint32_t mm = 0;
    136  uint32_t ss = 0;
    137  double fraction = 0.0;
    138  if (!ParseNPTMM(aString, mm)) {
    139    aString.Rebind(original, 0);
    140    return false;
    141  }
    142 
    143  if (aString.Length() < 2 || aString[0] != ':') {
    144    aString.Rebind(original, 0);
    145    return false;
    146  }
    147 
    148  aString.Rebind(aString, 1);
    149  if (!ParseNPTSS(aString, ss)) {
    150    aString.Rebind(original, 0);
    151    return false;
    152  }
    153 
    154  if (!ParseNPTFraction(aString, fraction)) {
    155    aString.Rebind(original, 0);
    156    return false;
    157  }
    158  aTime = mm * 60 + ss + fraction;
    159  return true;
    160 }
    161 
    162 bool MediaFragmentURIParser::ParseNPTFraction(nsDependentSubstring& aString,
    163                                              double& aFraction) {
    164  double fraction = 0.0;
    165 
    166  if (aString.Length() > 0 && aString[0] == '.') {
    167    uint32_t index = FirstNonDigit(aString, 1);
    168 
    169    if (index > 1) {
    170      nsDependentSubstring number(aString, 0, index);
    171      nsresult ec;
    172      fraction = PromiseFlatString(number).ToDouble(&ec);
    173      if (NS_FAILED(ec)) {
    174        return false;
    175      }
    176    }
    177    aString.Rebind(aString, index);
    178  }
    179 
    180  aFraction = fraction;
    181  return true;
    182 }
    183 
    184 bool MediaFragmentURIParser::ParseNPTHHMMSS(nsDependentSubstring& aString,
    185                                            double& aTime) {
    186  nsDependentSubstring original(aString);
    187  uint32_t hh = 0;
    188  double seconds = 0.0;
    189  if (!ParseNPTHH(aString, hh)) {
    190    return false;
    191  }
    192 
    193  if (aString.Length() < 2 || aString[0] != ':') {
    194    aString.Rebind(original, 0);
    195    return false;
    196  }
    197 
    198  aString.Rebind(aString, 1);
    199  if (!ParseNPTMMSS(aString, seconds)) {
    200    aString.Rebind(original, 0);
    201    return false;
    202  }
    203 
    204  aTime = hh * 3600 + seconds;
    205  return true;
    206 }
    207 
    208 bool MediaFragmentURIParser::ParseNPTHH(nsDependentSubstring& aString,
    209                                        uint32_t& aHour) {
    210  if (aString.Length() == 0) {
    211    return false;
    212  }
    213 
    214  uint32_t index = FirstNonDigit(aString, 0);
    215  if (index == 0) {
    216    return false;
    217  }
    218 
    219  nsDependentSubstring n(aString, 0, index);
    220  nsresult ec;
    221  int32_t u = n.ToInteger(&ec);
    222  if (NS_FAILED(ec)) {
    223    return false;
    224  }
    225 
    226  aString.Rebind(aString, index);
    227  aHour = u;
    228  return true;
    229 }
    230 
    231 bool MediaFragmentURIParser::ParseNPTMM(nsDependentSubstring& aString,
    232                                        uint32_t& aMinute) {
    233  return ParseNPTSS(aString, aMinute);
    234 }
    235 
    236 bool MediaFragmentURIParser::ParseNPTSS(nsDependentSubstring& aString,
    237                                        uint32_t& aSecond) {
    238  if (aString.Length() < 2) {
    239    return false;
    240  }
    241 
    242  if (IsDigit(aString[0]) && IsDigit(aString[1])) {
    243    nsDependentSubstring n(aString, 0, 2);
    244    nsresult ec;
    245    int32_t u = n.ToInteger(&ec);
    246    if (NS_FAILED(ec)) {
    247      return false;
    248    }
    249 
    250    aString.Rebind(aString, 2);
    251    if (u >= 60) return false;
    252 
    253    aSecond = u;
    254    return true;
    255  }
    256 
    257  return false;
    258 }
    259 
    260 static bool ParseInteger(nsDependentSubstring& aString, int32_t& aResult) {
    261  uint32_t index = FirstNonDigit(aString, 0);
    262  if (index == 0) {
    263    return false;
    264  }
    265 
    266  nsDependentSubstring n(aString, 0, index);
    267  nsresult ec;
    268  int32_t s = n.ToInteger(&ec);
    269  if (NS_FAILED(ec)) {
    270    return false;
    271  }
    272 
    273  aString.Rebind(aString, index);
    274  aResult = s;
    275  return true;
    276 }
    277 
    278 static bool ParseCommaSeparator(nsDependentSubstring& aString) {
    279  if (aString.Length() > 1 && aString[0] == ',') {
    280    aString.Rebind(aString, 1);
    281    return true;
    282  }
    283 
    284  return false;
    285 }
    286 
    287 bool MediaFragmentURIParser::ParseXYWH(nsDependentSubstring aString) {
    288  int32_t x, y, w, h;
    289  ClipUnit clipUnit;
    290 
    291  // Determine units.
    292  if (StringBeginsWith(aString, u"pixel:"_ns)) {
    293    clipUnit = eClipUnit_Pixel;
    294    aString.Rebind(aString, 6);
    295  } else if (StringBeginsWith(aString, u"percent:"_ns)) {
    296    clipUnit = eClipUnit_Percent;
    297    aString.Rebind(aString, 8);
    298  } else {
    299    clipUnit = eClipUnit_Pixel;
    300  }
    301 
    302  // Read and validate coordinates.
    303  if (ParseInteger(aString, x) && x >= 0 && ParseCommaSeparator(aString) &&
    304      ParseInteger(aString, y) && y >= 0 && ParseCommaSeparator(aString) &&
    305      ParseInteger(aString, w) && w > 0 && ParseCommaSeparator(aString) &&
    306      ParseInteger(aString, h) && h > 0 && aString.Length() == 0) {
    307    // Reject invalid percentage coordinates.
    308    if (clipUnit == eClipUnit_Percent && (x + w > 100 || y + h > 100)) {
    309      return false;
    310    }
    311 
    312    mClip.emplace(x, y, w, h);
    313    mClipUnit = clipUnit;
    314    return true;
    315  }
    316 
    317  return false;
    318 }
    319 
    320 void MediaFragmentURIParser::Parse(nsACString& aRef) {
    321  // Create an array of possibly-invalid media fragments.
    322  nsTArray<std::pair<nsCString, nsCString> > fragments;
    323 
    324  for (const nsACString& nv : nsCCharSeparatedTokenizer(aRef, '&').ToRange()) {
    325    int32_t index = nv.FindChar('=');
    326    if (index >= 0) {
    327      nsAutoCString name;
    328      nsAutoCString value;
    329      NS_UnescapeURL(StringHead(nv, index), esc_Ref | esc_AlwaysCopy, name);
    330      NS_UnescapeURL(Substring(nv, index + 1, nv.Length()),
    331                     esc_Ref | esc_AlwaysCopy, value);
    332      fragments.AppendElement(make_pair(name, value));
    333    }
    334  }
    335 
    336  // Parse the media fragment values.
    337  bool gotTemporal = false, gotSpatial = false;
    338  for (int i = fragments.Length() - 1; i >= 0; --i) {
    339    if (gotTemporal && gotSpatial) {
    340      // We've got one of each possible type. No need to look at the rest.
    341      break;
    342    }
    343    if (!gotTemporal && fragments[i].first.EqualsLiteral("t")) {
    344      nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second);
    345      gotTemporal = ParseNPT(nsDependentSubstring(value, 0));
    346    } else if (!gotSpatial && fragments[i].first.EqualsLiteral("xywh")) {
    347      nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second);
    348      gotSpatial = ParseXYWH(nsDependentSubstring(value, 0));
    349    }
    350  }
    351 }
    352 
    353 }  // namespace mozilla