Keep auxilliary media objects on the heap always.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / MediaFragmentURIParser.cpp
blobb41cd74d61fd4112692e86307b0c4ce3d6cbcce1
1 /*
2 * Copyright (C) 2011, 2012 Apple Inc. All rights reserved.
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.
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.
26 #include "config.h"
27 #include "core/html/MediaFragmentURIParser.h"
29 #include "wtf/text/CString.h"
30 #include "wtf/text/StringBuilder.h"
31 #include "wtf/text/WTFString.h"
33 namespace blink {
35 const int secondsPerHour = 3600;
36 const int secondsPerMinute = 60;
37 const unsigned nptIdentiferLength = 4; // "npt:"
39 static String collectDigits(const LChar* input, unsigned length, unsigned& position)
41 StringBuilder digits;
43 // http://www.ietf.org/rfc/rfc2326.txt
44 // DIGIT ; any positive number
45 while (position < length && isASCIIDigit(input[position]))
46 digits.append(input[position++]);
47 return digits.toString();
50 static String collectFraction(const LChar* input, unsigned length, unsigned& position)
52 StringBuilder digits;
54 // http://www.ietf.org/rfc/rfc2326.txt
55 // [ "." *DIGIT ]
56 if (input[position] != '.')
57 return String();
59 digits.append(input[position++]);
60 while (position < length && isASCIIDigit(input[position]))
61 digits.append(input[position++]);
62 return digits.toString();
65 MediaFragmentURIParser::MediaFragmentURIParser(const KURL& url)
66 : m_url(url)
67 , m_timeFormat(None)
68 , m_startTime(std::numeric_limits<double>::quiet_NaN())
69 , m_endTime(std::numeric_limits<double>::quiet_NaN())
73 double MediaFragmentURIParser::startTime()
75 if (!m_url.isValid())
76 return std::numeric_limits<double>::quiet_NaN();
77 if (m_timeFormat == None)
78 parseTimeFragment();
79 return m_startTime;
82 double MediaFragmentURIParser::endTime()
84 if (!m_url.isValid())
85 return std::numeric_limits<double>::quiet_NaN();
86 if (m_timeFormat == None)
87 parseTimeFragment();
88 return m_endTime;
91 void MediaFragmentURIParser::parseFragments()
93 if (!m_url.hasFragmentIdentifier())
94 return;
95 String fragmentString = m_url.fragmentIdentifier();
96 if (fragmentString.isEmpty())
97 return;
99 unsigned offset = 0;
100 unsigned end = fragmentString.length();
101 while (offset < end) {
102 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#processing-name-value-components
103 // 1. Parse the octet string according to the namevalues syntax, yielding a list of
104 // name-value pairs, where name and value are both octet string. In accordance
105 // with RFC 3986, the name and value components must be parsed and separated before
106 // percent-encoded octets are decoded.
107 size_t parameterStart = offset;
108 size_t parameterEnd = fragmentString.find('&', offset);
109 if (parameterEnd == kNotFound)
110 parameterEnd = end;
112 size_t equalOffset = fragmentString.find('=', offset);
113 if (equalOffset == kNotFound || equalOffset > parameterEnd) {
114 offset = parameterEnd + 1;
115 continue;
118 // 2. For each name-value pair:
119 // a. Decode percent-encoded octets in name and value as defined by RFC 3986. If either
120 // name or value are not valid percent-encoded strings, then remove the name-value pair
121 // from the list.
122 String name = decodeURLEscapeSequences(fragmentString.substring(parameterStart, equalOffset - parameterStart));
123 String value;
124 if (equalOffset != parameterEnd)
125 value = decodeURLEscapeSequences(fragmentString.substring(equalOffset + 1, parameterEnd - equalOffset - 1));
127 // b. Convert name and value to Unicode strings by interpreting them as UTF-8. If either
128 // name or value are not valid UTF-8 strings, then remove the name-value pair from the list.
129 bool validUTF8 = true;
130 if (!name.isEmpty()) {
131 name = name.utf8(StrictUTF8Conversion).data();
132 validUTF8 = !name.isEmpty();
134 if (validUTF8 && !value.isEmpty()) {
135 value = value.utf8(StrictUTF8Conversion).data();
136 validUTF8 = !value.isEmpty();
139 if (validUTF8)
140 m_fragments.append(std::make_pair(name, value));
142 offset = parameterEnd + 1;
146 void MediaFragmentURIParser::parseTimeFragment()
148 ASSERT(m_timeFormat == None);
150 if (m_fragments.isEmpty())
151 parseFragments();
153 m_timeFormat = Invalid;
155 for (unsigned i = 0; i < m_fragments.size(); ++i) {
156 pair<String, String>& fragment = m_fragments[i];
158 ASSERT(fragment.first.is8Bit());
159 ASSERT(fragment.second.is8Bit());
161 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-time
162 // Temporal clipping is denoted by the name t, and specified as an interval with a begin
163 // time and an end time
164 if (fragment.first != "t")
165 continue;
167 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npt-time
168 // Temporal clipping can be specified either as Normal Play Time (npt) RFC 2326, as SMPTE timecodes,
169 // SMPTE, or as real-world clock time (clock) RFC 2326. Begin and end times are always specified
170 // in the same format. The format is specified by name, followed by a colon (:), with npt: being
171 // the default.
173 double start = std::numeric_limits<double>::quiet_NaN();
174 double end = std::numeric_limits<double>::quiet_NaN();
175 if (parseNPTFragment(fragment.second.characters8(), fragment.second.length(), start, end)) {
176 m_startTime = start;
177 m_endTime = end;
178 m_timeFormat = NormalPlayTime;
180 // Although we have a valid fragment, don't return yet because when a fragment dimensions
181 // occurs multiple times, only the last occurrence of that dimension is used:
182 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#error-uri-general
183 // Multiple occurrences of the same dimension: only the last valid occurrence of a dimension
184 // (e.g., t=10 in #t=2&t=10) is interpreted, all previous occurrences (valid or invalid)
185 // SHOULD be ignored by the UA.
188 m_fragments.clear();
191 bool MediaFragmentURIParser::parseNPTFragment(const LChar* timeString, unsigned length, double& startTime, double& endTime)
193 unsigned offset = 0;
194 if (length >= nptIdentiferLength && timeString[0] == 'n' && timeString[1] == 'p' && timeString[2] == 't' && timeString[3] == ':')
195 offset += nptIdentiferLength;
197 if (offset == length)
198 return false;
200 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#naming-time
201 // If a single number only is given, this corresponds to the begin time except if it is preceded
202 // 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;
210 if (offset == length)
211 return true;
213 if (timeString[offset] != ',')
214 return false;
215 if (++offset == length)
216 return false;
218 if (!parseNPTTime(timeString, length, offset, endTime))
219 return false;
221 if (offset != length)
222 return false;
224 if (startTime >= endTime)
225 return false;
227 return true;
230 bool MediaFragmentURIParser::parseNPTTime(const LChar* timeString, unsigned length, unsigned& offset, double& time)
232 enum Mode { Minutes, Hours };
233 Mode mode = Minutes;
235 if (offset >= length || !isASCIIDigit(timeString[offset]))
236 return false;
238 // http://www.w3.org/2008/WebVideo/Fragments/WD-media-fragments-spec/#npttimedef
239 // Normal Play Time can either be specified as seconds, with an optional
240 // fractional part to indicate miliseconds, or as colon-separated hours,
241 // minutes and seconds (again with an optional fraction). Minutes and
242 // seconds must be specified as exactly two digits, hours and fractional
243 // seconds can be any number of digits. The hours, minutes and seconds
244 // specification for NPT is a convenience only, it does not signal frame
245 // accuracy. The specification of the "npt:" identifier is optional since
246 // NPT is the default time scheme. This specification builds on the RTSP
247 // specification of NPT RFC 2326.
249 // ; defined in RFC 2326
250 // npt-sec = 1*DIGIT [ "." *DIGIT ] ; definitions taken
251 // npt-hhmmss = npt-hh ":" npt-mm ":" npt-ss [ "." *DIGIT] ; from RFC 2326
252 // npt-mmss = npt-mm ":" npt-ss [ "." *DIGIT]
253 // npt-hh = 1*DIGIT ; any positive number
254 // npt-mm = 2DIGIT ; 0-59
255 // npt-ss = 2DIGIT ; 0-59
257 String digits1 = collectDigits(timeString, length, offset);
258 int value1 = digits1.toInt();
259 if (offset >= length || timeString[offset] == ',') {
260 time = value1;
261 return true;
264 double fraction = 0;
265 if (timeString[offset] == '.') {
266 if (offset == length)
267 return true;
268 String digits = collectFraction(timeString, length, offset);
269 fraction = digits.toDouble();
270 time = value1 + fraction;
271 return true;
274 if (digits1.length() < 2)
275 return false;
276 if (digits1.length() > 2)
277 mode = Hours;
279 // Collect the next sequence of 0-9 after ':'
280 if (offset >= length || timeString[offset++] != ':')
281 return false;
282 if (offset >= length || !isASCIIDigit(timeString[(offset)]))
283 return false;
284 String digits2 = collectDigits(timeString, length, offset);
285 int value2 = digits2.toInt();
286 if (digits2.length() != 2)
287 return false;
289 // Detect whether this timestamp includes hours.
290 int value3;
291 if (mode == Hours || (offset < length && timeString[offset] == ':')) {
292 if (offset >= length || timeString[offset++] != ':')
293 return false;
294 if (offset >= length || !isASCIIDigit(timeString[offset]))
295 return false;
296 String digits3 = collectDigits(timeString, length, offset);
297 if (digits3.length() != 2)
298 return false;
299 value3 = digits3.toInt();
300 } else {
301 value3 = value2;
302 value2 = value1;
303 value1 = 0;
306 if (offset < length && timeString[offset] == '.')
307 fraction = collectFraction(timeString, length, offset).toDouble();
309 time = (value1 * secondsPerHour) + (value2 * secondsPerMinute) + value3 + fraction;
310 return true;