15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2004, 2006, 2008, 2010 Apple Inc. All rights reserved.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * are met:
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 1. Redistributions of source code must retain the above copyright
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    notice, this list of conditions and the following disclaimer.
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 2. Redistributions in binary form must reproduce the above copyright
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    notice, this list of conditions and the following disclaimer in the
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *    documentation and/or other materials provided with the distribution.
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2302772c6a72f1ee0b226341a4f4439970c29fc861Ben Murdoch * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h"
2781a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)#include "wtf/text/TextCodecUTF16.h"
285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
29591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "wtf/PassOwnPtr.h"
30591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "wtf/text/CString.h"
31591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "wtf/text/StringBuffer.h"
32591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch#include "wtf/text/WTFString.h"
33d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)#include "wtf/unicode/CharacterNames.h"
345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)using namespace std;
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
3781a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)namespace WTF {
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void TextCodecUTF16::registerEncodingNames(EncodingNameRegistrar registrar)
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("UTF-16LE", "UTF-16LE");
425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("UTF-16BE", "UTF-16BE");
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("ISO-10646-UCS-2", "UTF-16LE");
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("UCS-2", "UTF-16LE");
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("UTF-16", "UTF-16LE");
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("Unicode", "UTF-16LE");
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("csUnicode", "UTF-16LE");
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("unicodeFEFF", "UTF-16LE");
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("unicodeFFFE", "UTF-16BE");
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static PassOwnPtr<TextCodec> newStreamingTextDecoderUTF16LE(const TextEncoding&, const void*)
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return adoptPtr(new TextCodecUTF16(true));
575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)static PassOwnPtr<TextCodec> newStreamingTextDecoderUTF16BE(const TextEncoding&, const void*)
605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return adoptPtr(new TextCodecUTF16(false));
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void TextCodecUTF16::registerCodecs(TextCodecRegistrar registrar)
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("UTF-16LE", newStreamingTextDecoderUTF16LE, 0);
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    registrar("UTF-16BE", newStreamingTextDecoderUTF16BE, 0);
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
70d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)String TextCodecUTF16::decode(const char* bytes, size_t length, FlushBehavior flush, bool, bool& sawError)
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
72d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    // For compatibility reasons, ignore flush from fetch EOF.
73d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    const bool reallyFlush = flush != DoNotFlush && flush != FetchEOF;
74d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
75d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    if (!length) {
76d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        if (!reallyFlush || !m_haveBufferedByte)
77d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)            return String();
78d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        sawError = true;
79d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        return String(&Unicode::replacementCharacter, 1);
80d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    }
815c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
825c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // FIXME: This should generate an error if there is an unpaired surrogate.
835c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const unsigned char* p = reinterpret_cast<const unsigned char*>(bytes);
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    size_t numBytes = length + m_haveBufferedByte;
86d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    size_t numCharsIn = numBytes / 2;
87d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    size_t numCharsOut = ((numBytes & 1) && reallyFlush) ? numCharsIn + 1 : numCharsIn;
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
89d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)    StringBuffer<UChar> buffer(numCharsOut);
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    UChar* q = buffer.characters();
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_haveBufferedByte) {
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        UChar c;
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (m_littleEndian)
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            c = m_bufferedByte | (p[0] << 8);
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        else
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            c = (m_bufferedByte << 8) | p[0];
985c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        *q++ = c;
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        m_haveBufferedByte = false;
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        p += 1;
101d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        numCharsIn -= 1;
1025c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_littleEndian) {
105d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        for (size_t i = 0; i < numCharsIn; ++i) {
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            UChar c = p[0] | (p[1] << 8);
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            p += 2;
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            *q++ = c;
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
111d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        for (size_t i = 0; i < numCharsIn; ++i) {
1125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            UChar c = (p[0] << 8) | p[1];
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            p += 2;
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            *q++ = c;
1155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (numBytes & 1) {
1195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        ASSERT(!m_haveBufferedByte);
120d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)
121d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        if (reallyFlush) {
122d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)            sawError = true;
123d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)            *q++ = Unicode::replacementCharacter;
124d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        } else {
125d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)            m_haveBufferedByte = true;
126d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)            m_bufferedByte = p[0];
127d5428f32f5d1719f774f62e19147104ca245a3abTorne (Richard Coles)        }
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1305c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    buffer.shrink(q - buffer.characters());
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    return String::adopt(buffer);
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)CString TextCodecUTF16::encode(const UChar* characters, size_t length, UnencodableHandling)
1365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1375c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // We need to be sure we can double the length without overflowing.
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // Since the passed-in length is the length of an actual existing
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // character buffer, each character is two bytes, and we know
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // the buffer doesn't occupy the entire address space, we can
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // assert here that doubling the length does not overflow size_t
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // and there's no need for a runtime check.
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    ASSERT(length <= numeric_limits<size_t>::max() / 2);
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    char* bytes;
146591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    CString result = CString::newUninitialized(length * 2, bytes);
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // FIXME: CString is not a reasonable data structure for encoded UTF-16, which will have
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // null characters inside it. Perhaps the result of encode should not be a CString.
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (m_littleEndian) {
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (size_t i = 0; i < length; ++i) {
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            UChar c = characters[i];
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bytes[i * 2] = c;
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bytes[i * 2 + 1] = c >> 8;
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
1575c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        for (size_t i = 0; i < length; ++i) {
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            UChar c = characters[i];
1595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bytes[i * 2] = c >> 8;
1605c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bytes[i * 2 + 1] = c;
1615c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
164591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    return result;
165591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch}
166591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch
167591b958dee2cf159d33a0b931e6231072eaf38d5Ben MurdochCString TextCodecUTF16::encode(const LChar* characters, size_t length, UnencodableHandling)
168591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch{
169e69819bd8e388ea4ad1636a19aa6b2eed4952191Ben Murdoch    // In the LChar case, we do actually need to perform this check in release.  :)
170591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    RELEASE_ASSERT(length <= numeric_limits<size_t>::max() / 2);
171591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch
172591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    char* bytes;
173591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    CString result = CString::newUninitialized(length * 2, bytes);
174591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch
175591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    if (m_littleEndian) {
176591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch        for (size_t i = 0; i < length; ++i) {
177591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch            bytes[i * 2] = characters[i];
178591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch            bytes[i * 2 + 1] = 0;
179591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch        }
180591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    } else {
181591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch        for (size_t i = 0; i < length; ++i) {
182591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch            bytes[i * 2] = 0;
183591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch            bytes[i * 2 + 1] = characters[i];
184591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch        }
185591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    }
186591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch
187591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch    return result;
1885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
19081a5157921f1d2a7ff6aae115bfe3c139b38a5c8Torne (Richard Coles)} // namespace WTF
191