| OLD | NEW | 
|---|
|  | (Empty) | 
| 1 /* |  | 
| 2  * Copyright (C) 2011, 2012 Apple Inc. All rights reserved. |  | 
| 3  * |  | 
| 4  * Redistribution and use in source and binary forms, with or without |  | 
| 5  * modification, are permitted provided that the following conditions |  | 
| 6  * are met: |  | 
| 7  * 1. Redistributions of source code must retain the above copyright |  | 
| 8  *    notice, this list of conditions and the following disclaimer. |  | 
| 9  * 2. Redistributions in binary form must reproduce the above copyright |  | 
| 10  *    notice, this list of conditions and the following disclaimer in the |  | 
| 11  *    documentation and/or other materials provided with the distribution. |  | 
| 12  * |  | 
| 13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY |  | 
| 14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |  | 
| 15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |  | 
| 16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR |  | 
| 17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |  | 
| 18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |  | 
| 19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |  | 
| 20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |  | 
| 21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |  | 
| 22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |  | 
| 23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |  | 
| 24  */ |  | 
| 25 |  | 
| 26 #include "core/html/MediaFragmentURIParser.h" |  | 
| 27 |  | 
| 28 #include "wtf/text/CString.h" |  | 
| 29 #include "wtf/text/StringBuilder.h" |  | 
| 30 #include "wtf/text/WTFString.h" |  | 
| 31 |  | 
| 32 namespace blink { |  | 
| 33 |  | 
| 34 const unsigned nptIdentiferLength = 4;  // "npt:" |  | 
| 35 |  | 
| 36 static String collectDigits(const LChar* input, |  | 
| 37                             unsigned length, |  | 
| 38                             unsigned& position) { |  | 
| 39   StringBuilder digits; |  | 
| 40 |  | 
| 41   // http://www.ietf.org/rfc/rfc2326.txt |  | 
| 42   // DIGIT ; any positive number |  | 
| 43   while (position < length && isASCIIDigit(input[position])) |  | 
| 44     digits.append(input[position++]); |  | 
| 45   return digits.toString(); |  | 
| 46 } |  | 
| 47 |  | 
| 48 static String collectFraction(const LChar* input, |  | 
| 49                               unsigned length, |  | 
| 50                               unsigned& position) { |  | 
| 51   StringBuilder digits; |  | 
| 52 |  | 
| 53   // http://www.ietf.org/rfc/rfc2326.txt |  | 
| 54   // [ "." *DIGIT ] |  | 
| 55   if (input[position] != '.') |  | 
| 56     return String(); |  | 
| 57 |  | 
| 58   digits.append(input[position++]); |  | 
| 59   while (position < length && isASCIIDigit(input[position])) |  | 
| 60     digits.append(input[position++]); |  | 
| 61   return digits.toString(); |  | 
| 62 } |  | 
| 63 |  | 
| 64 MediaFragmentURIParser::MediaFragmentURIParser(const KURL& url) |  | 
| 65     : m_url(url), |  | 
| 66       m_timeFormat(None), |  | 
| 67       m_startTime(std::numeric_limits<double>::quiet_NaN()), |  | 
| 68       m_endTime(std::numeric_limits<double>::quiet_NaN()) {} |  | 
| 69 |  | 
| 70 double MediaFragmentURIParser::startTime() { |  | 
| 71   if (!m_url.isValid()) |  | 
| 72     return std::numeric_limits<double>::quiet_NaN(); |  | 
| 73   if (m_timeFormat == None) |  | 
| 74     parseTimeFragment(); |  | 
| 75   return m_startTime; |  | 
| 76 } |  | 
| 77 |  | 
| 78 double MediaFragmentURIParser::endTime() { |  | 
| 79   if (!m_url.isValid()) |  | 
| 80     return std::numeric_limits<double>::quiet_NaN(); |  | 
| 81   if (m_timeFormat == None) |  | 
| 82     parseTimeFragment(); |  | 
| 83   return m_endTime; |  | 
| 84 } |  | 
| 85 |  | 
| 86 void MediaFragmentURIParser::parseFragments() { |  | 
| 87   if (!m_url.hasFragmentIdentifier()) |  | 
| 88     return; |  | 
| 89   String fragmentString = m_url.fragmentIdentifier(); |  | 
| 90   if (fragmentString.isEmpty()) |  | 
| 91     return; |  | 
| 92 |  | 
| 93   unsigned offset = 0; |  | 
| 94   unsigned end = fragmentString.length(); |  | 
| 95   while (offset < end) { |  | 
| 96     // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#proces
     sing-name-value-components |  | 
| 97     // 1. Parse the octet string according to the namevalues syntax, yielding a |  | 
| 98     //    list of name-value pairs, where name and value are both octet string. |  | 
| 99     //    In accordance with RFC 3986, the name and value components must be |  | 
| 100     //    parsed and separated before percent-encoded octets are decoded. |  | 
| 101     size_t parameterStart = offset; |  | 
| 102     size_t parameterEnd = fragmentString.find('&', offset); |  | 
| 103     if (parameterEnd == kNotFound) |  | 
| 104       parameterEnd = end; |  | 
| 105 |  | 
| 106     size_t equalOffset = fragmentString.find('=', offset); |  | 
| 107     if (equalOffset == kNotFound || equalOffset > parameterEnd) { |  | 
| 108       offset = parameterEnd + 1; |  | 
| 109       continue; |  | 
| 110     } |  | 
| 111 |  | 
| 112     // 2. For each name-value pair: |  | 
| 113     //  a. Decode percent-encoded octets in name and value as defined by RFC |  | 
| 114     //     3986. If either name or value are not valid percent-encoded strings, |  | 
| 115     //     then remove the name-value pair from the list. |  | 
| 116     String name = decodeURLEscapeSequences( |  | 
| 117         fragmentString.substring(parameterStart, equalOffset - parameterStart)); |  | 
| 118     String value; |  | 
| 119     if (equalOffset != parameterEnd) |  | 
| 120       value = decodeURLEscapeSequences(fragmentString.substring( |  | 
| 121           equalOffset + 1, parameterEnd - equalOffset - 1)); |  | 
| 122 |  | 
| 123     //  b. Convert name and value to Unicode strings by interpreting them as |  | 
| 124     //     UTF-8. If either name or value are not valid UTF-8 strings, then |  | 
| 125     //     remove the name-value pair from the list. |  | 
| 126     bool validUTF8 = true; |  | 
| 127     if (!name.isEmpty()) { |  | 
| 128       name = name.utf8(StrictUTF8Conversion).data(); |  | 
| 129       validUTF8 = !name.isEmpty(); |  | 
| 130     } |  | 
| 131     if (validUTF8 && !value.isEmpty()) { |  | 
| 132       value = value.utf8(StrictUTF8Conversion).data(); |  | 
| 133       validUTF8 = !value.isEmpty(); |  | 
| 134     } |  | 
| 135 |  | 
| 136     if (validUTF8) |  | 
| 137       m_fragments.push_back(std::make_pair(name, value)); |  | 
| 138 |  | 
| 139     offset = parameterEnd + 1; |  | 
| 140   } |  | 
| 141 } |  | 
| 142 |  | 
| 143 void MediaFragmentURIParser::parseTimeFragment() { |  | 
| 144   DCHECK_EQ(m_timeFormat, None); |  | 
| 145 |  | 
| 146   if (m_fragments.isEmpty()) |  | 
| 147     parseFragments(); |  | 
| 148 |  | 
| 149   m_timeFormat = Invalid; |  | 
| 150 |  | 
| 151   for (const auto& fragment : m_fragments) { |  | 
| 152     DCHECK(fragment.first.is8Bit()); |  | 
| 153     DCHECK(fragment.second.is8Bit()); |  | 
| 154 |  | 
| 155     // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming
     -time |  | 
| 156     // Temporal clipping is denoted by the name t, and specified as an interval |  | 
| 157     // with a begin time and an end time |  | 
| 158     if (fragment.first != "t") |  | 
| 159       continue; |  | 
| 160 |  | 
| 161     // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npt-ti
     me |  | 
| 162     // Temporal clipping can be specified either as Normal Play Time (npt) RFC |  | 
| 163     // 2326, as SMPTE timecodes, SMPTE, or as real-world clock time (clock) RFC |  | 
| 164     // 2326. Begin and end times are always specified in the same format. The |  | 
| 165     // format is specified by name, followed by a colon (:), with npt: being the |  | 
| 166     // default. |  | 
| 167 |  | 
| 168     double start = std::numeric_limits<double>::quiet_NaN(); |  | 
| 169     double end = std::numeric_limits<double>::quiet_NaN(); |  | 
| 170     if (parseNPTFragment(fragment.second.characters8(), |  | 
| 171                          fragment.second.length(), start, end)) { |  | 
| 172       m_startTime = start; |  | 
| 173       m_endTime = end; |  | 
| 174       m_timeFormat = NormalPlayTime; |  | 
| 175 |  | 
| 176       // Although we have a valid fragment, don't return yet because when a |  | 
| 177       // fragment dimensions occurs multiple times, only the last occurrence of |  | 
| 178       // that dimension is used: |  | 
| 179       // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#erro
     r-uri-general |  | 
| 180       // Multiple occurrences of the same dimension: only the last valid |  | 
| 181       // occurrence of a dimension (e.g., t=10 in #t=2&t=10) is interpreted, all |  | 
| 182       // previous occurrences (valid or invalid) SHOULD be ignored by the UA. |  | 
| 183     } |  | 
| 184   } |  | 
| 185   m_fragments.clear(); |  | 
| 186 } |  | 
| 187 |  | 
| 188 bool MediaFragmentURIParser::parseNPTFragment(const LChar* timeString, |  | 
| 189                                               unsigned length, |  | 
| 190                                               double& startTime, |  | 
| 191                                               double& endTime) { |  | 
| 192   unsigned offset = 0; |  | 
| 193   if (length >= nptIdentiferLength && timeString[0] == 'n' && |  | 
| 194       timeString[1] == 'p' && timeString[2] == 't' && timeString[3] == ':') |  | 
| 195     offset += nptIdentiferLength; |  | 
| 196 |  | 
| 197   if (offset == length) |  | 
| 198     return false; |  | 
| 199 |  | 
| 200   // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-t
     ime |  | 
| 201   // If a single number only is given, this corresponds to the begin time except |  | 
| 202   // if it is preceded by a comma that would in this case indicate the end time. |  | 
| 203   if (timeString[offset] == ',') { |  | 
| 204     startTime = 0; |  | 
| 205   } else { |  | 
| 206     if (!parseNPTTime(timeString, length, offset, startTime)) |  | 
| 207       return false; |  | 
| 208   } |  | 
| 209 |  | 
| 210   if (offset == length) |  | 
| 211     return true; |  | 
| 212 |  | 
| 213   if (timeString[offset] != ',') |  | 
| 214     return false; |  | 
| 215   if (++offset == length) |  | 
| 216     return false; |  | 
| 217 |  | 
| 218   if (!parseNPTTime(timeString, length, offset, endTime)) |  | 
| 219     return false; |  | 
| 220 |  | 
| 221   if (offset != length) |  | 
| 222     return false; |  | 
| 223 |  | 
| 224   if (startTime >= endTime) |  | 
| 225     return false; |  | 
| 226 |  | 
| 227   return true; |  | 
| 228 } |  | 
| 229 |  | 
| 230 bool MediaFragmentURIParser::parseNPTTime(const LChar* timeString, |  | 
| 231                                           unsigned length, |  | 
| 232                                           unsigned& offset, |  | 
| 233                                           double& time) { |  | 
| 234   enum Mode { Minutes, Hours }; |  | 
| 235   Mode mode = Minutes; |  | 
| 236 |  | 
| 237   if (offset >= length || !isASCIIDigit(timeString[offset])) |  | 
| 238     return false; |  | 
| 239 |  | 
| 240   // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npttimed
     ef |  | 
| 241   // Normal Play Time can either be specified as seconds, with an optional |  | 
| 242   // fractional part to indicate miliseconds, or as colon-separated hours, |  | 
| 243   // minutes and seconds (again with an optional fraction). Minutes and |  | 
| 244   // seconds must be specified as exactly two digits, hours and fractional |  | 
| 245   // seconds can be any number of digits. The hours, minutes and seconds |  | 
| 246   // specification for NPT is a convenience only, it does not signal frame |  | 
| 247   // accuracy. The specification of the "npt:" identifier is optional since |  | 
| 248   // NPT is the default time scheme. This specification builds on the RTSP |  | 
| 249   // specification of NPT RFC 2326. |  | 
| 250   // |  | 
| 251   // ; defined in RFC 2326 |  | 
| 252   // npt-sec       = 1*DIGIT [ "." *DIGIT ] |  | 
| 253   // npt-hhmmss    = npt-hh ":" npt-mm ":" npt-ss [ "." *DIGIT] |  | 
| 254   // npt-mmss      = npt-mm ":" npt-ss [ "." *DIGIT] |  | 
| 255   // npt-hh        =   1*DIGIT     ; any positive number |  | 
| 256   // npt-mm        =   2DIGIT      ; 0-59 |  | 
| 257   // npt-ss        =   2DIGIT      ; 0-59 |  | 
| 258 |  | 
| 259   String digits1 = collectDigits(timeString, length, offset); |  | 
| 260   int value1 = digits1.toInt(); |  | 
| 261   if (offset >= length || timeString[offset] == ',') { |  | 
| 262     time = value1; |  | 
| 263     return true; |  | 
| 264   } |  | 
| 265 |  | 
| 266   double fraction = 0; |  | 
| 267   if (timeString[offset] == '.') { |  | 
| 268     if (offset == length) |  | 
| 269       return true; |  | 
| 270     String digits = collectFraction(timeString, length, offset); |  | 
| 271     fraction = digits.toDouble(); |  | 
| 272     time = value1 + fraction; |  | 
| 273     return true; |  | 
| 274   } |  | 
| 275 |  | 
| 276   if (digits1.length() < 2) |  | 
| 277     return false; |  | 
| 278   if (digits1.length() > 2) |  | 
| 279     mode = Hours; |  | 
| 280 |  | 
| 281   // Collect the next sequence of 0-9 after ':' |  | 
| 282   if (offset >= length || timeString[offset++] != ':') |  | 
| 283     return false; |  | 
| 284   if (offset >= length || !isASCIIDigit(timeString[(offset)])) |  | 
| 285     return false; |  | 
| 286   String digits2 = collectDigits(timeString, length, offset); |  | 
| 287   int value2 = digits2.toInt(); |  | 
| 288   if (digits2.length() != 2) |  | 
| 289     return false; |  | 
| 290 |  | 
| 291   // Detect whether this timestamp includes hours. |  | 
| 292   int value3; |  | 
| 293   if (mode == Hours || (offset < length && timeString[offset] == ':')) { |  | 
| 294     if (offset >= length || timeString[offset++] != ':') |  | 
| 295       return false; |  | 
| 296     if (offset >= length || !isASCIIDigit(timeString[offset])) |  | 
| 297       return false; |  | 
| 298     String digits3 = collectDigits(timeString, length, offset); |  | 
| 299     if (digits3.length() != 2) |  | 
| 300       return false; |  | 
| 301     value3 = digits3.toInt(); |  | 
| 302   } else { |  | 
| 303     value3 = value2; |  | 
| 304     value2 = value1; |  | 
| 305     value1 = 0; |  | 
| 306   } |  | 
| 307 |  | 
| 308   if (offset < length && timeString[offset] == '.') |  | 
| 309     fraction = collectFraction(timeString, length, offset).toDouble(); |  | 
| 310 |  | 
| 311   const int secondsPerHour = 3600; |  | 
| 312   const int secondsPerMinute = 60; |  | 
| 313   time = (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + |  | 
| 314          fraction; |  | 
| 315   return true; |  | 
| 316 } |  | 
| 317 |  | 
| 318 }  // namespace blink |  | 
| OLD | NEW | 
|---|