netwerk/base/src/nsMediaFragmentURIParser.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     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/. */
     7 #include "nsTArray.h"
     8 #include "nsCharSeparatedTokenizer.h"
     9 #include "nsEscape.h"
    10 #include "nsIURI.h"
    11 #include <utility>
    13 #include "nsMediaFragmentURIParser.h"
    15 using std::pair;
    16 using std::make_pair;
    18 namespace mozilla { namespace net {
    20 nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsIURI* aURI)
    21   : mClipUnit(eClipUnit_Pixel)
    22 {
    23   nsAutoCString ref;
    24   aURI->GetRef(ref);
    25   Parse(ref);
    26 }
    28 nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsCString& aRef)
    29   : mClipUnit(eClipUnit_Pixel)
    30 {
    31   Parse(aRef);
    32 }
    34 bool nsMediaFragmentURIParser::ParseNPT(nsDependentSubstring aString)
    35 {
    36   nsDependentSubstring original(aString);
    37   if (aString.Length() > 4 &&
    38       aString[0] == 'n' && aString[1] == 'p' &&
    39       aString[2] == 't' && aString[3] == ':') {
    40     aString.Rebind(aString, 4);
    41   }
    43   if (aString.Length() == 0) {
    44     return false;
    45   }
    47   double start = -1.0;
    48   double end = -1.0;
    50   ParseNPTTime(aString, start);
    52   if (aString.Length() == 0) {
    53     mStart.construct(start);
    54     return true;
    55   }
    57   if (aString[0] != ',') {
    58     aString.Rebind(original, 0);
    59     return false;
    60   }
    62   aString.Rebind(aString, 1);
    64   if (aString.Length() == 0) {
    65     aString.Rebind(original, 0);
    66     return false;
    67   }
    69   ParseNPTTime(aString, end);
    71   if (end <= start || aString.Length() != 0) {
    72     aString.Rebind(original, 0);
    73     return false;
    74   }
    76   mStart.construct(start);
    77   mEnd.construct(end);
    78   return true;
    79 }
    81 bool nsMediaFragmentURIParser::ParseNPTTime(nsDependentSubstring& aString, double& aTime)
    82 {
    83   if (aString.Length() == 0) {
    84     return false;
    85   }
    87   return
    88     ParseNPTHHMMSS(aString, aTime) ||
    89     ParseNPTMMSS(aString, aTime) ||
    90     ParseNPTSec(aString, aTime);
    91 }
    93 // Return true if the given character is a numeric character
    94 static bool IsDigit(nsDependentSubstring::char_type aChar)
    95 {
    96   return (aChar >= '0' && aChar <= '9');
    97 }
    99 // Return the index of the first character in the string that is not
   100 // a numerical digit, starting from 'aStart'.
   101 static uint32_t FirstNonDigit(nsDependentSubstring& aString, uint32_t aStart)
   102 {
   103    while (aStart < aString.Length() && IsDigit(aString[aStart])) {
   104     ++aStart;
   105   }
   106   return aStart;
   107 }
   109 bool nsMediaFragmentURIParser::ParseNPTSec(nsDependentSubstring& aString, double& aSec)
   110 {
   111   nsDependentSubstring original(aString);
   112   if (aString.Length() == 0) {
   113     return false;
   114   }
   116   uint32_t index = FirstNonDigit(aString, 0);
   117   if (index == 0) {
   118     return false;
   119   }
   121   nsDependentSubstring n(aString, 0, index);
   122   nsresult ec;
   123   int32_t s = PromiseFlatString(n).ToInteger(&ec);
   124   if (NS_FAILED(ec)) {
   125     return false;
   126   }
   128   aString.Rebind(aString, index);
   129   double fraction = 0.0;
   130   if (!ParseNPTFraction(aString, fraction)) {
   131     aString.Rebind(original, 0);
   132     return false;
   133   }
   135   aSec = s + fraction;
   136   return true;
   137 }
   139 bool nsMediaFragmentURIParser::ParseNPTMMSS(nsDependentSubstring& aString, double& aTime)
   140 {
   141   nsDependentSubstring original(aString);
   142   uint32_t mm = 0;
   143   uint32_t ss = 0;
   144   double fraction = 0.0;
   145   if (!ParseNPTMM(aString, mm)) {
   146     aString.Rebind(original, 0);
   147     return false;
   148   }
   150   if (aString.Length() < 2 || aString[0] != ':') {
   151     aString.Rebind(original, 0);
   152     return false;
   153   }
   155   aString.Rebind(aString, 1);
   156   if (!ParseNPTSS(aString, ss)) {
   157     aString.Rebind(original, 0);
   158     return false;
   159   }
   161   if (!ParseNPTFraction(aString, fraction)) {
   162     aString.Rebind(original, 0);
   163     return false;
   164   }
   165   aTime = mm * 60 + ss + fraction;
   166   return true;
   167 }
   169 bool nsMediaFragmentURIParser::ParseNPTFraction(nsDependentSubstring& aString, double& aFraction)
   170 {
   171   double fraction = 0.0;
   173   if (aString.Length() > 0 && aString[0] == '.') {
   174     uint32_t index = FirstNonDigit(aString, 1);
   176     if (index > 1) {
   177       nsDependentSubstring number(aString, 0, index);
   178       nsresult ec;
   179       fraction = PromiseFlatString(number).ToDouble(&ec);
   180       if (NS_FAILED(ec)) {
   181         return false;
   182       }
   183     }
   184     aString.Rebind(aString, index);
   185   }
   187   aFraction = fraction;
   188   return true;
   189 }
   191 bool nsMediaFragmentURIParser::ParseNPTHHMMSS(nsDependentSubstring& aString, double& aTime)
   192 {
   193   nsDependentSubstring original(aString);
   194   uint32_t hh = 0;
   195   double seconds = 0.0;
   196   if (!ParseNPTHH(aString, hh)) {
   197     return false;
   198   }
   200   if (aString.Length() < 2 || aString[0] != ':') {
   201     aString.Rebind(original, 0);
   202     return false;
   203   }
   205   aString.Rebind(aString, 1);
   206   if (!ParseNPTMMSS(aString, seconds)) {
   207     aString.Rebind(original, 0);
   208     return false;
   209   }
   211   aTime = hh * 3600 + seconds;
   212   return true;
   213 }
   215 bool nsMediaFragmentURIParser::ParseNPTHH(nsDependentSubstring& aString, uint32_t& aHour)
   216 {
   217   if (aString.Length() == 0) {
   218     return false;
   219   }
   221   uint32_t index = FirstNonDigit(aString, 0);
   222   if (index == 0) {
   223     return false;
   224   }
   226   nsDependentSubstring n(aString, 0, index);
   227   nsresult ec;
   228   int32_t u = PromiseFlatString(n).ToInteger(&ec);
   229   if (NS_FAILED(ec)) {
   230     return false;
   231   }
   233   aString.Rebind(aString, index);
   234   aHour = u;
   235   return true;
   236 }
   238 bool nsMediaFragmentURIParser::ParseNPTMM(nsDependentSubstring& aString, uint32_t& aMinute)
   239 {
   240   return ParseNPTSS(aString, aMinute);
   241 }
   243 bool nsMediaFragmentURIParser::ParseNPTSS(nsDependentSubstring& aString, uint32_t& aSecond)
   244 {
   245   if (aString.Length() < 2) {
   246     return false;
   247   }
   249   if (IsDigit(aString[0]) && IsDigit(aString[1])) {
   250     nsDependentSubstring n(aString, 0, 2);
   251     nsresult ec;
   252     int32_t u = PromiseFlatString(n).ToInteger(&ec);
   253     if (NS_FAILED(ec)) {
   254       return false;
   255     }
   257     aString.Rebind(aString, 2);
   258     if (u >= 60)
   259       return false;
   261     aSecond = u;
   262     return true;
   263   }
   265   return false;
   266 }
   268 static bool ParseInteger(nsDependentSubstring& aString,
   269                          int32_t& aResult)
   270 {
   271   uint32_t index = FirstNonDigit(aString, 0);
   272   if (index == 0) {
   273     return false;
   274   }
   276   nsDependentSubstring n(aString, 0, index);
   277   nsresult ec;
   278   int32_t s = PromiseFlatString(n).ToInteger(&ec);
   279   if (NS_FAILED(ec)) {
   280     return false;
   281   }
   283   aString.Rebind(aString, index);
   284   aResult = s;
   285   return true;
   286 }
   288 static bool ParseCommaSeparator(nsDependentSubstring& aString)
   289 {
   290   if (aString.Length() > 1 && aString[0] == ',') {
   291     aString.Rebind(aString, 1);
   292     return true;
   293   }
   295   return false;
   296 }
   298 bool nsMediaFragmentURIParser::ParseXYWH(nsDependentSubstring aString)
   299 {
   300   int32_t x, y, w, h;
   301   ClipUnit clipUnit;
   303   // Determine units.
   304   if (StringBeginsWith(aString, NS_LITERAL_STRING("pixel:"))) {
   305     clipUnit = eClipUnit_Pixel;
   306     aString.Rebind(aString, 6);
   307   } else if (StringBeginsWith(aString, NS_LITERAL_STRING("percent:"))) {
   308     clipUnit = eClipUnit_Percent;
   309     aString.Rebind(aString, 8);
   310   } else {
   311     clipUnit = eClipUnit_Pixel;
   312   }
   314   // Read and validate coordinates.
   315   if (ParseInteger(aString, x) && x >= 0 &&
   316       ParseCommaSeparator(aString)       &&
   317       ParseInteger(aString, y) && y >= 0 &&
   318       ParseCommaSeparator(aString)       &&
   319       ParseInteger(aString, w) && w > 0  &&
   320       ParseCommaSeparator(aString)       &&
   321       ParseInteger(aString, h) && h > 0  &&
   322       aString.Length() == 0) {
   324     // Reject invalid percentage coordinates.
   325     if (clipUnit == eClipUnit_Percent &&
   326         (x + w > 100 || y + h > 100)) {
   327       return false;
   328     }
   330     mClip.construct(x, y, w, h);
   331     mClipUnit = clipUnit;
   332     return true;
   333   }
   335   return false;
   336 }
   338 bool nsMediaFragmentURIParser::ParseMozResolution(nsDependentSubstring aString)
   339 {
   340   int32_t w, h;
   342   // Read and validate coordinates.
   343   if (ParseInteger(aString, w) && w >= 0 &&
   344       ParseCommaSeparator(aString)       &&
   345       ParseInteger(aString, h) && h >= 0 &&
   346       aString.Length() == 0) {
   347     mResolution.construct(w,h);
   348     return true;
   349   }
   351   return false;
   352 }
   354 bool nsMediaFragmentURIParser::ParseMozSampleSize(nsDependentSubstring aString)
   355 {
   356   int32_t sampleSize;
   358   // Read and validate coordinates.
   359   if (ParseInteger(aString, sampleSize) && sampleSize > 0) {
   360     mSampleSize.construct(sampleSize);
   361     return true;
   362   }
   364   return false;
   365 }
   367 void nsMediaFragmentURIParser::Parse(nsACString& aRef)
   368 {
   369   // Create an array of possibly-invalid media fragments.
   370   nsTArray< std::pair<nsCString, nsCString> > fragments;
   371   nsCCharSeparatedTokenizer tokenizer(aRef, '&');
   373   while (tokenizer.hasMoreTokens()) {
   374     const nsCSubstring& nv = tokenizer.nextToken();
   375     int32_t index = nv.FindChar('=');
   376     if (index >= 0) {
   377       nsAutoCString name;
   378       nsAutoCString value;
   379       NS_UnescapeURL(StringHead(nv, index), esc_Ref | esc_AlwaysCopy, name);
   380       NS_UnescapeURL(Substring(nv, index + 1, nv.Length()),
   381                      esc_Ref | esc_AlwaysCopy, value);
   382       fragments.AppendElement(make_pair(name, value));
   383     }
   384   }
   386   // Parse the media fragment values.
   387   bool gotTemporal = false, gotSpatial = false,
   388       gotResolution = false, gotSampleSize = false;
   389   for (int i = fragments.Length() - 1 ; i >= 0 ; --i) {
   390     if (gotTemporal && gotSpatial && gotResolution && gotSampleSize) {
   391       // We've got one of each possible type. No need to look at the rest.
   392       break;
   393     } else if (!gotTemporal && fragments[i].first.EqualsLiteral("t")) {
   394       nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second);
   395       gotTemporal = ParseNPT(nsDependentSubstring(value, 0));
   396     } else if (!gotSpatial && fragments[i].first.EqualsLiteral("xywh")) {
   397       nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second);
   398       gotSpatial = ParseXYWH(nsDependentSubstring(value, 0));
   399     } else if (!gotResolution && fragments[i].first.EqualsLiteral("-moz-resolution")) {
   400       nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second);
   401       gotResolution = ParseMozResolution(nsDependentSubstring(value, 0));
   402     } else if (!gotSampleSize && fragments[i].first.EqualsLiteral("-moz-samplesize")) {
   403       nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second);
   404       gotSampleSize = ParseMozSampleSize(nsDependentSubstring(value, 0));
   405     }
   406   }
   407 }
   409 }} // namespace mozilla::net

mercurial