diff options
Diffstat (limited to '')
-rw-r--r-- | netwerk/base/nsMediaFragmentURIParser.cpp | 353 |
1 files changed, 353 insertions, 0 deletions
diff --git a/netwerk/base/nsMediaFragmentURIParser.cpp b/netwerk/base/nsMediaFragmentURIParser.cpp new file mode 100644 index 0000000000..9252c24263 --- /dev/null +++ b/netwerk/base/nsMediaFragmentURIParser.cpp @@ -0,0 +1,353 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsTArray.h" +#include "nsCharSeparatedTokenizer.h" +#include "nsEscape.h" +#include "nsIURI.h" +#include <utility> + +#include "nsMediaFragmentURIParser.h" + +using std::make_pair; +using std::pair; + +namespace mozilla { +namespace net { + +nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsIURI* aURI) + : mClipUnit(eClipUnit_Pixel) { + nsAutoCString ref; + aURI->GetRef(ref); + Parse(ref); +} + +nsMediaFragmentURIParser::nsMediaFragmentURIParser(nsCString& aRef) + : mClipUnit(eClipUnit_Pixel) { + Parse(aRef); +} + +bool nsMediaFragmentURIParser::ParseNPT(nsDependentSubstring aString) { + nsDependentSubstring original(aString); + if (aString.Length() > 4 && aString[0] == 'n' && aString[1] == 'p' && + aString[2] == 't' && aString[3] == ':') { + aString.Rebind(aString, 4); + } + + if (aString.Length() == 0) { + return false; + } + + double start = -1.0; + double end = -1.0; + + ParseNPTTime(aString, start); + + if (aString.Length() == 0) { + mStart.emplace(start); + return true; + } + + if (aString[0] != ',') { + aString.Rebind(original, 0); + return false; + } + + aString.Rebind(aString, 1); + + if (aString.Length() == 0) { + aString.Rebind(original, 0); + return false; + } + + ParseNPTTime(aString, end); + + if (end <= start || aString.Length() != 0) { + aString.Rebind(original, 0); + return false; + } + + mStart.emplace(start); + mEnd.emplace(end); + return true; +} + +bool nsMediaFragmentURIParser::ParseNPTTime(nsDependentSubstring& aString, + double& aTime) { + if (aString.Length() == 0) { + return false; + } + + return ParseNPTHHMMSS(aString, aTime) || ParseNPTMMSS(aString, aTime) || + ParseNPTSec(aString, aTime); +} + +// Return true if the given character is a numeric character +static bool IsDigit(nsDependentSubstring::char_type aChar) { + return (aChar >= '0' && aChar <= '9'); +} + +// Return the index of the first character in the string that is not +// a numerical digit, starting from 'aStart'. +static uint32_t FirstNonDigit(nsDependentSubstring& aString, uint32_t aStart) { + while (aStart < aString.Length() && IsDigit(aString[aStart])) { + ++aStart; + } + return aStart; +} + +bool nsMediaFragmentURIParser::ParseNPTSec(nsDependentSubstring& aString, + double& aSec) { + nsDependentSubstring original(aString); + if (aString.Length() == 0) { + return false; + } + + uint32_t index = FirstNonDigit(aString, 0); + if (index == 0) { + return false; + } + + nsDependentSubstring n(aString, 0, index); + nsresult ec; + int32_t s = n.ToInteger(&ec); + if (NS_FAILED(ec)) { + return false; + } + + aString.Rebind(aString, index); + double fraction = 0.0; + if (!ParseNPTFraction(aString, fraction)) { + aString.Rebind(original, 0); + return false; + } + + aSec = s + fraction; + return true; +} + +bool nsMediaFragmentURIParser::ParseNPTMMSS(nsDependentSubstring& aString, + double& aTime) { + nsDependentSubstring original(aString); + uint32_t mm = 0; + uint32_t ss = 0; + double fraction = 0.0; + if (!ParseNPTMM(aString, mm)) { + aString.Rebind(original, 0); + return false; + } + + if (aString.Length() < 2 || aString[0] != ':') { + aString.Rebind(original, 0); + return false; + } + + aString.Rebind(aString, 1); + if (!ParseNPTSS(aString, ss)) { + aString.Rebind(original, 0); + return false; + } + + if (!ParseNPTFraction(aString, fraction)) { + aString.Rebind(original, 0); + return false; + } + aTime = mm * 60 + ss + fraction; + return true; +} + +bool nsMediaFragmentURIParser::ParseNPTFraction(nsDependentSubstring& aString, + double& aFraction) { + double fraction = 0.0; + + if (aString.Length() > 0 && aString[0] == '.') { + uint32_t index = FirstNonDigit(aString, 1); + + if (index > 1) { + nsDependentSubstring number(aString, 0, index); + nsresult ec; + fraction = PromiseFlatString(number).ToDouble(&ec); + if (NS_FAILED(ec)) { + return false; + } + } + aString.Rebind(aString, index); + } + + aFraction = fraction; + return true; +} + +bool nsMediaFragmentURIParser::ParseNPTHHMMSS(nsDependentSubstring& aString, + double& aTime) { + nsDependentSubstring original(aString); + uint32_t hh = 0; + double seconds = 0.0; + if (!ParseNPTHH(aString, hh)) { + return false; + } + + if (aString.Length() < 2 || aString[0] != ':') { + aString.Rebind(original, 0); + return false; + } + + aString.Rebind(aString, 1); + if (!ParseNPTMMSS(aString, seconds)) { + aString.Rebind(original, 0); + return false; + } + + aTime = hh * 3600 + seconds; + return true; +} + +bool nsMediaFragmentURIParser::ParseNPTHH(nsDependentSubstring& aString, + uint32_t& aHour) { + if (aString.Length() == 0) { + return false; + } + + uint32_t index = FirstNonDigit(aString, 0); + if (index == 0) { + return false; + } + + nsDependentSubstring n(aString, 0, index); + nsresult ec; + int32_t u = n.ToInteger(&ec); + if (NS_FAILED(ec)) { + return false; + } + + aString.Rebind(aString, index); + aHour = u; + return true; +} + +bool nsMediaFragmentURIParser::ParseNPTMM(nsDependentSubstring& aString, + uint32_t& aMinute) { + return ParseNPTSS(aString, aMinute); +} + +bool nsMediaFragmentURIParser::ParseNPTSS(nsDependentSubstring& aString, + uint32_t& aSecond) { + if (aString.Length() < 2) { + return false; + } + + if (IsDigit(aString[0]) && IsDigit(aString[1])) { + nsDependentSubstring n(aString, 0, 2); + nsresult ec; + int32_t u = n.ToInteger(&ec); + if (NS_FAILED(ec)) { + return false; + } + + aString.Rebind(aString, 2); + if (u >= 60) return false; + + aSecond = u; + return true; + } + + return false; +} + +static bool ParseInteger(nsDependentSubstring& aString, int32_t& aResult) { + uint32_t index = FirstNonDigit(aString, 0); + if (index == 0) { + return false; + } + + nsDependentSubstring n(aString, 0, index); + nsresult ec; + int32_t s = n.ToInteger(&ec); + if (NS_FAILED(ec)) { + return false; + } + + aString.Rebind(aString, index); + aResult = s; + return true; +} + +static bool ParseCommaSeparator(nsDependentSubstring& aString) { + if (aString.Length() > 1 && aString[0] == ',') { + aString.Rebind(aString, 1); + return true; + } + + return false; +} + +bool nsMediaFragmentURIParser::ParseXYWH(nsDependentSubstring aString) { + int32_t x, y, w, h; + ClipUnit clipUnit; + + // Determine units. + if (StringBeginsWith(aString, u"pixel:"_ns)) { + clipUnit = eClipUnit_Pixel; + aString.Rebind(aString, 6); + } else if (StringBeginsWith(aString, u"percent:"_ns)) { + clipUnit = eClipUnit_Percent; + aString.Rebind(aString, 8); + } else { + clipUnit = eClipUnit_Pixel; + } + + // Read and validate coordinates. + if (ParseInteger(aString, x) && x >= 0 && ParseCommaSeparator(aString) && + ParseInteger(aString, y) && y >= 0 && ParseCommaSeparator(aString) && + ParseInteger(aString, w) && w > 0 && ParseCommaSeparator(aString) && + ParseInteger(aString, h) && h > 0 && aString.Length() == 0) { + // Reject invalid percentage coordinates. + if (clipUnit == eClipUnit_Percent && (x + w > 100 || y + h > 100)) { + return false; + } + + mClip.emplace(x, y, w, h); + mClipUnit = clipUnit; + return true; + } + + return false; +} + +void nsMediaFragmentURIParser::Parse(nsACString& aRef) { + // Create an array of possibly-invalid media fragments. + nsTArray<std::pair<nsCString, nsCString> > fragments; + + for (const nsACString& nv : nsCCharSeparatedTokenizer(aRef, '&').ToRange()) { + int32_t index = nv.FindChar('='); + if (index >= 0) { + nsAutoCString name; + nsAutoCString value; + NS_UnescapeURL(StringHead(nv, index), esc_Ref | esc_AlwaysCopy, name); + NS_UnescapeURL(Substring(nv, index + 1, nv.Length()), + esc_Ref | esc_AlwaysCopy, value); + fragments.AppendElement(make_pair(name, value)); + } + } + + // Parse the media fragment values. + bool gotTemporal = false, gotSpatial = false; + for (int i = fragments.Length() - 1; i >= 0; --i) { + if (gotTemporal && gotSpatial) { + // We've got one of each possible type. No need to look at the rest. + break; + } else if (!gotTemporal && fragments[i].first.EqualsLiteral("t")) { + nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second); + gotTemporal = ParseNPT(nsDependentSubstring(value, 0)); + } else if (!gotSpatial && fragments[i].first.EqualsLiteral("xywh")) { + nsAutoString value = NS_ConvertUTF8toUTF16(fragments[i].second); + gotSpatial = ParseXYWH(nsDependentSubstring(value, 0)); + } + } +} + +} // namespace net +} // namespace mozilla |