diff options
author | sgk@google.com <sgk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-14 12:50:31 +0000 |
---|---|---|
committer | sgk@google.com <sgk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-10-14 12:50:31 +0000 |
commit | f6c135caf66962d03f769d1255c62496f0c147c0 (patch) | |
tree | dc7aee39e98e7513bb2499f28a28ab56ee467b65 /webkit/pending | |
parent | 28964759b5c023b457ca6c8bd96a7e10e5bc5852 (diff) | |
download | chromium_src-f6c135caf66962d03f769d1255c62496f0c147c0.zip chromium_src-f6c135caf66962d03f769d1255c62496f0c147c0.tar.gz chromium_src-f6c135caf66962d03f769d1255c62496f0c147c0.tar.bz2 |
Remove no-longer-referenced webkit\pending files.
Review URL: http://codereview.chromium.org/7132
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@3342 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/pending')
32 files changed, 0 insertions, 14771 deletions
diff --git a/webkit/pending/GIFImageDecoder.cpp b/webkit/pending/GIFImageDecoder.cpp deleted file mode 100644 index d350d2c..0000000 --- a/webkit/pending/GIFImageDecoder.cpp +++ /dev/null @@ -1,417 +0,0 @@ -/* - * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "GIFImageDecoder.h" -#include "GIFImageReader.h" - -#if PLATFORM(CAIRO) || PLATFORM(QT) || PLATFORM(WX) - -namespace WebCore { - -class GIFImageDecoderPrivate -{ -public: - GIFImageDecoderPrivate(GIFImageDecoder* decoder = 0) - : m_reader(decoder) - { - m_readOffset = 0; - } - - ~GIFImageDecoderPrivate() - { - m_reader.close(); - } - - bool decode(SharedBuffer* data, - GIFImageDecoder::GIFQuery query = GIFImageDecoder::GIFFullQuery, - unsigned int haltFrame = -1) - { - return m_reader.read((const unsigned char*)data->data() + m_readOffset, data->size() - m_readOffset, - query, - haltFrame); - } - - unsigned frameCount() const { return m_reader.images_count; } - int repetitionCount() const { return m_reader.loop_count; } - - void setReadOffset(unsigned o) { m_readOffset = o; } - - bool isTransparent() const { return m_reader.frame_reader->is_transparent; } - - void getColorMap(unsigned char*& map, unsigned& size) const { - if (m_reader.frame_reader->is_local_colormap_defined) { - map = m_reader.frame_reader->local_colormap; - size = (unsigned)m_reader.frame_reader->local_colormap_size; - } else { - map = m_reader.global_colormap; - size = m_reader.global_colormap_size; - } - } - - unsigned frameXOffset() const { return m_reader.frame_reader->x_offset; } - unsigned frameYOffset() const { return m_reader.frame_reader->y_offset; } - unsigned frameWidth() const { return m_reader.frame_reader->width; } - unsigned frameHeight() const { return m_reader.frame_reader->height; } - - int transparentPixel() const { return m_reader.frame_reader->tpixel; } - - unsigned duration() const { return m_reader.frame_reader->delay_time; } - -private: - GIFImageReader m_reader; - unsigned m_readOffset; -}; - -GIFImageDecoder::GIFImageDecoder() -: m_frameCountValid(true), m_repetitionCount(cAnimationLoopOnce), m_reader(0) -{} - -GIFImageDecoder::~GIFImageDecoder() -{ - delete m_reader; -} - -// Take the data and store it. -void GIFImageDecoder::setData(SharedBuffer* data, bool allDataReceived) -{ - if (m_failed) - return; - - // Cache our new data. - ImageDecoder::setData(data, allDataReceived); - - // Our frame count is now unknown. - m_frameCountValid = false; - - // Create the GIF reader. - if (!m_reader && !m_failed) - m_reader = new GIFImageDecoderPrivate(this); -} - -// Whether or not the size information has been decoded yet. -bool GIFImageDecoder::isSizeAvailable() const -{ - // If we have pending data to decode, send it to the GIF reader now. - if (!m_sizeAvailable && m_reader) { - if (m_failed) - return false; - - // The decoder will go ahead and aggressively consume everything up until the first - // size is encountered. - decode(GIFSizeQuery, 0); - } - - return m_sizeAvailable; -} - -// The total number of frames for the image. Will scan the image data for the answer -// (without necessarily decoding all of the individual frames). -int GIFImageDecoder::frameCount() -{ - // If the decoder had an earlier error, we will just return what we had decoded - // so far. - if (!m_frameCountValid) { - // FIXME: Scanning all the data has O(n^2) behavior if the data were to come in really - // slowly. Might be interesting to try to clone our existing read session to preserve - // state, but for now we just crawl all the data. Note that this is no worse than what - // ImageIO does on Mac right now (it also crawls all the data again). - GIFImageDecoderPrivate reader; - reader.decode(m_data.get(), GIFFrameCountQuery); - m_frameCountValid = true; - m_frameBufferCache.resize(reader.frameCount()); - } - - return m_frameBufferCache.size(); -} - -// The number of repetitions to perform for an animation loop. -int GIFImageDecoder::repetitionCount() const -{ - // This value can arrive at any point in the image data stream. Most GIFs - // in the wild declare it near the beginning of the file, so it usually is - // set by the time we've decoded the size, but (depending on the GIF and the - // packets sent back by the webserver) not always. Our caller is - // responsible for waiting until image decoding has finished to ask this if - // it needs an authoritative answer. In the meantime, we should default to - // "loop once", both in the reader and here. - if (m_reader) - m_repetitionCount = m_reader->repetitionCount(); - return m_repetitionCount; -} - -RGBA32Buffer* GIFImageDecoder::frameBufferAtIndex(size_t index) -{ - if (index >= frameCount()) - return 0; - - RGBA32Buffer& frame = m_frameBufferCache[index]; - if (frame.status() != RGBA32Buffer::FrameComplete && m_reader) - // Decode this frame. - decode(GIFFullQuery, index+1); - return &frame; -} - -// Feed data to the GIF reader. -void GIFImageDecoder::decode(GIFQuery query, unsigned haltAtFrame) const -{ - if (m_failed) - return; - - m_failed = !m_reader->decode(m_data.get(), query, haltAtFrame); - - if (m_failed) { - delete m_reader; - m_reader = 0; - } -} - -// Callbacks from the GIF reader. -void GIFImageDecoder::sizeNowAvailable(unsigned width, unsigned height) -{ - m_size = IntSize(width, height); - m_sizeAvailable = true; -} - -void GIFImageDecoder::decodingHalted(unsigned bytesLeft) -{ - m_reader->setReadOffset(m_data->size() - bytesLeft); -} - -void GIFImageDecoder::initFrameBuffer(unsigned frameIndex) -{ - // Initialize the frame rect in our buffer. - IntRect frameRect(m_reader->frameXOffset(), m_reader->frameYOffset(), - m_reader->frameWidth(), m_reader->frameHeight()); - - // Make sure the frameRect doesn't extend past the bottom-right of the buffer. - if (frameRect.right() > m_size.width()) - frameRect.setWidth(m_size.width() - m_reader->frameXOffset()); - if (frameRect.bottom() > m_size.height()) - frameRect.setHeight(m_size.height() - m_reader->frameYOffset()); - - RGBA32Buffer* const buffer = &m_frameBufferCache[frameIndex]; - buffer->setRect(frameRect); - - if (frameIndex == 0) { - // This is the first frame, so we're not relying on any previous data. - prepEmptyFrameBuffer(buffer); - } else { - // The starting state for this frame depends on the previous frame's - // disposal method. - // - // Frames that use the DisposeOverwritePrevious method are effectively - // no-ops in terms of changing the starting state of a frame compared to - // the starting state of the previous frame, so skip over them. (If the - // first frame specifies this method, it will get treated like - // DisposeOverwriteBgcolor below and reset to a completely empty image.) - const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; - RGBA32Buffer::FrameDisposalMethod prevMethod = - prevBuffer->disposalMethod(); - while ((frameIndex > 0) && - (prevMethod == RGBA32Buffer::DisposeOverwritePrevious)) { - prevBuffer = &m_frameBufferCache[--frameIndex]; - prevMethod = prevBuffer->disposalMethod(); - } - - if ((prevMethod == RGBA32Buffer::DisposeNotSpecified) || - (prevMethod == RGBA32Buffer::DisposeKeep)) { - // Preserve the last frame as the starting state for this frame. - buffer->copyBitmapData(*prevBuffer); - // This next line isn't currently necessary since the alpha state is - // currently carried along in the Skia bitmap data, but it's safe, - // future-proof, and parallel to the Cairo code. - buffer->setHasAlpha(prevBuffer->hasAlpha()); - } else { - // We want to clear the previous frame to transparent, without - // affecting pixels in the image outside of the frame. - const IntRect& prevRect = prevBuffer->rect(); - if ((frameIndex == 0) || - prevRect.contains(IntRect(IntPoint(0, 0), m_size))) { - // Clearing the first frame, or a frame the size of the whole - // image, results in a completely empty image. - prepEmptyFrameBuffer(buffer); - } else { - // Copy the whole previous buffer, then clear just its frame. - buffer->copyBitmapData(*prevBuffer); - // Unnecessary (but safe); see comments on the similar call above. - buffer->setHasAlpha(prevBuffer->hasAlpha()); - SkBitmap& bitmap = buffer->bitmap(); - for (int y = prevRect.y(); y < prevRect.bottom(); ++y) { - for (int x = prevRect.x(); x < prevRect.right(); ++x) - buffer->setRGBA(bitmap.getAddr32(x, y), 0, 0, 0, 0); - } - if ((prevRect.width() > 0) && (prevRect.height() > 0)) - buffer->setHasAlpha(true); - } - } - } - - // Update our status to be partially complete. - buffer->setStatus(RGBA32Buffer::FramePartial); - - // Reset the alpha pixel tracker for this frame. - m_currentBufferSawAlpha = false; -} - -void GIFImageDecoder::prepEmptyFrameBuffer(RGBA32Buffer* buffer) const -{ - buffer->setSize(m_size.width(), m_size.height()); - buffer->bitmap().eraseARGB(0, 0, 0, 0); - // This next line isn't currently necessary since Skia's eraseARGB() sets - // this for us, but we do it for similar reasons to the setHasAlpha() calls - // in initFrameBuffer() above. - buffer->setHasAlpha(true); -} - -void GIFImageDecoder::haveDecodedRow(unsigned frameIndex, - unsigned char* rowBuffer, // Pointer to single scanline temporary buffer - unsigned char* rowEnd, - unsigned rowNumber, // The row index - unsigned repeatCount, // How many times to repeat the row - bool writeTransparentPixels) -{ - // Initialize the frame if necessary. - RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; - if (buffer.status() == RGBA32Buffer::FrameEmpty) - initFrameBuffer(frameIndex); - - // Do nothing for bogus data. - if (rowBuffer == 0 || static_cast<int>(m_reader->frameYOffset() + rowNumber) >= m_size.height()) - return; - - unsigned colorMapSize; - unsigned char* colorMap; - m_reader->getColorMap(colorMap, colorMapSize); - if (!colorMap) - return; - - // The buffers that we draw are the entire image's width and height, so a final output frame is - // width * height RGBA32 values in size. - // - // A single GIF frame, however, can be smaller than the entire image, i.e., it can represent some sub-rectangle - // within the overall image. The rows we are decoding are within this - // sub-rectangle. This means that if the GIF frame's sub-rectangle is (x,y,w,h) then row 0 is really row - // y, and each row goes from x to x+w. - unsigned dstPos = (m_reader->frameYOffset() + rowNumber) * m_size.width() + m_reader->frameXOffset(); - unsigned* dst = buffer.bitmap().getAddr32(0, 0) + dstPos; - unsigned* dstEnd = dst + m_size.width() - m_reader->frameXOffset(); - unsigned* currDst = dst; - unsigned char* currentRowByte = rowBuffer; - - while (currentRowByte != rowEnd && currDst < dstEnd) { - if ((!m_reader->isTransparent() || *currentRowByte != m_reader->transparentPixel()) && *currentRowByte < colorMapSize) { - unsigned colorIndex = *currentRowByte * 3; - unsigned red = colorMap[colorIndex]; - unsigned green = colorMap[colorIndex + 1]; - unsigned blue = colorMap[colorIndex + 2]; - RGBA32Buffer::setRGBA(currDst, red, green, blue, 255); - } else { - m_currentBufferSawAlpha = true; - // We may or may not need to write transparent pixels to the buffer. - // If we're compositing against a previous image, it's wrong, and if - // we're writing atop a cleared, fully transparent buffer, it's - // unnecessary; but if we're decoding an interlaced gif and - // displaying it "Haeberli"-style, we must write these for passes - // beyond the first, or the initial passes will "show through" the - // later ones. - if (writeTransparentPixels) - RGBA32Buffer::setRGBA(currDst, 0, 0, 0, 0); - } - currDst++; - currentRowByte++; - } - - if (repeatCount > 1) { - // Copy the row |repeatCount|-1 times. - unsigned num = currDst - dst; - unsigned size = num * sizeof(unsigned); - unsigned width = m_size.width(); - unsigned* end = buffer.bitmap().getAddr32(0, 0) + width * m_size.height(); - currDst = dst + width; - for (unsigned i = 1; i < repeatCount; i++) { - if (currDst + num > end) // Protect against a buffer overrun from a bogus repeatCount. - break; - memcpy(currDst, dst, size); - currDst += width; - } - } -} - -void GIFImageDecoder::frameComplete(unsigned frameIndex, unsigned frameDuration, RGBA32Buffer::FrameDisposalMethod disposalMethod) -{ - RGBA32Buffer& buffer = m_frameBufferCache[frameIndex]; - buffer.setStatus(RGBA32Buffer::FrameComplete); - buffer.setDuration(frameDuration); - buffer.setDisposalMethod(disposalMethod); - - if (!m_currentBufferSawAlpha) { - // The whole frame was non-transparent, so it's possible that the entire - // resulting buffer was non-transparent, and we can setHasAlpha(false). - if (buffer.rect().contains(IntRect(IntPoint(0, 0), m_size))) { - buffer.setHasAlpha(false); - } else if (frameIndex > 0) { - // Tricky case. This frame does not have alpha only if everywhere - // outside its rect doesn't have alpha. To know whether this is - // true, we check the start state of the frame -- if it doesn't have - // alpha, we're safe. - // - // First skip over prior DisposeOverwritePrevious frames (since they - // don't affect the start state of this frame) the same way we do in - // initFrameBuffer(). - const RGBA32Buffer* prevBuffer = &m_frameBufferCache[--frameIndex]; - while ((frameIndex > 0) && - (prevBuffer->disposalMethod() == - RGBA32Buffer::DisposeOverwritePrevious)) - prevBuffer = &m_frameBufferCache[--frameIndex]; - - // Now, if we're at a DisposeNotSpecified or DisposeKeep frame, then - // we can say we have no alpha if that frame had no alpha. But - // since in initFrameBuffer() we already copied that frame's alpha - // state into the current frame's, we need do nothing at all here. - // - // The only remaining case is a DisposeOverwriteBgcolor frame. If - // it had no alpha, and its rect is contained in the current frame's - // rect, we know the current frame has no alpha. - if ((prevBuffer->disposalMethod() == - RGBA32Buffer::DisposeOverwriteBgcolor) && - !prevBuffer->hasAlpha() && - buffer.rect().contains(prevBuffer->rect())) - buffer.setHasAlpha(false); - } - } -} - -void GIFImageDecoder::gifComplete() -{ - if (m_reader) - m_repetitionCount = m_reader->repetitionCount(); - delete m_reader; - m_reader = 0; -} - -} - -#endif // PLATFORM(CAIRO) diff --git a/webkit/pending/KURL.cpp b/webkit/pending/KURL.cpp deleted file mode 100644 index 00166ff..0000000 --- a/webkit/pending/KURL.cpp +++ /dev/null @@ -1,1606 +0,0 @@ -/* - * Copyright (C) 2004, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "KURL.h" - -#ifndef USE_GOOGLE_URL_LIBRARY - -#include "CString.h" -#include "PlatformString.h" -#include "TextEncoding.h" - -#if USE(ICU_UNICODE) -#include <unicode/uidna.h> -#elif USE(QT4_UNICODE) -#include <QUrl> -#endif - -#include <stdio.h> - -using namespace std; -using namespace WTF; - -namespace WebCore { - -typedef Vector<char, 512> CharBuffer; -typedef Vector<UChar, 512> UCharBuffer; - -// FIXME: This file makes too much use of the + operator on String. -// We either have to optimize that operator so it doesn't involve -// so many allocations, or change this to use Vector<UChar> instead. - -enum URLCharacterClasses { - // alpha - SchemeFirstChar = 1 << 0, - - // ( alpha | digit | "+" | "-" | "." ) - SchemeChar = 1 << 1, - - // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | "(" | ")" - // unreserved = alphanum | mark - // ( unreserved | escaped | ";" | ":" | "&" | "=" | "+" | "$" | "," ) - UserInfoChar = 1 << 2, - - // alnum | "." | "-" | "%" - // The above is what the specification says, but we are lenient to - // match existing practice and also allow: - // "_" - HostnameChar = 1 << 3, - - // hexdigit | ":" | "%" - IPv6Char = 1 << 4, - - // "#" | "?" | "/" | nul - PathSegmentEndChar = 1 << 5, - - // not allowed in path - BadChar = 1 << 6 -}; - -static const char hexDigits[17] = "0123456789ABCDEF"; - -static const unsigned char characterClassTable[256] = { - /* 0 nul */ PathSegmentEndChar, /* 1 soh */ BadChar, - /* 2 stx */ BadChar, /* 3 etx */ BadChar, - /* 4 eot */ BadChar, /* 5 enq */ BadChar, /* 6 ack */ BadChar, /* 7 bel */ BadChar, - /* 8 bs */ BadChar, /* 9 ht */ BadChar, /* 10 nl */ BadChar, /* 11 vt */ BadChar, - /* 12 np */ BadChar, /* 13 cr */ BadChar, /* 14 so */ BadChar, /* 15 si */ BadChar, - /* 16 dle */ BadChar, /* 17 dc1 */ BadChar, /* 18 dc2 */ BadChar, /* 19 dc3 */ BadChar, - /* 20 dc4 */ BadChar, /* 21 nak */ BadChar, /* 22 syn */ BadChar, /* 23 etb */ BadChar, - /* 24 can */ BadChar, /* 25 em */ BadChar, /* 26 sub */ BadChar, /* 27 esc */ BadChar, - /* 28 fs */ BadChar, /* 29 gs */ BadChar, /* 30 rs */ BadChar, /* 31 us */ BadChar, - /* 32 sp */ BadChar, /* 33 ! */ UserInfoChar, - /* 34 " */ BadChar, /* 35 # */ PathSegmentEndChar | BadChar, - /* 36 $ */ UserInfoChar, /* 37 % */ UserInfoChar | HostnameChar | IPv6Char | BadChar, - /* 38 & */ UserInfoChar, /* 39 ' */ UserInfoChar, - /* 40 ( */ UserInfoChar, /* 41 ) */ UserInfoChar, - /* 42 * */ UserInfoChar, /* 43 + */ SchemeChar | UserInfoChar, - /* 44 , */ UserInfoChar, - /* 45 - */ SchemeChar | UserInfoChar | HostnameChar, - /* 46 . */ SchemeChar | UserInfoChar | HostnameChar, - /* 47 / */ PathSegmentEndChar, - /* 48 0 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 49 1 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 50 2 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 51 3 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 52 4 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 53 5 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 54 6 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 55 7 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 56 8 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 57 9 */ SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 58 : */ UserInfoChar | IPv6Char, /* 59 ; */ UserInfoChar, - /* 60 < */ BadChar, /* 61 = */ UserInfoChar, - /* 62 > */ BadChar, /* 63 ? */ PathSegmentEndChar | BadChar, - /* 64 @ */ 0, - /* 65 A */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 66 B */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 67 C */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 68 D */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 69 E */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 70 F */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 71 G */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 72 H */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 73 I */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 74 J */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 75 K */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 76 L */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 77 M */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 78 N */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 79 O */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 80 P */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 81 Q */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 82 R */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 83 S */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 84 T */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 85 U */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 86 V */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 87 W */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 88 X */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 89 Y */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 90 Z */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 91 [ */ 0, - /* 92 \ */ 0, /* 93 ] */ 0, - /* 94 ^ */ 0, - /* 95 _ */ UserInfoChar | HostnameChar, - /* 96 ` */ 0, - /* 97 a */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 98 b */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 99 c */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 100 d */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 101 e */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 102 f */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar | IPv6Char, - /* 103 g */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 104 h */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 105 i */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 106 j */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 107 k */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 108 l */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 109 m */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 110 n */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 111 o */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 112 p */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 113 q */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 114 r */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 115 s */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 116 t */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 117 u */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 118 v */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 119 w */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 120 x */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 121 y */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 122 z */ SchemeFirstChar | SchemeChar | UserInfoChar | HostnameChar, - /* 123 { */ 0, - /* 124 | */ 0, /* 125 } */ 0, /* 126 ~ */ UserInfoChar, /* 127 del */ BadChar, - /* 128 */ BadChar, /* 129 */ BadChar, /* 130 */ BadChar, /* 131 */ BadChar, - /* 132 */ BadChar, /* 133 */ BadChar, /* 134 */ BadChar, /* 135 */ BadChar, - /* 136 */ BadChar, /* 137 */ BadChar, /* 138 */ BadChar, /* 139 */ BadChar, - /* 140 */ BadChar, /* 141 */ BadChar, /* 142 */ BadChar, /* 143 */ BadChar, - /* 144 */ BadChar, /* 145 */ BadChar, /* 146 */ BadChar, /* 147 */ BadChar, - /* 148 */ BadChar, /* 149 */ BadChar, /* 150 */ BadChar, /* 151 */ BadChar, - /* 152 */ BadChar, /* 153 */ BadChar, /* 154 */ BadChar, /* 155 */ BadChar, - /* 156 */ BadChar, /* 157 */ BadChar, /* 158 */ BadChar, /* 159 */ BadChar, - /* 160 */ BadChar, /* 161 */ BadChar, /* 162 */ BadChar, /* 163 */ BadChar, - /* 164 */ BadChar, /* 165 */ BadChar, /* 166 */ BadChar, /* 167 */ BadChar, - /* 168 */ BadChar, /* 169 */ BadChar, /* 170 */ BadChar, /* 171 */ BadChar, - /* 172 */ BadChar, /* 173 */ BadChar, /* 174 */ BadChar, /* 175 */ BadChar, - /* 176 */ BadChar, /* 177 */ BadChar, /* 178 */ BadChar, /* 179 */ BadChar, - /* 180 */ BadChar, /* 181 */ BadChar, /* 182 */ BadChar, /* 183 */ BadChar, - /* 184 */ BadChar, /* 185 */ BadChar, /* 186 */ BadChar, /* 187 */ BadChar, - /* 188 */ BadChar, /* 189 */ BadChar, /* 190 */ BadChar, /* 191 */ BadChar, - /* 192 */ BadChar, /* 193 */ BadChar, /* 194 */ BadChar, /* 195 */ BadChar, - /* 196 */ BadChar, /* 197 */ BadChar, /* 198 */ BadChar, /* 199 */ BadChar, - /* 200 */ BadChar, /* 201 */ BadChar, /* 202 */ BadChar, /* 203 */ BadChar, - /* 204 */ BadChar, /* 205 */ BadChar, /* 206 */ BadChar, /* 207 */ BadChar, - /* 208 */ BadChar, /* 209 */ BadChar, /* 210 */ BadChar, /* 211 */ BadChar, - /* 212 */ BadChar, /* 213 */ BadChar, /* 214 */ BadChar, /* 215 */ BadChar, - /* 216 */ BadChar, /* 217 */ BadChar, /* 218 */ BadChar, /* 219 */ BadChar, - /* 220 */ BadChar, /* 221 */ BadChar, /* 222 */ BadChar, /* 223 */ BadChar, - /* 224 */ BadChar, /* 225 */ BadChar, /* 226 */ BadChar, /* 227 */ BadChar, - /* 228 */ BadChar, /* 229 */ BadChar, /* 230 */ BadChar, /* 231 */ BadChar, - /* 232 */ BadChar, /* 233 */ BadChar, /* 234 */ BadChar, /* 235 */ BadChar, - /* 236 */ BadChar, /* 237 */ BadChar, /* 238 */ BadChar, /* 239 */ BadChar, - /* 240 */ BadChar, /* 241 */ BadChar, /* 242 */ BadChar, /* 243 */ BadChar, - /* 244 */ BadChar, /* 245 */ BadChar, /* 246 */ BadChar, /* 247 */ BadChar, - /* 248 */ BadChar, /* 249 */ BadChar, /* 250 */ BadChar, /* 251 */ BadChar, - /* 252 */ BadChar, /* 253 */ BadChar, /* 254 */ BadChar, /* 255 */ BadChar -}; - -static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int srcEnd); -static void encodeRelativeString(const String& rel, const TextEncoding&, CharBuffer& ouput); -static String substituteBackslashes(const String&); - -static inline bool isSchemeFirstChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & SchemeFirstChar; } -static inline bool isSchemeFirstChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeFirstChar); } -static inline bool isSchemeChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & SchemeChar; } -static inline bool isSchemeChar(UChar c) { return c <= 0xff && (characterClassTable[c] & SchemeChar); } -static inline bool isUserInfoChar(unsigned char c) { return characterClassTable[c] & UserInfoChar; } -static inline bool isHostnameChar(unsigned char c) { return characterClassTable[c] & HostnameChar; } -static inline bool isIPv6Char(unsigned char c) { return characterClassTable[c] & IPv6Char; } -static inline bool isPathSegmentEndChar(char c) { return characterClassTable[static_cast<unsigned char>(c)] & PathSegmentEndChar; } -static inline bool isPathSegmentEndChar(UChar c) { return c <= 0xff && (characterClassTable[c] & PathSegmentEndChar); } -static inline bool isBadChar(unsigned char c) { return characterClassTable[c] & BadChar; } - -static inline int hexDigitValue(UChar c) -{ - ASSERT(isASCIIHexDigit(c)); - if (c < 'A') - return c - '0'; - return (c - 'A' + 10) & 0xF; // handle both upper and lower case without a branch -} - -// Copies the source to the destination, assuming all the source characters are -// ASCII. The destination buffer must be large enough. Null characters are allowed -// in the source string, and no attempt is made to null-terminate the result. -static void copyASCII(const UChar* src, int length, char* dest) -{ - for (int i = 0; i < length; i++) - dest[i] = static_cast<char>(src[i]); -} - -// FIXME: Move to PlatformString.h eventually. -// Returns the index of the first index in string |s| of any of the characters -// in |toFind|. |toFind| should be a null-terminated string, all characters up -// to the null will be searched. Returns int if not found. -static int findFirstOf(const UChar* s, int sLen, int startPos, const char* toFind) -{ - for (int i = startPos; i < sLen; i++) { - const char* cur = toFind; - while (*cur) { - if (s[i] == *(cur++)) - return i; - } - } - return -1; -} - -#ifndef KURL_DECORATE_GLOBALS -inline bool KURL::protocolIs(const String& string, const char* protocol) -{ - return WebCore::protocolIs(string, protocol); -} -#endif - -void KURL::invalidate() -{ - m_isValid = false; - m_schemeEnd = 0; - m_userStart = 0; - m_userEnd = 0; - m_passwordEnd = 0; - m_hostEnd = 0; - m_portEnd = 0; - m_pathEnd = 0; - m_pathAfterLastSlash = 0; - m_queryEnd = 0; - m_fragmentEnd = 0; -} - -KURL::KURL(const char* url) -{ - if (!url || url[0] != '/') { - parse(url, 0); - return; - } - - size_t urlLength = strlen(url) + 1; - CharBuffer buffer(urlLength + 5); // 5 for "file:". - buffer[0] = 'f'; - buffer[1] = 'i'; - buffer[2] = 'l'; - buffer[3] = 'e'; - buffer[4] = ':'; - memcpy(&buffer[5], url, urlLength); - parse(buffer.data(), 0); -} - -KURL::KURL(const String& url) -{ - if (url[0] != '/') { - parse(url); - return; - } - - CharBuffer buffer(url.length() + 6); // 5 for "file:", 1 for terminator. - buffer[0] = 'f'; - buffer[1] = 'i'; - buffer[2] = 'l'; - buffer[3] = 'e'; - buffer[4] = ':'; - copyASCII(url.characters(), url.length(), &buffer[5]); - buffer[url.length() + 5] = '\0'; // Need null terminator. - - parse(buffer.data(), 0); -} - -KURL::KURL(const KURL& base, const String& relative) -{ - init(base, relative, UTF8Encoding()); -} - -KURL::KURL(const KURL& base, const String& relative, const TextEncoding& encoding) -{ - init(base, relative, encoding); -} - -void KURL::init(const KURL& base, const String& relative, const TextEncoding& encoding) -{ - // Allow resolutions with a null or empty base URL, but not with any other invalid one. - // FIXME: Is this a good rule? - if (!base.m_isValid && !base.isEmpty()) { - m_string = relative; - invalidate(); - return; - } - - // For compatibility with Win IE, treat backslashes as if they were slashes, - // as long as we're not dealing with javascript: or data: URLs. - String rel = relative; - if (rel.contains('\\') && !(protocolIs(rel, "javascript") || protocolIs(rel, "data"))) - rel = substituteBackslashes(rel); - - String* originalString = &rel; - - bool allASCII = charactersAreAllASCII(rel.characters(), rel.length()); - CharBuffer strBuffer; - char* str; - size_t len; - if (allASCII) { - len = rel.length(); - strBuffer.resize(len + 1); - copyASCII(rel.characters(), len, strBuffer.data()); - strBuffer[len] = 0; - str = strBuffer.data(); - } else { - originalString = 0; - encodeRelativeString(rel, encoding, strBuffer); - str = strBuffer.data(); - len = strlen(str); - } - - // Get rid of leading whitespace. - while (*str == ' ') { - originalString = 0; - str++; - --len; - } - - // Get rid of trailing whitespace. - while (len && str[len - 1] == ' ') { - originalString = 0; - str[--len] = '\0'; - } - - // According to the RFC, the reference should be interpreted as an - // absolute URI if possible, using the "leftmost, longest" - // algorithm. If the URI reference is absolute it will have a - // scheme, meaning that it will have a colon before the first - // non-scheme element. - bool absolute = false; - char* p = str; - if (isSchemeFirstChar(*p)) { - ++p; - while (isSchemeChar(*p)) { - ++p; - } - if (*p == ':') { - if (p[1] != '/' && equalIgnoringCase(base.protocol(), String(str, p - str)) && base.isHierarchical()) { - str = p + 1; - originalString = 0; - } else - absolute = true; - } - } - - if (absolute) { - parse(str, originalString); - } else { - // If the base is empty or opaque (e.g. data: or javascript:), then the URL is invalid - // unless the relative URL is a single fragment. - if (!base.isHierarchical()) { - if (str[0] == '#') - parse(base.m_string.left(base.m_queryEnd) + (allASCII ? String(str) : String::fromUTF8(str))); - else { - m_string = relative; - invalidate(); - } - return; - } - - switch (str[0]) { - case '\0': - // the reference must be empty - the RFC says this is a - // reference to the same document - *this = base; - break; - case '#': - // must be fragment-only reference - parse(base.m_string.left(base.m_queryEnd) + (allASCII ? String(str) : String::fromUTF8(str))); - break; - case '?': - // query-only reference, special case needed for non-URL results - parse(base.m_string.left(base.m_pathEnd) + (allASCII ? String(str) : String::fromUTF8(str))); - break; - case '/': - // must be net-path or absolute-path reference - if (str[1] == '/') { - // net-path - parse(base.m_string.left(base.m_schemeEnd + 1) + (allASCII ? String(str) : String::fromUTF8(str))); - } else { - // abs-path - parse(base.m_string.left(base.m_portEnd) + (allASCII ? String(str) : String::fromUTF8(str))); - } - break; - default: - { - // must be relative-path reference - - // Base part plus relative part plus one possible slash added in between plus terminating \0 byte. - CharBuffer buffer(base.m_pathEnd + 1 + len + 1); - - char* bufferPos = buffer.data(); - - // first copy everything before the path from the base - unsigned baseLength = base.m_string.length(); - const UChar* baseCharacters = base.m_string.characters(); - CharBuffer baseStringBuffer(baseLength); - for (unsigned i = 0; i < baseLength; ++i) - baseStringBuffer[i] = static_cast<char>(baseCharacters[i]); - const char* baseString = baseStringBuffer.data(); - const char* baseStringStart = baseString; - const char* pathStart = baseStringStart + base.m_portEnd; - while (baseStringStart < pathStart) - *bufferPos++ = *baseStringStart++; - char* bufferPathStart = bufferPos; - - // now copy the base path - const char* baseStringEnd = baseString + base.m_pathEnd; - - // go back to the last slash - while (baseStringEnd > baseStringStart && baseStringEnd[-1] != '/') - baseStringEnd--; - - if (baseStringEnd == baseStringStart) { - // no path in base, add a path separator if necessary - if (base.m_schemeEnd + 1 != base.m_pathEnd && *str && *str != '?' && *str != '#') - *bufferPos++ = '/'; - } else { - bufferPos += copyPathRemovingDots(bufferPos, baseStringStart, 0, baseStringEnd - baseStringStart); - } - - const char* relStringStart = str; - const char* relStringPos = relStringStart; - - while (*relStringPos && *relStringPos != '?' && *relStringPos != '#') { - if (relStringPos[0] == '.' && bufferPos[-1] == '/') { - if (isPathSegmentEndChar(relStringPos[1])) { - // skip over "." segment - relStringPos += 1; - if (relStringPos[0] == '/') - relStringPos++; - continue; - } else if (relStringPos[1] == '.' && isPathSegmentEndChar(relStringPos[2])) { - // skip over ".." segment and rewind the last segment - // the RFC leaves it up to the app to decide what to do with excess - // ".." segments - we choose to drop them since some web content - // relies on this. - relStringPos += 2; - if (relStringPos[0] == '/') - relStringPos++; - if (bufferPos > bufferPathStart + 1) - bufferPos--; - while (bufferPos > bufferPathStart + 1 && bufferPos[-1] != '/') - bufferPos--; - continue; - } - } - - *bufferPos = *relStringPos; - relStringPos++; - bufferPos++; - } - - // all done with the path work, now copy any remainder - // of the relative reference; this will also add a null terminator - strcpy(bufferPos, relStringPos); - - parse(buffer.data(), 0); - - ASSERT(strlen(buffer.data()) + 1 <= buffer.size()); - break; - } - } - } -} - -bool KURL::hasPath() const -{ - return m_pathEnd != m_portEnd; -} - -String KURL::lastPathComponent() const -{ - if (!hasPath()) - return String(); - - int end = m_pathEnd - 1; - if (m_string[end] == '/') - --end; - - int start = m_string.reverseFind('/', end); - if (start < m_portEnd) - return String(); - ++start; - - return m_string.substring(start, end - start + 1); -} - -String KURL::protocol() const -{ - return m_string.left(m_schemeEnd); -} - -String KURL::host() const -{ - int start = hostStart(); - return decodeURLEscapeSequences(m_string.substring(start, m_hostEnd - start)); -} - -unsigned short KURL::port() const -{ - if (m_hostEnd == m_portEnd) - return 0; - - int number = m_string.substring(m_hostEnd + 1, m_portEnd - m_hostEnd - 1).toInt(); - if (number < 0 || number > 0xFFFF) - return 0; - return number; -} - -String KURL::pass() const -{ - if (m_passwordEnd == m_userEnd) - return String(); - - return decodeURLEscapeSequences(m_string.substring(m_userEnd + 1, m_passwordEnd - m_userEnd - 1)); -} - -String KURL::user() const -{ - return decodeURLEscapeSequences(m_string.substring(m_userStart, m_userEnd - m_userStart)); -} - -String KURL::ref() const -{ - if (m_fragmentEnd == m_queryEnd) - return String(); - - return m_string.substring(m_queryEnd + 1, m_fragmentEnd - (m_queryEnd + 1)); -} - -bool KURL::hasRef() const -{ - return m_fragmentEnd != m_queryEnd; -} - -static inline void assertProtocolIsGood(const char* protocol) -{ -#ifndef NDEBUG - const char* p = protocol; - while (*p) { - ASSERT(*p > ' ' && *p < 0x7F && !(*p >= 'A' && *p <= 'Z')); - ++p; - } -#endif -} - -bool KURL::protocolIs(const char* protocol) const -{ - // Do the comparison without making a new string object. - assertProtocolIsGood(protocol); - if (!m_isValid) - return false; - for (int i = 0; i < m_schemeEnd; ++i) { - if (!protocol[i] || toASCIILower(m_string[i]) != protocol[i]) - return false; - } - return !protocol[m_schemeEnd]; // We should have consumed all characters in the argument. -} - -String KURL::query() const -{ - return m_string.substring(m_pathEnd, m_queryEnd - m_pathEnd); -} - -String KURL::path() const -{ - return decodeURLEscapeSequences(m_string.substring(m_portEnd, m_pathEnd - m_portEnd)); -} - -void KURL::setProtocol(const String& s) -{ - if (!m_isValid) { - parse(s + ":" + m_string); - return; - } - - parse(s + m_string.substring(m_schemeEnd)); -} - -void KURL::setHost(const String& s) -{ - if (!m_isValid) - return; - - bool slashSlashNeeded = m_userStart == m_schemeEnd + 1; - - parse(m_string.left(hostStart()) + (slashSlashNeeded ? "//" : "") + s + m_string.substring(m_hostEnd)); -} - -void KURL::setPort(unsigned short i) -{ - if (!m_isValid) - return; - - bool colonNeeded = m_portEnd == m_hostEnd; - int portStart = (colonNeeded ? m_hostEnd : m_hostEnd + 1); - - parse(m_string.left(portStart) + (colonNeeded ? ":" : "") + String::number(i) + m_string.substring(m_portEnd)); -} - -void KURL::setHostAndPort(const String& hostAndPort) -{ - if (!m_isValid) - return; - - bool slashSlashNeeded = m_userStart == m_schemeEnd + 1; - - parse(m_string.left(hostStart()) + (slashSlashNeeded ? "//" : "") + hostAndPort + m_string.substring(m_portEnd)); -} - -void KURL::setUser(const String& user) -{ - if (!m_isValid) - return; - - String u; - int end = m_userEnd; - if (!user.isEmpty()) { - u = user; - if (m_userStart == m_schemeEnd + 1) - u = "//" + u; - // Add '@' if we didn't have one before. - if (end == m_hostEnd || (end == m_passwordEnd && m_string[end] != '@')) - u.append('@'); - } else { - // Remove '@' if we now have neither user nor password. - if (m_userEnd == m_passwordEnd && end != m_hostEnd && m_string[end] == '@') - end += 1; - } - parse(m_string.left(m_userStart) + u + m_string.substring(end)); -} - -void KURL::setPass(const String& password) -{ - if (!m_isValid) - return; - - String p; - int end = m_passwordEnd; - if (!password.isEmpty()) { - p = ":" + password + "@"; - if (m_userEnd == m_schemeEnd + 1) - p = "//" + p; - // Eat the existing '@' since we are going to add our own. - if (end != m_hostEnd && m_string[end] == '@') - end += 1; - } else { - // Remove '@' if we now have neither user nor password. - if (m_userStart == m_userEnd && end != m_hostEnd && m_string[end] == '@') - end += 1; - } - parse(m_string.left(m_userEnd) + p + m_string.substring(end)); -} - -void KURL::setRef(const String& s) -{ - if (!m_isValid) - return; - parse(m_string.left(m_queryEnd) + (s.isNull() ? "" : "#" + s)); -} - -void KURL::removeRef() -{ - if (!m_isValid) - return; - parse(m_string.left(m_queryEnd)); -} - -void KURL::setQuery(const String& query) -{ - if (!m_isValid) - return; - - if ((query.isEmpty() || query[0] != '?') && !query.isNull()) - parse(m_string.left(m_pathEnd) + "?" + query + m_string.substring(m_queryEnd)); - else - parse(m_string.left(m_pathEnd) + query + m_string.substring(m_queryEnd)); - -} - -void KURL::setPath(const String& s) -{ - if (!m_isValid) - return; - - parse(m_string.left(m_portEnd) + encodeWithURLEscapeSequences(s) + m_string.substring(m_pathEnd)); -} - -String KURL::prettyURL() const -{ - if (!m_isValid) - return m_string; - - Vector<UChar> result; - - append(result, protocol()); - result.append(':'); - - Vector<UChar> authority; - - if (m_hostEnd != m_passwordEnd) { - if (m_userEnd != m_userStart) { - append(authority, user()); - authority.append('@'); - } - append(authority, host()); - if (port() != 0) { - authority.append(':'); - append(authority, String::number(port())); - } - } - - if (!authority.isEmpty()) { - result.append('/'); - result.append('/'); - result.append(authority); - } else if (protocolIs("file")) { - result.append('/'); - result.append('/'); - } - - append(result, path()); - append(result, query()); - - if (m_fragmentEnd != m_queryEnd) { - result.append('#'); - append(result, ref()); - } - - return String::adopt(result); -} - -#ifdef KURL_DECORATE_GLOBALS -String KURL::decodeURLEscapeSequences(const String& str) -#else -String decodeURLEscapeSequences(const String& str) -#endif -{ - return decodeURLEscapeSequences(str, UTF8Encoding()); -} - -#ifdef KURL_DECORATE_GLOBALS -String KURL::decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) -#else -String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) -#endif -{ - Vector<UChar> result; - - CharBuffer buffer; - - int length = str.length(); - int decodedPosition = 0; - int searchPosition = 0; - int encodedRunPosition; - while ((encodedRunPosition = str.find('%', searchPosition)) >= 0) { - // Find the sequence of %-escape codes. - int encodedRunEnd = encodedRunPosition; - while (length - encodedRunEnd >= 3 - && str[encodedRunEnd] == '%' - && isASCIIHexDigit(str[encodedRunEnd + 1]) - && isASCIIHexDigit(str[encodedRunEnd + 2])) - encodedRunEnd += 3; - if (encodedRunEnd == encodedRunPosition) { - ++searchPosition; - continue; - } - searchPosition = encodedRunEnd; - - // Decode the %-escapes into bytes. - unsigned runLength = (encodedRunEnd - encodedRunPosition) / 3; - buffer.resize(runLength); - char* p = buffer.data(); - const UChar* q = str.characters() + encodedRunPosition; - for (unsigned i = 0; i < runLength; ++i) { - *p++ = (hexDigitValue(q[1]) << 4) | hexDigitValue(q[2]); - q += 3; - } - - // Decode the bytes into Unicode characters. - String decoded = (encoding.isValid() ? encoding : UTF8Encoding()).decode(buffer.data(), p - buffer.data()); - if (decoded.isEmpty()) - continue; - - // Build up the string with what we just skipped and what we just decoded. - result.append(str.characters() + decodedPosition, encodedRunPosition - decodedPosition); - result.append(decoded.characters(), decoded.length()); - decodedPosition = encodedRunEnd; - } - - result.append(str.characters() + decodedPosition, length - decodedPosition); - - return String::adopt(result); -} - -bool KURL::isLocalFile() const -{ - // Including feed here might be a bad idea since drag and drop uses this check - // and including feed would allow feeds to potentially let someone's blog - // read the contents of the clipboard on a drag, even without a drop. - // Likewise with using the FrameLoader::shouldTreatURLAsLocal() function. - return protocolIs("file"); -} - -static void appendEscapingBadChars(char*& buffer, const char* strStart, size_t length) -{ - char* p = buffer; - - const char* str = strStart; - const char* strEnd = strStart + length; - while (str < strEnd) { - unsigned char c = *str++; - if (isBadChar(c)) { - if (c == '%' || c == '?') { - *p++ = c; - } else if (c != 0x09 && c != 0x0a && c != 0x0d) { - *p++ = '%'; - *p++ = hexDigits[c >> 4]; - *p++ = hexDigits[c & 0xF]; - } - } else { - *p++ = c; - } - } - - buffer = p; -} - -// copy a path, accounting for "." and ".." segments -static int copyPathRemovingDots(char* dst, const char* src, int srcStart, int srcEnd) -{ - char* bufferPathStart = dst; - - // empty path is a special case, and need not have a leading slash - if (srcStart != srcEnd) { - const char* baseStringStart = src + srcStart; - const char* baseStringEnd = src + srcEnd; - const char* baseStringPos = baseStringStart; - - // this code is unprepared for paths that do not begin with a - // slash and we should always have one in the source string - ASSERT(baseStringPos[0] == '/'); - - // copy the leading slash into the destination - *dst = *baseStringPos; - baseStringPos++; - dst++; - - while (baseStringPos < baseStringEnd) { - if (baseStringPos[0] == '.' && dst[-1] == '/') { - if (baseStringPos[1] == '/' || baseStringPos + 1 == baseStringEnd) { - // skip over "." segment - baseStringPos += 2; - continue; - } else if (baseStringPos[1] == '.' && (baseStringPos[2] == '/' || - baseStringPos + 2 == baseStringEnd)) { - // skip over ".." segment and rewind the last segment - // the RFC leaves it up to the app to decide what to do with excess - // ".." segments - we choose to drop them since some web content - // relies on this. - baseStringPos += 3; - if (dst > bufferPathStart + 1) - dst--; - // Note that these two while blocks differ subtly. - // The first helps to remove multiple adjoining slashes as we rewind. - // The +1 to bufferPathStart in the first while block prevents eating a leading slash - while (dst > bufferPathStart + 1 && dst[-1] == '/') - dst--; - while (dst > bufferPathStart && dst[-1] != '/') - dst--; - continue; - } - } - - *dst = *baseStringPos; - baseStringPos++; - dst++; - } - } - *dst = '\0'; - return dst - bufferPathStart; -} - -static inline bool hasSlashDotOrDotDot(const char* str) -{ - const unsigned char* p = reinterpret_cast<const unsigned char*>(str); - if (!*p) - return false; - unsigned char pc = *p; - while (unsigned char c = *++p) { - if (c == '.' && (pc == '/' || pc == '.')) - return true; - pc = c; - } - return false; -} - -static inline bool matchLetter(char c, char lowercaseLetter) -{ - return (c | 0x20) == lowercaseLetter; -} - -void KURL::parse(const String& string) -{ - CharBuffer buffer; - encodeRelativeString(string, UTF8Encoding(), buffer); - parse(buffer.data(), &string); -} - -void KURL::parse(const char* url, const String* originalString) -{ - if (!url || url[0] == '\0') { - // valid URL must be non-empty - m_string = originalString ? *originalString : url; - invalidate(); - return; - } - - if (!isSchemeFirstChar(url[0])) { - // scheme must start with an alphabetic character - m_string = originalString ? *originalString : url; - invalidate(); - return; - } - - int schemeEnd = 0; - while (isSchemeChar(url[schemeEnd])) - schemeEnd++; - - if (url[schemeEnd] != ':') { - m_string = originalString ? *originalString : url; - invalidate(); - return; - } - - int userStart = schemeEnd + 1; - int userEnd; - int passwordStart; - int passwordEnd; - int hostStart; - int hostEnd; - int portStart; - int portEnd; - - bool hierarchical = url[schemeEnd + 1] == '/'; - - bool isFile = schemeEnd == 4 - && matchLetter(url[0], 'f') - && matchLetter(url[1], 'i') - && matchLetter(url[2], 'l') - && matchLetter(url[3], 'e'); - - bool isHTTPorHTTPS = matchLetter(url[0], 'h') - && matchLetter(url[1], 't') - && matchLetter(url[2], 't') - && matchLetter(url[3], 'p') - && (url[4] == ':' || (matchLetter(url[4], 's') && url[5] == ':')); - - if (hierarchical && url[schemeEnd + 2] == '/') { - // The part after the scheme is either a net_path or an abs_path whose first path segment is empty. - // Attempt to find an authority. - - // FIXME: Authority characters may be scanned twice, and it would be nice to be faster. - userStart += 2; - userEnd = userStart; - - int colonPos = 0; - while (isUserInfoChar(url[userEnd])) { - if (url[userEnd] == ':' && colonPos == 0) - colonPos = userEnd; - userEnd++; - } - - if (url[userEnd] == '@') { - // actual end of the userinfo, start on the host - if (colonPos != 0) { - passwordEnd = userEnd; - userEnd = colonPos; - passwordStart = colonPos + 1; - } else - passwordStart = passwordEnd = userEnd; - - hostStart = passwordEnd + 1; - } else if (url[userEnd] == '[' || isPathSegmentEndChar(url[userEnd])) { - // hit the end of the authority, must have been no user - // or looks like an IPv6 hostname - // either way, try to parse it as a hostname - userEnd = userStart; - passwordStart = passwordEnd = userEnd; - hostStart = userStart; - } else { - // invalid character - m_string = originalString ? *originalString : url; - invalidate(); - return; - } - - hostEnd = hostStart; - - // IPV6 IP address - if (url[hostEnd] == '[') { - hostEnd++; - while (isIPv6Char(url[hostEnd])) - hostEnd++; - if (url[hostEnd] == ']') - hostEnd++; - else { - // invalid character - m_string = originalString ? *originalString : url; - invalidate(); - return; - } - } else { - while (isHostnameChar(url[hostEnd])) - hostEnd++; - } - - if (url[hostEnd] == ':') { - portStart = portEnd = hostEnd + 1; - - // possible start of port - portEnd = portStart; - while (isASCIIDigit(url[portEnd])) - portEnd++; - } else - portStart = portEnd = hostEnd; - - if (!isPathSegmentEndChar(url[portEnd])) { - // invalid character - m_string = originalString ? *originalString : url; - invalidate(); - return; - } - - if (userStart == portEnd && !isHTTPorHTTPS && !isFile) { - // No authority found, which means that this is not a net_path, but rather an abs_path whose first two - // path segments are empty. For file, http and https only, an empty authority is allowed. - userStart -= 2; - userEnd = userStart; - passwordStart = userEnd; - passwordEnd = passwordStart; - hostStart = passwordEnd; - hostEnd = hostStart; - portStart = hostEnd; - portEnd = hostEnd; - } - } else { - // the part after the scheme must be an opaque_part or an abs_path - userEnd = userStart; - passwordStart = passwordEnd = userEnd; - hostStart = hostEnd = passwordEnd; - portStart = portEnd = hostEnd; - } - - int pathStart = portEnd; - int pathEnd = pathStart; - while (url[pathEnd] && url[pathEnd] != '?' && url[pathEnd] != '#') - pathEnd++; - - int queryStart = pathEnd; - int queryEnd = queryStart; - if (url[queryStart] == '?') { - while (url[queryEnd] && url[queryEnd] != '#') - queryEnd++; - } - - int fragmentStart = queryEnd; - int fragmentEnd = fragmentStart; - if (url[fragmentStart] == '#') { - fragmentStart++; - fragmentEnd = fragmentStart; - while (url[fragmentEnd]) - fragmentEnd++; - } - - // assemble it all, remembering the real ranges - - Vector<char, 4096> buffer(fragmentEnd * 3 + 1); - - char *p = buffer.data(); - const char *strPtr = url; - - // copy in the scheme - const char *schemeEndPtr = url + schemeEnd; - while (strPtr < schemeEndPtr) - *p++ = *strPtr++; - m_schemeEnd = p - buffer.data(); - - bool hostIsLocalHost = portEnd - userStart == 9 - && matchLetter(url[userStart], 'l') - && matchLetter(url[userStart+1], 'o') - && matchLetter(url[userStart+2], 'c') - && matchLetter(url[userStart+3], 'a') - && matchLetter(url[userStart+4], 'l') - && matchLetter(url[userStart+5], 'h') - && matchLetter(url[userStart+6], 'o') - && matchLetter(url[userStart+7], 's') - && matchLetter(url[userStart+8], 't'); - - // File URLs need a host part unless it is just file:// or file://localhost - bool degenFilePath = pathStart == pathEnd && (hostStart == hostEnd || hostIsLocalHost); - - bool haveNonHostAuthorityPart = userStart != userEnd || passwordStart != passwordEnd || portStart != portEnd; - - // add ":" after scheme - *p++ = ':'; - - // if we have at least one authority part or a file URL - add "//" and authority - if (isFile ? !degenFilePath : (haveNonHostAuthorityPart || hostStart != hostEnd)) { - *p++ = '/'; - *p++ = '/'; - - m_userStart = p - buffer.data(); - - // copy in the user - strPtr = url + userStart; - const char* userEndPtr = url + userEnd; - while (strPtr < userEndPtr) - *p++ = *strPtr++; - m_userEnd = p - buffer.data(); - - // copy in the password - if (passwordEnd != passwordStart) { - *p++ = ':'; - strPtr = url + passwordStart; - const char* passwordEndPtr = url + passwordEnd; - while (strPtr < passwordEndPtr) - *p++ = *strPtr++; - } - m_passwordEnd = p - buffer.data(); - - // If we had any user info, add "@" - if (p - buffer.data() != m_userStart) - *p++ = '@'; - - // copy in the host, except in the case of a file URL with authority="localhost" - if (!(isFile && hostIsLocalHost && !haveNonHostAuthorityPart)) { - strPtr = url + hostStart; - const char* hostEndPtr = url + hostEnd; - while (strPtr < hostEndPtr) - *p++ = *strPtr++; - } - m_hostEnd = p - buffer.data(); - - // copy in the port - if (hostEnd != portStart) { - *p++ = ':'; - strPtr = url + portStart; - const char *portEndPtr = url + portEnd; - while (strPtr < portEndPtr) - *p++ = *strPtr++; - } - m_portEnd = p - buffer.data(); - } else - m_userStart = m_userEnd = m_passwordEnd = m_hostEnd = m_portEnd = p - buffer.data(); - - // For canonicalization, ensure we have a '/' for no path. - // Only do this for http and https. - if (isHTTPorHTTPS && pathEnd - pathStart == 0) - *p++ = '/'; - - // add path, escaping bad characters - if (!hierarchical || !hasSlashDotOrDotDot(url)) - appendEscapingBadChars(p, url + pathStart, pathEnd - pathStart); - else { - CharBuffer pathBuffer(pathEnd - pathStart + 1); - size_t length = copyPathRemovingDots(pathBuffer.data(), url, pathStart, pathEnd); - appendEscapingBadChars(p, pathBuffer.data(), length); - } - - m_pathEnd = p - buffer.data(); - - // Find the position after the last slash in the path, or - // the position before the path if there are no slashes in it. - int i; - for (i = m_pathEnd; i > m_portEnd; --i) { - if (buffer[i - 1] == '/') - break; - } - m_pathAfterLastSlash = i; - - // add query, escaping bad characters - appendEscapingBadChars(p, url + queryStart, queryEnd - queryStart); - m_queryEnd = p - buffer.data(); - - // add fragment, escaping bad characters - if (fragmentEnd != queryEnd) { - *p++ = '#'; - appendEscapingBadChars(p, url + fragmentStart, fragmentEnd - fragmentStart); - } - m_fragmentEnd = p - buffer.data(); - - ASSERT(p - buffer.data() <= static_cast<int>(buffer.size())); - - // If we didn't end up actually changing the original string and - // it was already in a String, reuse it to avoid extra allocation. - if (originalString && strncmp(buffer.data(), url, m_fragmentEnd) == 0) - m_string = *originalString; - else - m_string = String(buffer.data(), m_fragmentEnd); - - m_isValid = true; -} - -bool equalIgnoringRef(const KURL& a, const KURL& b) -{ - if (a.m_queryEnd != b.m_queryEnd) - return false; - unsigned queryLength = a.m_queryEnd; - for (unsigned i = 0; i < queryLength; ++i) - if (a.string()[i] != b.string()[i]) - return false; - return true; -} - -bool protocolHostAndPortAreEqual(const KURL& a, const KURL& b) -{ - if (a.m_schemeEnd != b.m_schemeEnd) - return false; - int hostStartA = a.hostStart(); - int hostStartB = b.hostStart(); - if (a.m_hostEnd - hostStartA != b.m_hostEnd - hostStartB) - return false; - - // Check the scheme - for (int i = 0; i < a.m_schemeEnd; ++i) - if (a.string()[i] != b.string()[i]) - return false; - - // And the host - for (int i = hostStartA; i < a.m_hostEnd; ++i) - if (a.string()[i] != b.string()[i]) - return false; - - if (a.port() != b.port()) - return false; - - return true; -} - -#ifdef KURL_DECORATE_GLOBALS -String KURL::encodeWithURLEscapeSequences(const String& notEncodedString) -#else -String encodeWithURLEscapeSequences(const String& notEncodedString) -#endif -{ - CString asUTF8 = notEncodedString.utf8(); - - CharBuffer buffer(asUTF8.length() * 3 + 1); - char* p = buffer.data(); - - const char* str = asUTF8.data(); - const char* strEnd = str + asUTF8.length(); - while (str < strEnd) { - unsigned char c = *str++; - if (isBadChar(c)) { - *p++ = '%'; - *p++ = hexDigits[c >> 4]; - *p++ = hexDigits[c & 0xF]; - } else - *p++ = c; - } - - ASSERT(p - buffer.data() <= static_cast<int>(buffer.size())); - - return String(buffer.data(), p - buffer.data()); -} - -// Appends the punycoded hostname identified by the given string and length to -// the output buffer. The result will not be null terminated. -static void appendEncodedHostname(UCharBuffer& buffer, const UChar* str, unsigned strLen) -{ - // Needs to be big enough to hold an IDN-encoded name. - // For host names bigger than this, we won't do IDN encoding, which is almost certainly OK. - const unsigned hostnameBufferLength = 2048; - - if (strLen > hostnameBufferLength || charactersAreAllASCII(str, strLen)) { - buffer.append(str, strLen); - return; - } - -#if USE(ICU_UNICODE) - UChar hostnameBuffer[hostnameBufferLength]; - UErrorCode error = U_ZERO_ERROR; - int32_t numCharactersConverted = uidna_IDNToASCII(str, strLen, hostnameBuffer, - hostnameBufferLength, UIDNA_ALLOW_UNASSIGNED, 0, &error); - if (error == U_ZERO_ERROR) - buffer.append(hostnameBuffer, numCharactersConverted); -#elif USE(QT4_UNICODE) - QByteArray result = QUrl::toAce(String(str, strLen)); - buffer.append(result.constData(), result.length()); -#endif -} - -static void findHostnamesInMailToURL(const UChar* str, int strLen, Vector<pair<int, int> >& nameRanges) -{ - // In a mailto: URL, host names come after a '@' character and end with a '>' or ',' or '?' or end of string character. - // Skip quoted strings so that characters in them don't confuse us. - // When we find a '?' character, we are past the part of the URL that contains host names. - - nameRanges.clear(); - - int p = 0; - while (1) { - // Find start of host name or of quoted string. - int hostnameOrStringStart = findFirstOf(str, strLen, p, "\"@?"); - if (hostnameOrStringStart == -1) - return; - UChar c = str[hostnameOrStringStart]; - p = hostnameOrStringStart + 1; - - if (c == '?') - return; - - if (c == '@') { - // Find end of host name. - int hostnameStart = p; - int hostnameEnd = findFirstOf(str, strLen, p, ">,?"); - bool done; - if (hostnameEnd == -1) { - hostnameEnd = strLen; - done = true; - } else { - p = hostnameEnd; - done = false; - } - - nameRanges.append(make_pair(hostnameStart, hostnameEnd)); - - if (done) - return; - } else { - // Skip quoted string. - ASSERT(c == '"'); - while (1) { - int escapedCharacterOrStringEnd = findFirstOf(str, strLen, p, "\"\\"); - if (escapedCharacterOrStringEnd == -1) - return; - - c = str[escapedCharacterOrStringEnd]; - p = escapedCharacterOrStringEnd + 1; - - // If we are the end of the string, then break from the string loop back to the host name loop. - if (c == '"') - break; - - // Skip escaped character. - ASSERT(c == '\\'); - if (p == strLen) - return; - - ++p; - } - } - } -} - -static bool findHostnameInHierarchicalURL(const UChar* str, int strLen, int& startOffset, int& endOffset) -{ - // Find the host name in a hierarchical URL. - // It comes after a "://" sequence, with scheme characters preceding, and - // this should be the first colon in the string. - // It ends with the end of the string or a ":" or a path segment ending character. - // If there is a "@" character, the host part is just the part after the "@". - int separator = findFirstOf(str, strLen, 0, ":"); - if (separator == -1 || separator + 2 >= strLen || - str[separator + 1] != '/' || str[separator + 2] != '/') - return false; - - // Check that all characters before the :// are valid scheme characters. - if (!isSchemeFirstChar(str[0])) - return false; - for (int i = 1; i < separator; ++i) { - if (!isSchemeChar(str[i])) - return false; - } - - // Start after the separator. - int authorityStart = separator + 3; - - // Find terminating character. - int hostnameEnd = strLen; - for (int i = authorityStart; i < strLen; ++i) { - UChar c = str[i]; - if (c == ':' || (isPathSegmentEndChar(c) && c != 0)) { - hostnameEnd = i; - break; - } - } - - // Find "@" for the start of the host name. - int userInfoTerminator = findFirstOf(str, strLen, authorityStart, "@"); - int hostnameStart; - if (userInfoTerminator == -1 || userInfoTerminator > hostnameEnd) - hostnameStart = authorityStart; - else - hostnameStart = userInfoTerminator + 1; - - startOffset = hostnameStart; - endOffset = hostnameEnd; - return true; -} - -// Converts all hostnames found in the given input to punycode, preserving the -// rest of the URL unchanged. The output will NOT be null-terminated. -static void encodeHostnames(const String& str, UCharBuffer& output) -{ - output.clear(); - - if (KURL::protocolIs(str, "mailto")) { - Vector<pair<int, int> > hostnameRanges; - findHostnamesInMailToURL(str.characters(), str.length(), hostnameRanges); - int n = hostnameRanges.size(); - int p = 0; - for (int i = 0; i < n; ++i) { - const pair<int, int>& r = hostnameRanges[i]; - output.append(&str.characters()[p], r.first - p); - appendEncodedHostname(output, &str.characters()[r.first], r.second - r.first); - p = r.second; - } - // This will copy either everything after the last hostname, or the - // whole thing if there is no hostname. - output.append(&str.characters()[p], str.length() - p); - } else { - int hostStart, hostEnd; - if (findHostnameInHierarchicalURL(str.characters(), str.length(), hostStart, hostEnd)) { - output.append(str.characters(), hostStart); // Before hostname. - appendEncodedHostname(output, &str.characters()[hostStart], hostEnd - hostStart); - output.append(&str.characters()[hostEnd], str.length() - hostEnd); // After hostname. - } else { - // No hostname to encode, return the input. - output.append(str.characters(), str.length()); - } - } -} - -static void encodeRelativeString(const String& rel, const TextEncoding& encoding, CharBuffer& output) -{ - UCharBuffer s; - encodeHostnames(rel, s); - - TextEncoding pathEncoding(UTF8Encoding()); // Path is always encoded as UTF-8; other parts may depend on the scheme. - - int pathEnd = -1; - if (encoding != pathEncoding && encoding.isValid() && !KURL::protocolIs(rel, "mailto") && !KURL::protocolIs(rel, "data")) { - // Find the first instance of either # or ?, keep pathEnd at -1 otherwise. - pathEnd = findFirstOf(s.data(), s.size(), 0, "#?"); - } - - if (pathEnd == -1) { - CString decoded = pathEncoding.encode(s.data(), s.size(), URLEncodedEntitiesForUnencodables); - output.resize(decoded.length()); - memcpy(output.data(), decoded.data(), decoded.length()); - } else { - CString pathDecoded = pathEncoding.encode(s.data(), pathEnd, URLEncodedEntitiesForUnencodables); - // Unencodable characters in URLs are represented by converting - // them to XML entities and escaping non-alphanumeric characters. - CString otherDecoded = encoding.encode(s.data() + pathEnd, s.size() - pathEnd, URLEncodedEntitiesForUnencodables); - - output.resize(pathDecoded.length() + otherDecoded.length()); - memcpy(output.data(), pathDecoded.data(), pathDecoded.length()); - memcpy(output.data() + pathDecoded.length(), otherDecoded.data(), otherDecoded.length()); - } - output.append('\0'); // null-terminate the output. -} - -static String substituteBackslashes(const String& string) -{ - int questionPos = string.find('?'); - int hashPos = string.find('#'); - int pathEnd; - - if (hashPos >= 0 && (questionPos < 0 || questionPos > hashPos)) - pathEnd = hashPos; - else if (questionPos >= 0) - pathEnd = questionPos; - else - pathEnd = string.length(); - - return string.left(pathEnd).replace('\\','/') + string.substring(pathEnd); -} - -bool KURL::isHierarchical() const -{ - if (!m_isValid) - return false; - ASSERT(m_string[m_schemeEnd] == ':'); - return m_string[m_schemeEnd + 1] == '/'; -} - -void KURL::copyToBuffer(CharBuffer& buffer) const -{ - // FIXME: This throws away the high bytes of all the characters in the string! - // That's fine for a valid URL, which is all ASCII, but not for invalid URLs. - buffer.resize(m_string.length()); - copyASCII(m_string.characters(), m_string.length(), buffer.data()); -} - -#ifdef KURL_DECORATE_GLOBALS -bool KURL::protocolIs(const String& url, const char* protocol) -#else -bool protocolIs(const String& url, const char* protocol) -#endif -{ - // Do the comparison without making a new string object. - assertProtocolIsGood(protocol); - for (int i = 0; ; ++i) { - if (!protocol[i]) - return url[i] == ':'; - if (toASCIILower(url[i]) != protocol[i]) - return false; - } -} - -#ifdef KURL_DECORATE_GLOBALS -String KURL::mimeTypeFromDataURL(const String& url) -#else -String mimeTypeFromDataURL(const String& url) -#endif -{ - ASSERT(protocolIs(url, "data")); - int index = url.find(';'); - if (index == -1) - index = url.find(','); - if (index != -1) { - int len = index - 5; - if (len > 0) - return url.substring(5, len); - return "text/plain"; // Data URLs with no MIME type are considered text/plain. - } - return ""; -} - -#ifdef KURL_DECORATE_GLOBALS -const KURL& KURL::blankURL() -#else -const KURL& blankURL() -#endif -{ - static KURL staticBlankURL("about:blank"); - return staticBlankURL; -} - -#ifndef NDEBUG -void KURL::print() const -{ - printf("%s\n", m_string.utf8().data()); -} -#endif - -} - -#endif // #ifndef USE_GOOGLE_URL_LIBRARY diff --git a/webkit/pending/KURL.h b/webkit/pending/KURL.h deleted file mode 100644 index b7c4cb2..0000000 --- a/webkit/pending/KURL.h +++ /dev/null @@ -1,447 +0,0 @@ -/* - * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef KURL_h -#define KURL_h - -#include "PlatformString.h" - -#ifdef USE_GOOGLE_URL_LIBRARY -#include "CString.h" -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_parse.h" -// TODO(brettw): Crazy hack. Because these headers pull in base/basictypes.h, -// which redefines COMPILE_ASSERT, restore it here to the definition in -// wtf/Assertions.h. -#undef COMPILE_ASSERT -#define COMPILE_ASSERT(exp, name) typedef int dummy##name [(exp) ? 1 : -1]; -#endif - -#if PLATFORM(CF) -typedef const struct __CFURL* CFURLRef; -#endif - -#if PLATFORM(MAC) -#ifdef __OBJC__ -@class NSURL; -#else -class NSURL; -#endif -#endif - -#if PLATFORM(QT) -QT_BEGIN_NAMESPACE -class QUrl; -QT_END_NAMESPACE -#endif - -namespace WebCore { - -class TextEncoding; -struct KURLHash; - -// FIXME: Our terminology here is a bit inconsistent. We refer to the part -// after the "#" as the "fragment" in some places and the "ref" in others. -// We should fix the terminology to match the URL and URI RFCs as closely -// as possible to resolve this. - -class KURL { -public: - // Generates a URL which contains a null string. - KURL() { invalidate(); } - - // The argument is an absolute URL string. The string is assumed to be - // already encoded. - // FIXME: This constructor has a special case for strings that start with - // "/", prepending "file://" to such strings; it would be good to get - // rid of that special case. - explicit KURL(const char*); - - // The argument is an absolute URL string. The string is assumed to be - // already encoded. - // FIXME: For characters with codes other than 0000-00FF will be chopped - // off, so this call is currently not safe to use with arbitrary strings. - // FIXME: This constructor has a special case for strings that start with - // "/", prepending "file://" to such strings; it would be good to get - // rid of that special case. - explicit KURL(const String&); - - // Resolves the relative URL with the given base URL. If provided, the - // TextEncoding is used to encode non-ASCII characers. The base URL can be - // null or empty, in which case the relative URL will be interpreted as - // absolute. - // FIXME: If the base URL is invalid, this always creates an invalid - // URL. Instead I think it would be better to treat all invalid base URLs - // the same way we treate null and empty base URLs. - KURL(const KURL& base, const String& relative); - KURL(const KURL& base, const String& relative, const TextEncoding&); - - // FIXME: The above functions should be harmonized so that passing a - // base of null or the empty string gives the same result as the - // standard String constructor. -#ifdef USE_GOOGLE_URL_LIBRARY - // For conversions for other structures that have already parsed and - // canonicalized the URL. The input must be exactly what KURL would have - // done with the same input. - KURL(const char* canonical_spec, int canonical_spec_len, - const url_parse::Parsed& parsed, bool is_valid); -#endif - - bool isValid() const { return m_isValid; } - - // Returns true if this URL has a path. Note that "http://foo.com/" has a - // path of "/", so this function will return true. Only invalid or - // non-hierarchical (like "javascript:") URLs will have no path. - bool hasPath() const; - -#ifdef USE_GOOGLE_URL_LIBRARY - bool isNull() const { return m_url.utf8String().isNull(); } - bool isEmpty() const { return m_url.utf8String().length() == 0; } - - const String& string() const { return m_url.string(); } -#else - bool isNull() const { return m_string.isNull(); } - bool isEmpty() const { return m_string.isEmpty(); } - - const String& string() const { return m_string; } -#endif - - String protocol() const; - String host() const; - unsigned short port() const; - String user() const; - String pass() const; - String path() const; - String lastPathComponent() const; - String query() const; // Includes the "?". - String ref() const; // Does *not* include the "#". - bool hasRef() const; - - String prettyURL() const; - String fileSystemPath() const; - - // Returns true if the current URL's protocol is the same as the null- - // terminated ASCII argument. The argument must be lower-case. - bool protocolIs(const char*) const; - bool isLocalFile() const; - - void setProtocol(const String&); - void setHost(const String&); - - // Setting the port to 0 will clear any port from the URL. - void setPort(unsigned short); - - // Input is like "foo.com" or "foo.com:8000". - void setHostAndPort(const String&); - - void setUser(const String&); - void setPass(const String&); - - // If you pass an empty path for HTTP or HTTPS URLs, the resulting path - // will be "/". - void setPath(const String&); - - // The query may begin with a question mark, or, if not, one will be added - // for you. Setting the query to the empty string will leave a "?" in the - // URL (with nothing after it). To clear the query, pass a null string. - void setQuery(const String&); - - void setRef(const String&); - void removeRef(); - - friend bool equalIgnoringRef(const KURL&, const KURL&); - - friend bool protocolHostAndPortAreEqual(const KURL&, const KURL&); - - static bool protocolIs(const String&, const char*); - -#ifdef USE_GOOGLE_URL_LIBRARY - operator const String&() const { return m_url.string(); } -#if USE(JSC) - operator KJS::UString() const { return m_url.string(); } -#endif - - unsigned hostStart() const; - unsigned hostEnd() const; - - unsigned pathStart() const; - unsigned pathEnd() const; - unsigned pathAfterLastSlash() const; - -#else // !USE_GOOGLE_URL_LIBRARY - operator const String&() const { return m_string; } -#if USE(JSC) - operator KJS::UString() const { return m_string; } -#endif - - unsigned hostStart() const { return (m_passwordEnd == m_userStart) ? m_passwordEnd : m_passwordEnd + 1; } - unsigned hostEnd() const { return m_hostEnd; } - - unsigned pathStart() const { return m_portEnd; } - unsigned pathEnd() const { return m_pathEnd; } - unsigned pathAfterLastSlash() const { return m_pathAfterLastSlash; } -#endif - -#if PLATFORM(CF) - KURL(CFURLRef); - CFURLRef createCFURL() const; -#endif - -#if PLATFORM(MAC) - KURL(NSURL*); - operator NSURL*() const; -#endif -#ifdef __OBJC__ -#ifdef USE_GOOGLE_URL_LIBRARY - operator NSString*() const { return string(); } -#else // USE_GOOGLE_URL_LIBRARY - operator NSString*() const { return m_string; } -#endif // USE_GOOGLE_URL_LIBRARY -#endif - -#if PLATFORM(QT) - KURL(const QUrl&); - operator QUrl() const; -#endif - -#ifdef USE_GOOGLE_URL_LIBRARY - // Getters for the parsed structure and its corresponding 8-bit string. - const url_parse::Parsed& parsed() const { return m_parsed; } - const CString& utf8String() const { return m_url.utf8String(); } -#endif - -#ifndef NDEBUG - void print() const; -#endif - -private: - void invalidate(); - bool isHierarchical() const; - - bool m_isValid; -#ifdef USE_GOOGLE_URL_LIBRARY - // Initializes the object. This will call through to one of the backend - // initializers below depending on whether the string's internal - // representation is 8 or 16 bit. - void init(const KURL& base, const String& relative, - const TextEncoding* query_encoding); - - // Backend initializers. The query encoding parameters are optional and can - // be NULL (this implies UTF-8). These initializers require that the object - // has just been created and the strings are NULL. Do not call on an - // already-constructed object. - void init(const KURL& base, const char* rel, int rel_len, - const TextEncoding* query_encoding); - void init(const KURL& base, const UChar* rel, int rel_len, - const TextEncoding* query_encoding); - - // Returns the substring of the input identified by the given component. - String componentString(const url_parse::Component& comp) const; - - // Replaces the given components, modifying the current URL. The current - // URL must be valid. - typedef url_canon::Replacements<url_parse::UTF16Char> Replacements; - void replaceComponents(const Replacements& replacements); - - // Returns true if the scheme matches the given lowercase ASCII scheme. - bool schemeIs(const char* lower_ascii_scheme) const; - - // We store the URL in UTF-8 (really, ASCII except for the ref which can be - // UTF-8). WebKit wants Strings out of us. - // We create the conversion of the strings on demand and cache the results - // to speed things up. - class URLString { - public: - URLString(); - - // Setters for the data. Using the ASCII version when you know the - // data is ASCII will be slightly more efficient. The UTF-8 version - // will always be correct if the caller is unsure. - void setUtf8(const char* data, int data_len); - void setAscii(const char* data, int data_len); - - // TODO(brettw) we can support an additional optimization when KURL - // class is supported only for Strings. Make this buffer support both - // optinal Strings and UTF-8 data. This way, we can use the optimization - // from the original KURL which uses = with the original string when - // canonicalization did not change it. This allows the strings to share - // a buffer internally, and saves a malloc. - - // Getters for the data. - const CString& utf8String() const { return m_utf8; } - const String& string() const; - - private: - CString m_utf8; - - // Set to true when the caller set us using the ASCII setter. We can - // be more efficient when we know there is no UTF-8 to worry about. - // This flag is currently always correct, but should be changed to be a - // hint (see setUtf8). - bool m_utf8IsASCII; - - mutable bool m_stringIsValid; - mutable String m_string; - }; - - URLString m_url; - url_parse::Parsed m_parsed; // Indexes into the UTF-8 version of the string. - -#else - void init(const KURL&, const String&, const TextEncoding&); - void copyToBuffer(Vector<char, 512>& buffer) const; - - // Parses the given URL. The originalString parameter allows for an - // optimization: When the source is the same as the fixed-up string, - // it will use the passed-in string instead of allocating a new one. - void parse(const String&); - void parse(const char* url, const String* originalString); - - String m_string; - - int m_schemeEnd; - int m_userStart; - int m_userEnd; - int m_passwordEnd; - int m_hostEnd; - int m_portEnd; - int m_pathAfterLastSlash; - int m_pathEnd; - int m_queryEnd; - int m_fragmentEnd; -#endif - -// See the following "#infdef KURL_DECORATE_GLOBALS" for an explanation. -#ifdef KURL_DECORATE_GLOBALS - public: - static const KURL& blankURL(); - // Note: protocolIs() is already defined, so omit it. - static String mimeTypeFromDataURL(const String& url); - static String decodeURLEscapeSequences(const String&); - static String decodeURLEscapeSequences(const String&, const TextEncoding&); - static String encodeWithURLEscapeSequences(const String&); -#endif // ifdef KURL_DECORATE_GLOBALS -}; - -bool operator==(const KURL&, const KURL&); -bool operator==(const KURL&, const String&); -bool operator==(const String&, const KURL&); -bool operator!=(const KURL&, const KURL&); -bool operator!=(const KURL&, const String&); -bool operator!=(const String&, const KURL&); - -bool equalIgnoringRef(const KURL&, const KURL&); -bool protocolHostAndPortAreEqual(const KURL&, const KURL&); - -// GKURL_unittest.cpp includes both KURL.cpp and GKURL.cpp. -// For that to work, global functions need to be avoided. -// (the other globals are okay since they include KURL in parameter list). -// The workaround is to move the globals into KURL's class namespace. -#ifndef KURL_DECORATE_GLOBALS - -const KURL& blankURL(); - -// Functions to do URL operations on strings. -// These are operations that aren't faster on a parsed URL. - -bool protocolIs(const String& url, const char* protocol); - -String mimeTypeFromDataURL(const String& url); - -// Unescapes the given string using URL escaping rules, given an optional -// encoding (defaulting to UTF-8 otherwise). DANGER: If the URL has "%00" -// in it, the resulting string will have embedded null characters! -String decodeURLEscapeSequences(const String&); -String decodeURLEscapeSequences(const String&, const TextEncoding&); - -String encodeWithURLEscapeSequences(const String&); - -#endif // ifndef KURL_DECORATE_GLOBALS - -// Inlines. - -#ifdef USE_GOOGLE_URL_LIBRARY - -inline bool operator==(const KURL& a, const KURL& b) -{ - return a.utf8String() == b.utf8String(); -} - -inline bool operator!=(const KURL& a, const KURL& b) -{ - return a.utf8String() != b.utf8String(); -} - -#else - -inline bool operator==(const KURL& a, const KURL& b) -{ - return a.string() == b.string(); -} - -#endif - -inline bool operator==(const KURL& a, const String& b) -{ - return a.string() == b; -} - -inline bool operator==(const String& a, const KURL& b) -{ - return a == b.string(); -} - -#ifndef USE_GOOGLE_URL_LIBRARY - -inline bool operator!=(const KURL& a, const KURL& b) -{ - return a.string() != b.string(); -} - -#endif - -inline bool operator!=(const KURL& a, const String& b) -{ - return a.string() != b; -} - -inline bool operator!=(const String& a, const KURL& b) -{ - return a != b.string(); -} - -} // namespace WebCore - -namespace WTF { - - // KURLHash is the default hash for String - template<typename T> struct DefaultHash; - template<> struct DefaultHash<WebCore::KURL> { - typedef WebCore::KURLHash Hash; - }; - -} // namespace WTF - -#endif // KURL_h diff --git a/webkit/pending/NodeFilter.cpp b/webkit/pending/NodeFilter.cpp deleted file mode 100644 index 7e0fb50..0000000 --- a/webkit/pending/NodeFilter.cpp +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "NodeFilter.h" - -#include "ExceptionContext.h" -#include "Node.h" - -namespace WebCore { - -short NodeFilter::acceptNode(ExceptionContext* exec, Node* node) const -{ - // cast to short silences "enumeral and non-enumeral types in return" warning - return m_condition ? m_condition->acceptNode(exec, node) : static_cast<short>(FILTER_ACCEPT); -} - -short NodeFilter::acceptNode(Node* node) const -{ - ExceptionContext context(node); - return acceptNode(&context, node); -} - -} // namespace WebCore diff --git a/webkit/pending/NodeFilter.h b/webkit/pending/NodeFilter.h deleted file mode 100644 index ead7a0c..0000000 --- a/webkit/pending/NodeFilter.h +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef NodeFilter_h -#define NodeFilter_h - -#include "NodeFilterCondition.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefPtr.h> - -namespace WebCore { - - class ExceptionContext; - - class NodeFilter : public RefCounted<NodeFilter> { - public: - /** - * The following constants are returned by the acceptNode() - * method: - */ - enum { - FILTER_ACCEPT = 1, - FILTER_REJECT = 2, - FILTER_SKIP = 3 - }; - - /** - * These are the available values for the whatToShow parameter. - * They are the same as the set of possible types for Node, and - * their values are derived by using a bit position corresponding - * to the value of NodeType for the equivalent node type. - */ - enum { - SHOW_ALL = 0xFFFFFFFF, - SHOW_ELEMENT = 0x00000001, - SHOW_ATTRIBUTE = 0x00000002, - SHOW_TEXT = 0x00000004, - SHOW_CDATA_SECTION = 0x00000008, - SHOW_ENTITY_REFERENCE = 0x00000010, - SHOW_ENTITY = 0x00000020, - SHOW_PROCESSING_INSTRUCTION = 0x00000040, - SHOW_COMMENT = 0x00000080, - SHOW_DOCUMENT = 0x00000100, - SHOW_DOCUMENT_TYPE = 0x00000200, - SHOW_DOCUMENT_FRAGMENT = 0x00000400, - SHOW_NOTATION = 0x00000800 - }; - - static PassRefPtr<NodeFilter> create(PassRefPtr<NodeFilterCondition> condition) - { - return adoptRef(new NodeFilter(condition)); - } - - short acceptNode(ExceptionContext*, Node*) const; - void mark() { m_condition->mark(); }; - - // For non-JS bindings. Silently ignores the JavaScript exception if any. - short acceptNode(Node* node) const; - - private: - NodeFilter(PassRefPtr<NodeFilterCondition> condition) : m_condition(condition) { } - - RefPtr<NodeFilterCondition> m_condition; - }; - -} // namespace WebCore - -#endif // NodeFilter_h diff --git a/webkit/pending/NodeFilterCondition.cpp b/webkit/pending/NodeFilterCondition.cpp deleted file mode 100644 index e421492..0000000 --- a/webkit/pending/NodeFilterCondition.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "NodeFilterCondition.h" - -#include "NodeFilter.h" - -namespace WebCore { - -class ExceptionContext; - -short NodeFilterCondition::acceptNode(ExceptionContext*, Node*) const -{ - return NodeFilter::FILTER_ACCEPT; -} - -} // namespace WebCore diff --git a/webkit/pending/NodeFilterCondition.h b/webkit/pending/NodeFilterCondition.h deleted file mode 100644 index 4afdda6..0000000 --- a/webkit/pending/NodeFilterCondition.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef NodeFilterCondition_h -#define NodeFilterCondition_h - -#include <wtf/RefCounted.h> - -namespace WebCore { - - class ExceptionContext; - class Node; - - class NodeFilterCondition : public RefCounted<NodeFilterCondition> { - public: - virtual ~NodeFilterCondition() { } - virtual short acceptNode(ExceptionContext*, Node*) const = 0; - virtual void mark() { } - }; - -} // namespace WebCore - -#endif // NodeFilterCondition_h diff --git a/webkit/pending/NodeIterator.cpp b/webkit/pending/NodeIterator.cpp deleted file mode 100644 index ede32d0..0000000 --- a/webkit/pending/NodeIterator.cpp +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "NodeIterator.h" - -#include "Document.h" -#include "ExceptionCode.h" -#include "ExceptionContext.h" -#include "NodeFilter.h" - -namespace WebCore { - -NodeIterator::NodePointer::NodePointer() -{ -} - -NodeIterator::NodePointer::NodePointer(PassRefPtr<Node> n, bool b) - : node(n) - , isPointerBeforeNode(b) -{ -} - -void NodeIterator::NodePointer::clear() -{ - node.clear(); -} - -bool NodeIterator::NodePointer::moveToNext(Node* root) -{ - if (!node) - return false; - if (isPointerBeforeNode) { - isPointerBeforeNode = false; - return true; - } - node = node->traverseNextNode(root); - return node; -} - -bool NodeIterator::NodePointer::moveToPrevious(Node* root) -{ - if (!node) - return false; - if (!isPointerBeforeNode) { - isPointerBeforeNode = true; - return true; - } - node = node->traversePreviousNode(root); - return node; -} - -NodeIterator::NodeIterator(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) - : Traversal(rootNode, whatToShow, filter, expandEntityReferences) - , m_referenceNode(root(), true) - , m_detached(false) -{ - root()->document()->attachNodeIterator(this); -} - -NodeIterator::~NodeIterator() -{ - root()->document()->detachNodeIterator(this); -} - -PassRefPtr<Node> NodeIterator::nextNode(ExceptionContext* exec, ExceptionCode& ec) -{ - if (m_detached) { - ec = INVALID_STATE_ERR; - return 0; - } - - RefPtr<Node> result; - - m_candidateNode = m_referenceNode; - while (m_candidateNode.moveToNext(root())) { - // NodeIterators treat the DOM tree as a flat list of nodes. - // In other words, FILTER_REJECT does not pass over descendants - // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP. - RefPtr<Node> provisionalResult = m_candidateNode.node; - bool nodeWasAccepted = acceptNode(exec, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; - if (exec && exec->hadException()) - break; - if (nodeWasAccepted) { - m_referenceNode = m_candidateNode; - result = provisionalResult.release(); - break; - } - } - - m_candidateNode.clear(); - return result.release(); -} - -PassRefPtr<Node> NodeIterator::previousNode(ExceptionContext* exec, ExceptionCode& ec) -{ - if (m_detached) { - ec = INVALID_STATE_ERR; - return 0; - } - - RefPtr<Node> result; - - m_candidateNode = m_referenceNode; - while (m_candidateNode.moveToPrevious(root())) { - // NodeIterators treat the DOM tree as a flat list of nodes. - // In other words, FILTER_REJECT does not pass over descendants - // of the rejected node. Hence, FILTER_REJECT is the same as FILTER_SKIP. - RefPtr<Node> provisionalResult = m_candidateNode.node; - bool nodeWasAccepted = acceptNode(exec, provisionalResult.get()) == NodeFilter::FILTER_ACCEPT; - if (exec && exec->hadException()) - break; - if (nodeWasAccepted) { - m_referenceNode = m_candidateNode; - result = provisionalResult.release(); - break; - } - } - - m_candidateNode.clear(); - return result.release(); -} - -void NodeIterator::detach() -{ - root()->document()->detachNodeIterator(this); - m_detached = true; - m_referenceNode.node.clear(); -} - -void NodeIterator::nodeWillBeRemoved(Node* removedNode) -{ - updateForNodeRemoval(removedNode, m_candidateNode); - updateForNodeRemoval(removedNode, m_referenceNode); -} - -void NodeIterator::updateForNodeRemoval(Node* removedNode, NodePointer& referenceNode) const -{ - ASSERT(!m_detached); - ASSERT(removedNode); - ASSERT(root()->document() == removedNode->document()); - - // Iterator is not affected if the removed node is the reference node and is the root. - // or if removed node is not the reference node, or the ancestor of the reference node. - if (!removedNode->isDescendantOf(root())) - return; - bool willRemoveReferenceNode = removedNode == referenceNode.node; - bool willRemoveReferenceNodeAncestor = referenceNode.node && referenceNode.node->isDescendantOf(removedNode); - if (!willRemoveReferenceNode && !willRemoveReferenceNodeAncestor) - return; - - if (referenceNode.isPointerBeforeNode) { - Node* node = removedNode->traverseNextNode(root()); - if (node) { - // Move out from under the node being removed if the reference node is - // a descendant of the node being removed. - if (willRemoveReferenceNodeAncestor) { - while (node && node->isDescendantOf(removedNode)) - node = node->traverseNextNode(root()); - } - if (node) - referenceNode.node = node; - } else { - node = removedNode->traversePreviousNode(root()); - if (node) { - // Move out from under the node being removed if the reference node is - // a descendant of the node being removed. - if (willRemoveReferenceNodeAncestor) { - while (node && node->isDescendantOf(removedNode)) - node = node->traversePreviousNode(root()); - } - if (node) { - // Removing last node. - // Need to move the pointer after the node preceding the - // new reference node. - referenceNode.node = node; - referenceNode.isPointerBeforeNode = false; - } - } - } - } else { - Node* node = removedNode->traversePreviousNode(root()); - if (node) { - // Move out from under the node being removed if the reference node is - // a descendant of the node being removed. - if (willRemoveReferenceNodeAncestor) { - while (node && node->isDescendantOf(removedNode)) - node = node->traversePreviousNode(root()); - } - if (node) - referenceNode.node = node; - } else { - node = removedNode->traverseNextNode(root()); - // Move out from under the node being removed if the reference node is - // a descendant of the node being removed. - if (willRemoveReferenceNodeAncestor) { - while (node && node->isDescendantOf(removedNode)) - node = node->traversePreviousNode(root()); - } - if (node) - referenceNode.node = node; - } - } -} - -PassRefPtr<Node> NodeIterator::nextNode(ExceptionCode& ec) -{ - ExceptionContext context(referenceNode()); - return nextNode(&context, ec); -} - -PassRefPtr<Node> NodeIterator::previousNode(ExceptionCode& ec) -{ - ExceptionContext context(referenceNode()); - return previousNode(&context, ec); -} - -} // namespace WebCore diff --git a/webkit/pending/NodeIterator.h b/webkit/pending/NodeIterator.h deleted file mode 100644 index c53a010..0000000 --- a/webkit/pending/NodeIterator.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef NodeIterator_h -#define NodeIterator_h - -#include "NodeFilter.h" -#include "Traversal.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> - -namespace WebCore { - - class ExceptionContext; - - typedef int ExceptionCode; - - class NodeIterator : public RefCounted<NodeIterator>, public Traversal { - public: - static PassRefPtr<NodeIterator> create(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) - { - return adoptRef(new NodeIterator(rootNode, whatToShow, filter, expandEntityReferences)); - } - ~NodeIterator(); - - PassRefPtr<Node> nextNode(ExceptionContext*, ExceptionCode&); - PassRefPtr<Node> previousNode(ExceptionContext*, ExceptionCode&); - void detach(); - - Node* referenceNode() const { return m_referenceNode.node.get(); } - bool pointerBeforeReferenceNode() const { return m_referenceNode.isPointerBeforeNode; } - - // This function is called before any node is removed from the document tree. - void nodeWillBeRemoved(Node*); - - // For non-JS bindings. Silently ignores the JavaScript exception if any. - PassRefPtr<Node> nextNode(ExceptionCode& ec); - PassRefPtr<Node> previousNode(ExceptionCode& ec); - - private: - NodeIterator(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); - - struct NodePointer { - RefPtr<Node> node; - bool isPointerBeforeNode; - NodePointer(); - NodePointer(PassRefPtr<Node>, bool); - void clear(); - bool moveToNext(Node* root); - bool moveToPrevious(Node* root); - }; - - void updateForNodeRemoval(Node* nodeToBeRemoved, NodePointer&) const; - - NodePointer m_referenceNode; - NodePointer m_candidateNode; - bool m_detached; - }; - -} // namespace WebCore - -#endif // NodeIterator_h diff --git a/webkit/pending/Page.cpp b/webkit/pending/Page.cpp deleted file mode 100644 index abc89cf..0000000 --- a/webkit/pending/Page.cpp +++ /dev/null @@ -1,577 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All Rights Reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "Page.h" - -#include "Chrome.h" -#include "ChromeClient.h" -#include "ContextMenuClient.h" -#include "ContextMenuController.h" -#include "CSSStyleSelector.h" -#include "EditorClient.h" -#include "DOMWindow.h" -#include "DragController.h" -#include "EventNames.h" -#include "FileSystem.h" -#include "FocusController.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "HistoryItem.h" -#include "InspectorController.h" -#include "JavaScriptDebugServer.h" -#include "Logging.h" -#include "NetworkStateNotifier.h" -#include "PageGroup.h" -#include "PluginData.h" -#include "ProgressTracker.h" -#include "RenderWidget.h" -#include "SelectionController.h" -#include "Settings.h" -#include "StringHash.h" -#include "TextResourceDecoder.h" -#include "Widget.h" -#include "ScriptController.h" -#include <kjs/collector.h> -#include <kjs/JSLock.h> -#include <wtf/HashMap.h> -#include <wtf/RefCountedLeakCounter.h> - -#if ENABLE(DOM_STORAGE) -#include "LocalStorage.h" -#include "SessionStorage.h" -#include "StorageArea.h" -#endif - -namespace WebCore { - -using namespace EventNames; - -static HashSet<Page*>* allPages; - -#ifndef NDEBUG -static WTF::RefCountedLeakCounter pageCounter("Page"); -#endif - -static void networkStateChanged() -{ - Vector<RefPtr<Frame> > frames; - - // Get all the frames of all the pages in all the page groups - HashSet<Page*>::iterator end = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) { - for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) - frames.append(frame); - } - - AtomicString eventName = networkStateNotifier().onLine() ? onlineEvent : offlineEvent; - - for (unsigned i = 0; i < frames.size(); i++) { - Document* document = frames[i]->document(); - - if (!document) - continue; - - // If the document does not have a body the event should be dispatched to the document - EventTargetNode* eventTarget = document->body(); - if (!eventTarget) - eventTarget = document; - - eventTarget->dispatchHTMLEvent(eventName, false, false); - } -} - -Page::Page(ChromeClient* chromeClient, ContextMenuClient* contextMenuClient, EditorClient* editorClient, DragClient* dragClient, InspectorClient* inspectorClient) - : m_chrome(new Chrome(this, chromeClient)) - , m_dragCaretController(new SelectionController(0, true)) - , m_dragController(new DragController(this, dragClient)) - , m_focusController(new FocusController(this)) - , m_contextMenuController(new ContextMenuController(this, contextMenuClient)) - , m_inspectorController(new InspectorController(this, inspectorClient)) - , m_settings(new Settings(this)) - , m_progress(new ProgressTracker) - , m_backForwardList(BackForwardList::create(this)) - , m_editorClient(editorClient) - , m_frameCount(0) - , m_tabKeyCyclesThroughElements(true) - , m_defersLoading(false) - , m_inLowQualityInterpolationMode(false) - , m_parentInspectorController(0) - , m_didLoadUserStyleSheet(false) - , m_userStyleSheetModificationTime(0) - , m_group(0) - , m_debugger(0) - , m_pendingUnloadEventCount(0) - , m_pendingBeforeUnloadEventCount(0) - , m_customHTMLTokenizerTimeDelay(-1) - , m_customHTMLTokenizerChunkSize(-1) -{ - if (!allPages) { - allPages = new HashSet<Page*>; - setFocusRingColorChangeFunction(setNeedsReapplyStyles); - - networkStateNotifier().setNetworkStateChangedFunction(networkStateChanged); - } - - ASSERT(!allPages->contains(this)); - allPages->add(this); - -#if USE(JSC) - JavaScriptDebugServer::shared().pageCreated(this); -#endif - -#ifndef NDEBUG - pageCounter.increment(); -#endif -} - -Page::~Page() -{ - m_mainFrame->setView(0); - setGroupName(String()); - allPages->remove(this); - - for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) - frame->pageDestroyed(); - m_editorClient->pageDestroyed(); - if (m_parentInspectorController) - m_parentInspectorController->pageDestroyed(); - m_inspectorController->inspectedPageDestroyed(); - - m_backForwardList->close(); - -#ifndef NDEBUG - pageCounter.decrement(); - - // Cancel keepAlive timers, to ensure we release all Frames before exiting. - // It's safe to do this because we prohibit closing a Page while JavaScript - // is executing. - Frame::cancelAllKeepAlive(); -#endif -} - -void Page::setMainFrame(PassRefPtr<Frame> mainFrame) -{ - ASSERT(!m_mainFrame); // Should only be called during initialization - m_mainFrame = mainFrame; -} - -BackForwardList* Page::backForwardList() -{ - return m_backForwardList.get(); -} - -bool Page::goBack() -{ - HistoryItem* item = m_backForwardList->backItem(); - - if (item) { - goToItem(item, FrameLoadTypeBack); - return true; - } - return false; -} - -bool Page::goForward() -{ - HistoryItem* item = m_backForwardList->forwardItem(); - - if (item) { - goToItem(item, FrameLoadTypeForward); - return true; - } - return false; -} - -void Page::goToItem(HistoryItem* item, FrameLoadType type) -{ - // Abort any current load if we're going to a history item - m_mainFrame->loader()->stopAllLoaders(); - m_mainFrame->loader()->goToItem(item, type); -} - -void Page::setGroupName(const String& name) -{ - if (m_group && !m_group->name().isEmpty()) { - ASSERT(m_group != m_singlePageGroup.get()); - ASSERT(!m_singlePageGroup); - m_group->removePage(this); - } - - if (name.isEmpty()) - m_group = 0; - else { - m_singlePageGroup.clear(); - m_group = PageGroup::pageGroup(name); - m_group->addPage(this); - } -} - -const String& Page::groupName() const -{ - static String nullString; - return m_group ? m_group->name() : nullString; -} - -void Page::initGroup() -{ - ASSERT(!m_singlePageGroup); - ASSERT(!m_group); - m_singlePageGroup.set(new PageGroup(this)); - m_group = m_singlePageGroup.get(); -} - -void Page::setNeedsReapplyStyles() -{ - if (!allPages) - return; - HashSet<Page*>::iterator end = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) - for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) - frame->setNeedsReapplyStyles(); -} - -void Page::refreshPlugins(bool reload) -{ - if (!allPages) - return; - - PluginData::refresh(); - - Vector<RefPtr<Frame> > framesNeedingReload; - - HashSet<Page*>::iterator end = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) { - (*it)->m_pluginData = 0; - - if (reload) { - for (Frame* frame = (*it)->mainFrame(); frame; frame = frame->tree()->traverseNext()) { - if (frame->loader()->containsPlugins()) - framesNeedingReload.append(frame); - } - } - } - - for (size_t i = 0; i < framesNeedingReload.size(); ++i) - framesNeedingReload[i]->loader()->reload(); -} - -PluginData* Page::pluginData() const -{ - if (!m_pluginData) - m_pluginData = PluginData::create(this); - return m_pluginData.get(); -} - -static Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag) -{ - return forward - ? curr->tree()->traverseNextWithWrap(wrapFlag) - : curr->tree()->traversePreviousWithWrap(wrapFlag); -} - -bool Page::findString(const String& target, TextCaseSensitivity caseSensitivity, FindDirection direction, bool shouldWrap) -{ - if (target.isEmpty() || !mainFrame()) - return false; - - Frame* frame = focusController()->focusedOrMainFrame(); - Frame* startFrame = frame; - do { - if (frame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, false, true)) { - if (frame != startFrame) - startFrame->selection()->clear(); - focusController()->setFocusedFrame(frame); - return true; - } - frame = incrementFrame(frame, direction == FindDirectionForward, shouldWrap); - } while (frame && frame != startFrame); - - // Search contents of startFrame, on the other side of the selection that we did earlier. - // We cheat a bit and just research with wrap on - if (shouldWrap && !startFrame->selection()->isNone()) { - bool found = startFrame->findString(target, direction == FindDirectionForward, caseSensitivity == TextCaseSensitive, true, true); - focusController()->setFocusedFrame(frame); - return found; - } - - return false; -} - -unsigned int Page::markAllMatchesForText(const String& target, TextCaseSensitivity caseSensitivity, bool shouldHighlight, unsigned limit) -{ - if (target.isEmpty() || !mainFrame()) - return 0; - - unsigned matches = 0; - - Frame* frame = mainFrame(); - do { - frame->setMarkedTextMatchesAreHighlighted(shouldHighlight); - matches += frame->markAllMatchesForText(target, caseSensitivity == TextCaseSensitive, (limit == 0) ? 0 : (limit - matches)); - frame = incrementFrame(frame, true, false); - } while (frame); - - return matches; -} - -void Page::unmarkAllTextMatches() -{ - if (!mainFrame()) - return; - - Frame* frame = mainFrame(); - do { - if (Document* document = frame->document()) - document->removeMarkers(DocumentMarker::TextMatch); - frame = incrementFrame(frame, true, false); - } while (frame); -} - -const Selection& Page::selection() const -{ - return focusController()->focusedOrMainFrame()->selection()->selection(); -} - -void Page::setDefersLoading(bool defers) -{ - if (defers == m_defersLoading) - return; - - m_defersLoading = defers; - for (Frame* frame = mainFrame(); frame; frame = frame->tree()->traverseNext()) - frame->loader()->setDefersLoading(defers); -} - -void Page::clearUndoRedoOperations() -{ - m_editorClient->clearUndoRedoOperations(); -} - -bool Page::inLowQualityImageInterpolationMode() const -{ - return m_inLowQualityInterpolationMode; -} - -void Page::setInLowQualityImageInterpolationMode(bool mode) -{ - m_inLowQualityInterpolationMode = mode; -} - -void Page::userStyleSheetLocationChanged() -{ -#if !FRAME_LOADS_USER_STYLESHEET - // FIXME: We should provide a way to load other types of URLs than just - // file: (e.g., http:, data:). - if (m_settings->userStyleSheetLocation().isLocalFile()) - m_userStyleSheetPath = m_settings->userStyleSheetLocation().fileSystemPath(); - else - m_userStyleSheetPath = String(); - - m_didLoadUserStyleSheet = false; - m_userStyleSheet = String(); - m_userStyleSheetModificationTime = 0; -#endif -} - -const String& Page::userStyleSheet() const -{ - if (m_userStyleSheetPath.isEmpty()) { - ASSERT(m_userStyleSheet.isEmpty()); - return m_userStyleSheet; - } - - time_t modTime; - if (!getFileModificationTime(m_userStyleSheetPath, modTime)) { - // The stylesheet either doesn't exist, was just deleted, or is - // otherwise unreadable. If we've read the stylesheet before, we should - // throw away that data now as it no longer represents what's on disk. - m_userStyleSheet = String(); - return m_userStyleSheet; - } - - // If the stylesheet hasn't changed since the last time we read it, we can - // just return the old data. - if (m_didLoadUserStyleSheet && modTime <= m_userStyleSheetModificationTime) - return m_userStyleSheet; - - m_didLoadUserStyleSheet = true; - m_userStyleSheet = String(); - m_userStyleSheetModificationTime = modTime; - - // FIXME: It would be better to load this asynchronously to avoid blocking - // the process, but we will first need to create an asynchronous loading - // mechanism that is not tied to a particular Frame. We will also have to - // determine what our behavior should be before the stylesheet is loaded - // and what should happen when it finishes loading, especially with respect - // to when the load event fires, when Document::close is called, and when - // layout/paint are allowed to happen. - RefPtr<SharedBuffer> data = SharedBuffer::createWithContentsOfFile(m_userStyleSheetPath); - if (!data) - return m_userStyleSheet; - - m_userStyleSheet = TextResourceDecoder::create("text/css")->decode(data->data(), data->size()); - - return m_userStyleSheet; -} - -void Page::removeAllVisitedLinks() -{ - if (!allPages) - return; - HashSet<PageGroup*> groups; - HashSet<Page*>::iterator pagesEnd = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { - if (PageGroup* group = (*it)->groupPtr()) - groups.add(group); - } - HashSet<PageGroup*>::iterator groupsEnd = groups.end(); - for (HashSet<PageGroup*>::iterator it = groups.begin(); it != groupsEnd; ++it) - (*it)->removeVisitedLinks(); -} - -void Page::allVisitedStateChanged(PageGroup* group) -{ - ASSERT(group); - ASSERT(allPages); - HashSet<Page*>::iterator pagesEnd = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { - Page* page = *it; - if (page->m_group != group) - continue; - for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) { - if (CSSStyleSelector* styleSelector = frame->document()->styleSelector()) - styleSelector->allVisitedStateChanged(); - } - } -} - -void Page::visitedStateChanged(PageGroup* group, unsigned visitedLinkHash) -{ - ASSERT(group); - ASSERT(allPages); - HashSet<Page*>::iterator pagesEnd = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != pagesEnd; ++it) { - Page* page = *it; - if (page->m_group != group) - continue; - for (Frame* frame = page->m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) { - if (CSSStyleSelector* styleSelector = frame->document()->styleSelector()) - styleSelector->visitedStateChanged(visitedLinkHash); - } - } -} - -void Page::setDebuggerForAllPages(KJS::Debugger* debugger) -{ - ASSERT(allPages); - - HashSet<Page*>::iterator end = allPages->end(); - for (HashSet<Page*>::iterator it = allPages->begin(); it != end; ++it) - (*it)->setDebugger(debugger); -} - -void Page::setDebugger(KJS::Debugger* debugger) -{ - if (m_debugger == debugger) - return; - - m_debugger = debugger; - - for (Frame* frame = m_mainFrame.get(); frame; frame = frame->tree()->traverseNext()) - frame->script()->attachDebugger(m_debugger); -} - -#if ENABLE(DOM_STORAGE) -SessionStorage* Page::sessionStorage(bool optionalCreate) -{ - if (!m_sessionStorage && optionalCreate) - m_sessionStorage = SessionStorage::create(this); - - return m_sessionStorage.get(); -} - -void Page::setSessionStorage(PassRefPtr<SessionStorage> newStorage) -{ - ASSERT(newStorage->page() == this); - m_sessionStorage = newStorage; -} -#endif - -unsigned Page::pendingUnloadEventCount() -{ - return m_pendingUnloadEventCount; -} - -void Page::changePendingUnloadEventCount(int delta) -{ - if (!delta) - return; - ASSERT( (delta + (int)m_pendingUnloadEventCount) >= 0 ); - - if (m_pendingUnloadEventCount == 0) - m_chrome->disableSuddenTermination(); - else if ((m_pendingUnloadEventCount + delta) == 0) - m_chrome->enableSuddenTermination(); - - m_pendingUnloadEventCount += delta; - return; -} - -unsigned Page::pendingBeforeUnloadEventCount() -{ - return m_pendingBeforeUnloadEventCount; -} - -void Page::changePendingBeforeUnloadEventCount(int delta) -{ - if (!delta) - return; - ASSERT( (delta + (int)m_pendingBeforeUnloadEventCount) >= 0 ); - - if (m_pendingBeforeUnloadEventCount == 0) - m_chrome->disableSuddenTermination(); - else if ((m_pendingBeforeUnloadEventCount + delta) == 0) - m_chrome->enableSuddenTermination(); - - m_pendingBeforeUnloadEventCount += delta; - return; -} - -void Page::setCustomHTMLTokenizerTimeDelay(double customHTMLTokenizerTimeDelay) -{ - if (customHTMLTokenizerTimeDelay < 0) { - m_customHTMLTokenizerTimeDelay = -1; - return; - } - m_customHTMLTokenizerTimeDelay = customHTMLTokenizerTimeDelay; -} - -void Page::setCustomHTMLTokenizerChunkSize(int customHTMLTokenizerChunkSize) -{ - if (customHTMLTokenizerChunkSize < 0) { - m_customHTMLTokenizerChunkSize = -1; - return; - } - m_customHTMLTokenizerChunkSize = customHTMLTokenizerChunkSize; -} - -} // namespace WebCore diff --git a/webkit/pending/Page.h b/webkit/pending/Page.h deleted file mode 100644 index d0e5ca1..0000000 --- a/webkit/pending/Page.h +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#ifndef Page_h -#define Page_h - -#include "BackForwardList.h" -#include "Chrome.h" -#include "ContextMenuController.h" -#include "FrameLoaderTypes.h" -#include "PlatformString.h" -#if PLATFORM(MAC) -#include "SchedulePair.h" -#endif -#include <wtf/HashSet.h> -#include <wtf/OwnPtr.h> - -#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS)) || (PLATFORM(QT) && defined(Q_WS_WIN)) -typedef struct HINSTANCE__* HINSTANCE; -#endif - -namespace KJS { - class Debugger; -} - -namespace WebCore { - - class Chrome; - class ChromeClient; - class ContextMenuClient; - class ContextMenuController; - class Document; - class DragClient; - class DragController; - class EditorClient; - class FocusController; - class Frame; - class InspectorClient; - class InspectorController; - class Node; - class PageGroup; - class PluginData; - class ProgressTracker; - class Selection; - class SelectionController; -#if ENABLE(DOM_STORAGE) - class SessionStorage; -#endif - class Settings; - class KURL; - - enum TextCaseSensitivity { TextCaseSensitive, TextCaseInsensitive }; - enum FindDirection { FindDirectionForward, FindDirectionBackward }; - - class Page : Noncopyable { - public: - static void setNeedsReapplyStyles(); - - Page(ChromeClient*, ContextMenuClient*, EditorClient*, DragClient*, InspectorClient*); - ~Page(); - - static void refreshPlugins(bool reload); - PluginData* pluginData() const; - - EditorClient* editorClient() const { return m_editorClient; } - - void setMainFrame(PassRefPtr<Frame>); - Frame* mainFrame() const { return m_mainFrame.get(); } - - BackForwardList* backForwardList(); - - // FIXME: The following three methods don't fall under the responsibilities of the Page object - // They seem to fit a hypothetical Page-controller object that would be akin to the - // Frame-FrameLoader relationship. They have to live here now, but should move somewhere that - // makes more sense when that class exists. - bool goBack(); - bool goForward(); - void goToItem(HistoryItem*, FrameLoadType); - - void setGroupName(const String&); - const String& groupName() const; - - PageGroup& group() { if (!m_group) initGroup(); return *m_group; } - PageGroup* groupPtr() { return m_group; } // can return 0 - - void incrementFrameCount() { ++m_frameCount; } - void decrementFrameCount() { --m_frameCount; } - int frameCount() const { return m_frameCount; } - - Chrome* chrome() const { return m_chrome.get(); } - SelectionController* dragCaretController() const { return m_dragCaretController.get(); } - DragController* dragController() const { return m_dragController.get(); } - FocusController* focusController() const { return m_focusController.get(); } - ContextMenuController* contextMenuController() const { return m_contextMenuController.get(); } - InspectorController* inspectorController() const { return m_inspectorController.get(); } - Settings* settings() const { return m_settings.get(); } - ProgressTracker* progress() const { return m_progress.get(); } - - void setParentInspectorController(InspectorController* controller) { m_parentInspectorController = controller; } - InspectorController* parentInspectorController() const { return m_parentInspectorController; } - - void setTabKeyCyclesThroughElements(bool b) { m_tabKeyCyclesThroughElements = b; } - bool tabKeyCyclesThroughElements() const { return m_tabKeyCyclesThroughElements; } - - bool findString(const String&, TextCaseSensitivity, FindDirection, bool shouldWrap); - unsigned int markAllMatchesForText(const String&, TextCaseSensitivity, bool shouldHighlight, unsigned); - void unmarkAllTextMatches(); - -#if PLATFORM(MAC) - void addSchedulePair(PassRefPtr<SchedulePair>); - void removeSchedulePair(PassRefPtr<SchedulePair>); - SchedulePairHashSet* scheduledRunLoopPairs() { return m_scheduledRunLoopPairs.get(); } - - OwnPtr<SchedulePairHashSet> m_scheduledRunLoopPairs; -#endif - - const Selection& selection() const; - - void setDefersLoading(bool); - bool defersLoading() const { return m_defersLoading; } - - void clearUndoRedoOperations(); - - bool inLowQualityImageInterpolationMode() const; - void setInLowQualityImageInterpolationMode(bool = true); - - void userStyleSheetLocationChanged(); - const String& userStyleSheet() const; - - void changePendingUnloadEventCount(int delta); - unsigned pendingUnloadEventCount(); - void changePendingBeforeUnloadEventCount(int delta); - unsigned pendingBeforeUnloadEventCount(); - - static void setDebuggerForAllPages(KJS::Debugger*); - void setDebugger(KJS::Debugger*); - KJS::Debugger* debugger() const { return m_debugger; } - -#if PLATFORM(WIN) || (PLATFORM(WX) && PLATFORM(WIN_OS)) || (PLATFORM(QT) && defined(Q_WS_WIN)) - // The global DLL or application instance used for all windows. - static void setInstanceHandle(HINSTANCE instanceHandle) { s_instanceHandle = instanceHandle; } - static HINSTANCE instanceHandle() { return s_instanceHandle; } -#endif - - static void removeAllVisitedLinks(); - - static void allVisitedStateChanged(PageGroup*); - static void visitedStateChanged(PageGroup*, unsigned visitedHash); - -#if ENABLE(DOM_STORAGE) - SessionStorage* sessionStorage(bool optionalCreate = true); - void setSessionStorage(PassRefPtr<SessionStorage>); -#endif - - void setCustomHTMLTokenizerTimeDelay(double); - bool hasCustomHTMLTokenizerTimeDelay() const { return m_customHTMLTokenizerTimeDelay != -1; } - double customHTMLTokenizerTimeDelay() const { ASSERT(m_customHTMLTokenizerTimeDelay != -1); return m_customHTMLTokenizerTimeDelay; } - - void setCustomHTMLTokenizerChunkSize(int); - bool hasCustomHTMLTokenizerChunkSize() const { return m_customHTMLTokenizerChunkSize != -1; } - int customHTMLTokenizerChunkSize() const { ASSERT(m_customHTMLTokenizerChunkSize != -1); return m_customHTMLTokenizerChunkSize; } - - private: - void initGroup(); - - OwnPtr<Chrome> m_chrome; - OwnPtr<SelectionController> m_dragCaretController; - OwnPtr<DragController> m_dragController; - OwnPtr<FocusController> m_focusController; - OwnPtr<ContextMenuController> m_contextMenuController; - OwnPtr<Settings> m_settings; - OwnPtr<ProgressTracker> m_progress; - -#if USE(V8) - // To fix crash in inspector window (Bug 904340) - RefPtr<InspectorController> m_inspectorController; -#else - OwnPtr<InspectorController> m_inspectorController; -#endif - RefPtr<BackForwardList> m_backForwardList; - RefPtr<Frame> m_mainFrame; - - mutable RefPtr<PluginData> m_pluginData; - - EditorClient* m_editorClient; - - int m_frameCount; - String m_groupName; - - bool m_tabKeyCyclesThroughElements; - bool m_defersLoading; - - bool m_inLowQualityInterpolationMode; - - InspectorController* m_parentInspectorController; - - String m_userStyleSheetPath; - mutable String m_userStyleSheet; - mutable bool m_didLoadUserStyleSheet; - mutable time_t m_userStyleSheetModificationTime; - - OwnPtr<PageGroup> m_singlePageGroup; - PageGroup* m_group; - - KJS::Debugger* m_debugger; - - unsigned m_pendingUnloadEventCount; - unsigned m_pendingBeforeUnloadEventCount; - - double m_customHTMLTokenizerTimeDelay; - int m_customHTMLTokenizerChunkSize; - -#if ENABLE(DOM_STORAGE) - RefPtr<SessionStorage> m_sessionStorage; -#endif -#if PLATFORM(WIN) || (PLATFORM(WX) && defined(__WXMSW__)) || (PLATFORM(QT) && defined(Q_WS_WIN)) - static HINSTANCE s_instanceHandle; -#endif - }; - -} // namespace WebCore - -#endif // Page_h diff --git a/webkit/pending/RenderButton.cpp b/webkit/pending/RenderButton.cpp deleted file mode 100644 index 34d30f0..0000000 --- a/webkit/pending/RenderButton.cpp +++ /dev/null @@ -1,152 +0,0 @@ -/** - * This file is part of the html renderer for KDE. - * - * Copyright (C) 2005 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderButton.h" - -#include "Document.h" -#include "GraphicsContext.h" -#include "HTMLInputElement.h" -#include "HTMLNames.h" -#include "RenderTextFragment.h" -#include "RenderTheme.h" - -namespace WebCore { - -using namespace HTMLNames; - -RenderButton::RenderButton(Node* node) - : RenderFlexibleBox(node) - , m_buttonText(0) - , m_inner(0) - , m_default(false) -{ -} - -void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild) -{ - if (!m_inner) { - // Create an anonymous block. - ASSERT(!firstChild()); - m_inner = createAnonymousBlock(); - setupInnerStyle(m_inner->style()); - RenderFlexibleBox::addChild(m_inner); - } - - m_inner->addChild(newChild, beforeChild); -} - -void RenderButton::removeChild(RenderObject* oldChild) -{ - if (oldChild == m_inner || !m_inner) { - RenderFlexibleBox::removeChild(oldChild); - m_inner = 0; - } else - m_inner->removeChild(oldChild); -} - -void RenderButton::setStyle(RenderStyle* style) -{ - RenderBlock::setStyle(style); - if (m_buttonText) - m_buttonText->setStyle(style); - if (m_inner) { - // RenderBlock handled updating the anonymous block's style. - setupInnerStyle(m_inner->style()); - } - setReplaced(isInline()); - - if (!m_default && theme()->isDefault(this)) { - if (!m_timer) - m_timer.set(new Timer<RenderButton>(this, &RenderButton::timerFired)); - m_timer->startRepeating(0.01); - m_default = true; - } else if (m_default && !theme()->isDefault(this)) { - m_default = false; - m_timer.clear(); - } -} - -void RenderButton::setupInnerStyle(RenderStyle* style) { - ASSERT(style->refCount() == 1); - // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is - // safe to modify. - style->setBoxFlex(1.0f); - theme()->adjustButtonInnerStyle(style); -} - -void RenderButton::updateFromElement() -{ - // If we're an input element, we may need to change our button text. - if (element()->hasTagName(inputTag)) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(element()); - String value = input->valueWithDefault(); - setText(value); - } -} - -bool RenderButton::canHaveChildren() const -{ - // Input elements can't have children, but button elements can. We'll - // write the code assuming any other button types that might emerge in the future - // can also have children. - return !element()->hasTagName(inputTag); -} - -void RenderButton::setText(const String& str) -{ - if (str.isEmpty()) { - if (m_buttonText) { - m_buttonText->destroy(); - m_buttonText = 0; - } - } else { - if (m_buttonText) - m_buttonText->setText(str.impl()); - else { - m_buttonText = new (renderArena()) RenderTextFragment(document(), str.impl()); - m_buttonText->setStyle(style()); - addChild(m_buttonText); - } - } -} - -void RenderButton::updateBeforeAfterContent(RenderStyle::PseudoId type) -{ - if (m_inner) - m_inner->updateBeforeAfterContentForContainer(type, this); - else - updateBeforeAfterContentForContainer(type, this); -} - -IntRect RenderButton::controlClipRect(int tx, int ty) const -{ - // Clip to the padding box to at least give content the extra padding space. - return IntRect(tx + borderLeft(), ty + borderTop(), m_width - borderLeft() - borderRight(), m_height - borderTop() - borderBottom()); -} - -void RenderButton::timerFired(Timer<RenderButton>*) -{ - repaint(); -} - -} // namespace WebCore diff --git a/webkit/pending/RenderButton.h b/webkit/pending/RenderButton.h deleted file mode 100644 index 27a6827..0000000 --- a/webkit/pending/RenderButton.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of the html renderer for KDE. - * - * Copyright (C) 2005 Apple Computer - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef RenderButton_h -#define RenderButton_h - -#include "RenderFlexibleBox.h" -#include "Timer.h" -#include <wtf/OwnPtr.h> - -namespace WebCore { - -class RenderTextFragment; - -// RenderButtons are just like normal flexboxes except that they will generate an anonymous block child. -// For inputs, they will also generate an anonymous RenderText and keep its style and content up -// to date as the button changes. -class RenderButton : public RenderFlexibleBox { -public: - RenderButton(Node*); - - virtual const char* renderName() const { return "RenderButton"; } - - virtual void addChild(RenderObject* newChild, RenderObject *beforeChild = 0); - virtual void removeChild(RenderObject*); - virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } - virtual bool createsAnonymousWrapper() const { return true; } - - virtual void setStyle(RenderStyle*); - void setupInnerStyle(RenderStyle*); - virtual void updateFromElement(); - - virtual void updateBeforeAfterContent(RenderStyle::PseudoId); - - virtual bool hasControlClip() const { return true; } - virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const; - - void setText(const String&); - - virtual bool canHaveChildren() const; - -protected: - virtual bool hasLineIfEmpty() const { return true; } - - void timerFired(Timer<RenderButton>*); - - RenderTextFragment* m_buttonText; - RenderBlock* m_inner; - - OwnPtr<Timer<RenderButton> > m_timer; - bool m_default; -}; - -} // namespace WebCore - -#endif // RenderButton_h diff --git a/webkit/pending/RenderPartObject.cpp b/webkit/pending/RenderPartObject.cpp deleted file mode 100644 index 86ea516..0000000 --- a/webkit/pending/RenderPartObject.cpp +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 2000 Simon Hausmann <hausmann@kde.org> - * (C) 2000 Stefan Schimanski (1Stein@gmx.de) - * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderPartObject.h" - -#include "Frame.h" -#include "FrameLoader.h" -#include "FrameLoaderClient.h" -#include "FrameTree.h" -#include "FrameView.h" -#include "HTMLEmbedElement.h" -#include "HTMLIFrameElement.h" -#include "HTMLNames.h" -#include "HTMLObjectElement.h" -#include "HTMLParamElement.h" -#include "MIMETypeRegistry.h" -#include "Page.h" -#include "Text.h" - -namespace WebCore { - -using namespace HTMLNames; - -RenderPartObject::RenderPartObject(HTMLFrameOwnerElement* element) - : RenderPart(element) -{ - // init RenderObject attributes - setInline(true); - m_hasFallbackContent = false; -} - -RenderPartObject::~RenderPartObject() -{ - if (m_view) - m_view->removeWidgetToUpdate(this); -} - -static bool isURLAllowed(Document* doc, const String& url) -{ - if (doc->frame()->page()->frameCount() >= 200) - return false; - - // We allow one level of self-reference because some sites depend on that. - // But we don't allow more than one. - KURL completeURL = doc->completeURL(url); - bool foundSelfReference = false; - for (Frame* frame = doc->frame(); frame; frame = frame->tree()->parent()) { - if (equalIgnoringRef(frame->loader()->url(), completeURL)) { - if (foundSelfReference) - return false; - foundSelfReference = true; - } - } - return true; -} - -static inline void mapClassIdToServiceType(const String& classId, String& serviceType) -{ - // It is ActiveX, but the nsplugin system handling - // should also work, that's why we don't override the - // serviceType with application/x-oleobject - // but let the KTrader in khtmlpart::createPart() detect - // the user's preference: launch with activex viewer or - // with nspluginviewer (Niko) -#ifndef DISABLE_ACTIVEX_TYPE_CONVERSION_FLASH - if (classId.contains("D27CDB6E-AE6D-11cf-96B8-444553540000", false)) { - serviceType = "application/x-shockwave-flash"; - return; - } -#endif -#ifndef DISABLE_ACTIVEX_TYPE_CONVERSION_REALAUDIO - if (classId.contains("CFCDAA03-8BE4-11cf-B84B-0020AFBBCCFA", false)) { - serviceType = "audio/x-pn-realaudio-plugin"; - return; - } -#endif -#ifndef DISABLE_ACTIVEX_TYPE_CONVERSION_QUICKTIME - if (classId.contains("02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", false)) { - serviceType = "video/quicktime"; - return; - } -#endif -#ifndef DISABLE_ACTIVEX_TYPE_CONVERSION_DIRECTOR - if (classId.contains("166B1BCA-3F9C-11CF-8075-444553540000", false)) { - serviceType = "application/x-director"; - return; - } -#endif -#ifndef DISABLE_ACTIVEX_TYPE_CONVERSION_MPLAYER2 - if (classId.contains("6BF52A52-394A-11d3-B153-00C04F79FAA6", false)) { - serviceType = "application/x-mplayer2"; - return; - } -#endif - // TODO: add more plugins here - - if (!classId.isEmpty()) { - // We have a clsid, means this is activex (Niko) - serviceType = "application/x-oleobject"; - } -} - -// By default, when an Object element contains an embed tag, we will try to -// create a plugin for the embed tag. However we will not do this for Windows -// media object because our internal ActiveX plugin supports scripting and -// some websites rely on that. The default npdsplay.dll plugin doesn't support -// scripting. -static bool ShouldUseEmbedForObject(HTMLObjectElement* o) -{ -#ifndef DISABLE_ACTIVEX_TYPE_CONVERSION_MPLAYER2 - return true; -#endif - NamedAttrMap* attributes = o->attributes(); - if (attributes) { - for (unsigned i = 0; i < attributes->length(); ++i) { - Attribute* it = attributes->attributeItem(i); - if (it->name().localName().string().foldCase() == "classid") { - // If it's windows media player type. - if (it->value().contains("6BF52A52-394A-11d3-B153-00C04F79FAA6", false) || - it->value().contains("22D6F312-B0F6-11D0-94AB-0080C74C7E95", false)) - return false; - } - } - } - return true; -} - -void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) -{ - String url; - String serviceType; - Vector<String> paramNames; - Vector<String> paramValues; - Frame* frame = m_view->frame(); - - if (element()->hasTagName(objectTag)) { - - HTMLObjectElement* o = static_cast<HTMLObjectElement*>(element()); - - o->setNeedWidgetUpdate(false); - if (!o->isFinishedParsingChildren()) - return; - // Check for a child EMBED tag. - HTMLEmbedElement* embed = 0; - if (ShouldUseEmbedForObject(o)) { - for (Node* child = o->firstChild(); child;) { - if (child->hasTagName(embedTag)) { - embed = static_cast<HTMLEmbedElement*>(child); - break; - } else if (child->hasTagName(objectTag)) - child = child->nextSibling(); // Don't descend into nested OBJECT tags - else - child = child->traverseNextNode(o); // Otherwise descend (EMBEDs may be inside COMMENT tags) - } - } - // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. - HTMLElement *embedOrObject; - if (embed) { - embedOrObject = (HTMLElement *)embed; - url = embed->url(); - serviceType = embed->serviceType(); - } else - embedOrObject = (HTMLElement *)o; - - // If there was no URL or type defined in EMBED, try the OBJECT tag. - if (url.isEmpty()) - url = o->url(); - if (serviceType.isEmpty()) - serviceType = o->serviceType(); - - HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; - - // Scan the PARAM children. - // Get the URL and type from the params if we don't already have them. - // Get the attributes from the params if there is no EMBED tag. - Node *child = o->firstChild(); - while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { - if (child->hasTagName(paramTag)) { - HTMLParamElement* p = static_cast<HTMLParamElement*>(child); - String name = p->name(); - if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) - url = p->value(); - if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { - serviceType = p->value(); - int pos = serviceType.find(";"); - if (pos != -1) - serviceType = serviceType.left(pos); - } - if (!embed && !name.isEmpty()) { - uniqueParamNames.add(name.impl()); - paramNames.append(p->name()); - paramValues.append(p->value()); - } - } - child = child->nextSibling(); - } - - // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag - // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is - // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means - // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, - // else our Java plugin will misinterpret it. [4004531] - String codebase; - if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { - codebase = "codebase"; - uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already - } - - // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. - NamedAttrMap* attributes = embedOrObject->attributes(); - if (attributes) { - for (unsigned i = 0; i < attributes->length(); ++i) { - Attribute* it = attributes->attributeItem(i); - const AtomicString& name = it->name().localName(); - if (embed || !uniqueParamNames.contains(name.impl())) { - paramNames.append(name.string()); - paramValues.append(it->value().string()); - } - } - } - - // If we still don't have a type, try to map from a specific CLASSID to a type. - // However, if we have the embed tag we shouldn't do so because the outer object - // may be converted to application/x-oleobject, while we are not getting the paramaters - // including classid etc for the outer object. - if (serviceType.isEmpty() && !o->classId().isEmpty() && !embed) - mapClassIdToServiceType(o->classId(), serviceType); - - if (!isURLAllowed(document(), url)) - return; - - // Find out if we support fallback content. - m_hasFallbackContent = false; - for (Node *child = o->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { - if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) || // Discount <embed> and <param> - (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) - m_hasFallbackContent = true; - } - - if (onlyCreateNonNetscapePlugins) { - KURL completedURL; - if (!url.isEmpty()) - completedURL = frame->loader()->completeURL(url); - - if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) - return; - } - - bool success = frame->loader()->requestObject(this, url, AtomicString(o->name()), serviceType, paramNames, paramValues); - if (!success && m_hasFallbackContent) - o->renderFallbackContent(); - } else if (element()->hasTagName(embedTag)) { - HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(element()); - o->setNeedWidgetUpdate(false); - url = o->url(); - serviceType = o->serviceType(); - - if (url.isEmpty() && serviceType.isEmpty()) - return; - if (!isURLAllowed(document(), url)) - return; - - // add all attributes set on the embed object - NamedAttrMap* a = o->attributes(); - if (a) { - for (unsigned i = 0; i < a->length(); ++i) { - Attribute* it = a->attributeItem(i); - paramNames.append(it->name().localName().string()); - paramValues.append(it->value().string()); - } - } - - if (onlyCreateNonNetscapePlugins) { - KURL completedURL; - if (!url.isEmpty()) - completedURL = frame->loader()->completeURL(url); - - if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) - return; - - } - - frame->loader()->requestObject(this, url, o->getAttribute(nameAttr), serviceType, paramNames, paramValues); - } -} - -void RenderPartObject::layout() -{ - ASSERT(needsLayout()); - - calcWidth(); - calcHeight(); - adjustOverflowForBoxShadow(); - - RenderPart::layout(); - - if (!m_widget && m_view) - m_view->addWidgetToUpdate(this); - - setNeedsLayout(false); -} - -void RenderPartObject::viewCleared() -{ - if (element() && m_widget && m_widget->isFrameView()) { - FrameView* view = static_cast<FrameView*>(m_widget); - int marginw = -1; - int marginh = -1; - if (element()->hasTagName(iframeTag)) { - HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(element()); - marginw = frame->getMarginWidth(); - marginh = frame->getMarginHeight(); - } - if (marginw != -1) - view->setMarginWidth(marginw); - if (marginh != -1) - view->setMarginHeight(marginh); - } -} - -} diff --git a/webkit/pending/RenderText.cpp b/webkit/pending/RenderText.cpp deleted file mode 100644 index c01e1fc..0000000 --- a/webkit/pending/RenderText.cpp +++ /dev/null @@ -1,1176 +0,0 @@ -/** - * (C) 1999 Lars Knoll (knoll@kde.org) - * (C) 2000 Dirk Mueller (mueller@kde.org) - * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. - * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) - * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.com) - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderText.h" - -#include "CharacterNames.h" -#include "InlineTextBox.h" -#include "Range.h" -#include "RenderArena.h" -#include "RenderBlock.h" -#include "RenderLayer.h" -#include "Text.h" -#include "TextBreakIterator.h" -#include "break_lines.h" -#include <wtf/AlwaysInline.h> - -using namespace std; -using namespace WTF; -using namespace Unicode; - -namespace WebCore { - -// FIXME: Move to StringImpl.h eventually. -static inline bool charactersAreAllASCII(StringImpl* text) -{ - return charactersAreAllASCII(text->characters(), text->length()); -} - -RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str) - : RenderObject(node) - , m_text(str) - , m_firstTextBox(0) - , m_lastTextBox(0) - , m_minWidth(-1) - , m_maxWidth(-1) - , m_selectionState(SelectionNone) - , m_hasTab(false) - , m_linesDirty(false) - , m_containsReversedText(false) - , m_isAllASCII(charactersAreAllASCII(m_text.get())) -{ - ASSERT(m_text); - setRenderText(); - m_text = m_text->replace('\\', backslashAsCurrencySymbol()); -} - -#ifndef NDEBUG - -RenderText::~RenderText() -{ - ASSERT(!m_firstTextBox); - ASSERT(!m_lastTextBox); -} - -#endif - -const char* RenderText::renderName() const -{ - return "RenderText"; -} - -bool RenderText::isTextFragment() const -{ - return false; -} - -bool RenderText::isWordBreak() const -{ - return false; -} - -void RenderText::setStyle(RenderStyle* newStyle) -{ - RenderStyle* oldStyle = style(); - if (oldStyle == newStyle) - return; - - ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE; - ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE; - - RenderObject::setStyle(newStyle); - - if (oldTransform != newStyle->textTransform() || oldSecurity != newStyle->textSecurity() -#if ENABLE(SVG) - || isSVGText() /* All SVG text has to be transformed */ -#endif - ) { - if (RefPtr<StringImpl> textToTransform = originalText()) - setText(textToTransform.release(), true); - } -} - -void RenderText::destroy() -{ - if (!documentBeingDestroyed()) { - if (firstTextBox()) { - if (isBR()) { - RootInlineBox* next = firstTextBox()->root()->nextRootBox(); - if (next) - next->markDirty(); - } - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - box->remove(); - } else if (parent()) - parent()->dirtyLinesFromChangedChild(this); - } - deleteTextBoxes(); - RenderObject::destroy(); -} - -void RenderText::extractTextBox(InlineTextBox* box) -{ - checkConsistency(); - - m_lastTextBox = box->prevTextBox(); - if (box == m_firstTextBox) - m_firstTextBox = 0; - if (box->prevTextBox()) - box->prevTextBox()->setNextLineBox(0); - box->setPreviousLineBox(0); - for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox()) - curr->setExtracted(); - - checkConsistency(); -} - -void RenderText::attachTextBox(InlineTextBox* box) -{ - checkConsistency(); - - if (m_lastTextBox) { - m_lastTextBox->setNextLineBox(box); - box->setPreviousLineBox(m_lastTextBox); - } else - m_firstTextBox = box; - InlineTextBox* last = box; - for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) { - curr->setExtracted(false); - last = curr; - } - m_lastTextBox = last; - - checkConsistency(); -} - -void RenderText::removeTextBox(InlineTextBox* box) -{ - checkConsistency(); - - if (box == m_firstTextBox) - m_firstTextBox = box->nextTextBox(); - if (box == m_lastTextBox) - m_lastTextBox = box->prevTextBox(); - if (box->nextTextBox()) - box->nextTextBox()->setPreviousLineBox(box->prevTextBox()); - if (box->prevTextBox()) - box->prevTextBox()->setNextLineBox(box->nextTextBox()); - - checkConsistency(); -} - -void RenderText::deleteTextBoxes() -{ - if (firstTextBox()) { - RenderArena* arena = renderArena(); - InlineTextBox* next; - for (InlineTextBox* curr = firstTextBox(); curr; curr = next) { - next = curr->nextTextBox(); - curr->destroy(arena); - } - m_firstTextBox = m_lastTextBox = 0; - } -} - -PassRefPtr<StringImpl> RenderText::originalText() const -{ - Node* e = element(); - return e ? static_cast<Text*>(e)->string() : 0; -} - -void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool) -{ - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - rects.append(IntRect(tx + box->xPos(), ty + box->yPos(), box->width(), box->height())); -} - -void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight) -{ - // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX - // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this - // function to take ints causes various internal mismatches. But selectionRect takes ints, and - // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but - // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX. - ASSERT(end == UINT_MAX || end <= INT_MAX); - ASSERT(start <= INT_MAX); - start = min(start, static_cast<unsigned>(INT_MAX)); - end = min(end, static_cast<unsigned>(INT_MAX)); - - int x, y; - absolutePositionForContent(x, y); - - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { - // Note: box->end() returns the index of the last character, not the index past it - if (start <= box->start() && box->end() < end) { - IntRect r = IntRect(x + box->xPos(), y + box->yPos(), box->width(), box->height()); - if (useSelectionHeight) { - IntRect selectionRect = box->selectionRect(x, y, start, end); - r.setHeight(selectionRect.height()); - r.setY(selectionRect.y()); - } - rects.append(r); - } else { - unsigned realEnd = min(box->end() + 1, end); - IntRect r = box->selectionRect(x, y, start, realEnd); - if (!r.isEmpty()) { - if (!useSelectionHeight) { - // change the height and y position because selectionRect uses selection-specific values - r.setHeight(box->height()); - r.setY(y + box->yPos()); - } - rects.append(r); - } - } - } -} - -InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const -{ - // The text runs point to parts of the RenderText's m_text - // (they don't include '\n') - // Find the text run that includes the character at offset - // and return pos, which is the position of the char in the run. - - if (!m_firstTextBox) - return 0; - - InlineTextBox* s = m_firstTextBox; - int off = s->m_len; - while (offset > off && s->nextTextBox()) { - s = s->nextTextBox(); - off = s->m_start + s->m_len; - } - // we are now in the correct text run - pos = (offset > off ? s->m_len : s->m_len - (off - offset) ); - return s; -} - -VisiblePosition RenderText::positionForCoordinates(int x, int y) -{ - if (!firstTextBox() || textLength() == 0) - return VisiblePosition(element(), 0, DOWNSTREAM); - - // Get the offset for the position, since this will take rtl text into account. - int offset; - - // FIXME: We should be able to roll these special cases into the general cases in the loop below. - if (firstTextBox() && y < firstTextBox()->root()->bottomOverflow() && x < firstTextBox()->m_x) { - // at the y coordinate of the first line or above - // and the x coordinate is to the left of the first text box left edge - offset = firstTextBox()->offsetForPosition(x); - return VisiblePosition(element(), offset + firstTextBox()->m_start, DOWNSTREAM); - } - if (lastTextBox() && y >= lastTextBox()->root()->topOverflow() && x >= lastTextBox()->m_x + lastTextBox()->m_width) { - // at the y coordinate of the last line or below - // and the x coordinate is to the right of the last text box right edge - offset = lastTextBox()->offsetForPosition(x); - return VisiblePosition(element(), offset + lastTextBox()->m_start, DOWNSTREAM); - } - - InlineTextBox* lastBoxAbove = 0; - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { - if (y >= box->root()->topOverflow()) { - int bottom = box->root()->nextRootBox() ? box->root()->nextRootBox()->topOverflow() : box->root()->bottomOverflow(); - if (y < bottom) { - offset = box->offsetForPosition(x); - - if (x == box->m_x) - // the x coordinate is equal to the left edge of this box - // the affinity must be downstream so the position doesn't jump back to the previous line - return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM); - - if (x < box->m_x + box->m_width) - // and the x coordinate is to the left of the right edge of this box - // check to see if position goes in this box - return VisiblePosition(element(), offset + box->m_start, offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); - - if (!box->prevOnLine() && x < box->m_x) - // box is first on line - // and the x coordinate is to the left of the first text box left edge - return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM); - - if (!box->nextOnLine()) - // box is last on line - // and the x coordinate is to the right of the last text box right edge - // generate VisiblePosition, use UPSTREAM affinity if possible - return VisiblePosition(element(), offset + box->m_start, offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); - } - lastBoxAbove = box; - } - } - - return VisiblePosition(element(), lastBoxAbove ? lastBoxAbove->m_start + lastBoxAbove->m_len : 0, DOWNSTREAM); -} - -IntRect RenderText::caretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) -{ - if (!inlineBox) - return IntRect(); - - ASSERT(inlineBox->isInlineTextBox()); - if (!inlineBox->isInlineTextBox()) - return IntRect(); - - InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox); - - int height = box->root()->bottomOverflow() - box->root()->topOverflow(); - int top = box->root()->topOverflow(); - - int left = box->positionForOffset(caretOffset); - - int rootLeft = box->root()->xPos(); - // FIXME: should we use the width of the root inline box or the - // width of the containing block for this? - if (extraWidthToEndOfLine) - *extraWidthToEndOfLine = (box->root()->width() + rootLeft) - (left + 1); - - int absx, absy; - absolutePositionForContent(absx, absy); - left += absx; - top += absy; - - RenderBlock* cb = containingBlock(); - if (style()->autoWrap()) { - int availableWidth = cb->lineWidth(top); - if (box->direction() == LTR) - left = min(left, absx + rootLeft + availableWidth - 1); - else - left = max(left, absx + rootLeft); - } - - return IntRect(left, top, 1, height); -} - -ALWAYS_INLINE int RenderText::widthFromCache(const Font& f, int start, int len, int xPos) const -{ - if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII) { - int monospaceCharacterWidth = f.spaceWidth(); - int tabWidth = allowTabs() ? monospaceCharacterWidth * 8 : 0; - int w = 0; - bool isSpace; - bool previousCharWasSpace = true; // FIXME: Preserves historical behavior, but seems wrong for start > 0. - for (int i = start; i < start + len; i++) { - char c = (*m_text)[i]; - if (c <= ' ') { - if (c == ' ' || c == '\n') { - w += monospaceCharacterWidth; - isSpace = true; - } else if (c == '\t') { - w += tabWidth ? tabWidth - ((xPos + w) % tabWidth) : monospaceCharacterWidth; - isSpace = true; - } else - isSpace = false; - } else { - w += monospaceCharacterWidth; - isSpace = false; - } - if (isSpace && !previousCharWasSpace) - w += f.wordSpacing(); - previousCharWasSpace = isSpace; - } - return w; - } - - return f.width(TextRun(text()->characters() + start, len, allowTabs(), xPos)); -} - -void RenderText::trimmedPrefWidths(int leadWidth, - int& beginMinW, bool& beginWS, - int& endMinW, bool& endWS, - bool& hasBreakableChar, bool& hasBreak, - int& beginMaxW, int& endMaxW, - int& minW, int& maxW, bool& stripFrontSpaces) -{ - bool collapseWhiteSpace = style()->collapseWhiteSpace(); - if (!collapseWhiteSpace) - stripFrontSpaces = false; - - if (m_hasTab || prefWidthsDirty()) - calcPrefWidths(leadWidth); - - beginWS = !stripFrontSpaces && m_hasBeginWS; - endWS = m_hasEndWS; - - int len = textLength(); - - if (!len || (stripFrontSpaces && m_text->containsOnlyWhitespace())) { - beginMinW = 0; - endMinW = 0; - beginMaxW = 0; - endMaxW = 0; - minW = 0; - maxW = 0; - hasBreak = false; - return; - } - - minW = m_minWidth; - maxW = m_maxWidth; - - beginMinW = m_beginMinWidth; - endMinW = m_endMinWidth; - - hasBreakableChar = m_hasBreakableChar; - hasBreak = m_hasBreak; - - if ((*m_text)[0] == ' ' || ((*m_text)[0] == '\n' && !style()->preserveNewline()) || (*m_text)[0] == '\t') { - const Font& f = style()->font(); // FIXME: This ignores first-line. - if (stripFrontSpaces) { - const UChar space = ' '; - int spaceWidth = f.width(TextRun(&space, 1)); - maxW -= spaceWidth; - } else - maxW += f.wordSpacing(); - } - - stripFrontSpaces = collapseWhiteSpace && m_hasEndWS; - - if (!style()->autoWrap() || minW > maxW) - minW = maxW; - - // Compute our max widths by scanning the string for newlines. - if (hasBreak) { - const Font& f = style()->font(); // FIXME: This ignores first-line. - bool firstLine = true; - beginMaxW = maxW; - endMaxW = maxW; - for (int i = 0; i < len; i++) { - int linelen = 0; - while (i + linelen < len && (*m_text)[i + linelen] != '\n') - linelen++; - - if (linelen) { - endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW); - if (firstLine) { - firstLine = false; - leadWidth = 0; - beginMaxW = endMaxW; - } - i += linelen; - } else if (firstLine) { - beginMaxW = 0; - firstLine = false; - leadWidth = 0; - } - - if (i == len - 1) - // A <pre> run that ends with a newline, as in, e.g., - // <pre>Some text\n\n<span>More text</pre> - endMaxW = 0; - } - } -} - -static inline bool isSpaceAccordingToStyle(UChar c, RenderStyle* style) -{ - return c == ' ' || (c == noBreakSpace && style->nbspMode() == SPACE); -} - -int RenderText::minPrefWidth() const -{ - if (prefWidthsDirty()) - const_cast<RenderText*>(this)->calcPrefWidths(0); - - return m_minWidth; -} - -int RenderText::maxPrefWidth() const -{ - if (prefWidthsDirty()) - const_cast<RenderText*>(this)->calcPrefWidths(0); - - return m_maxWidth; -} - -void RenderText::calcPrefWidths(int leadWidth) -{ - ASSERT(m_hasTab || prefWidthsDirty()); - - m_minWidth = 0; - m_beginMinWidth = 0; - m_endMinWidth = 0; - m_maxWidth = 0; - - if (isBR()) - return; - - int currMinWidth = 0; - int currMaxWidth = 0; - m_hasBreakableChar = false; - m_hasBreak = false; - m_hasTab = false; - m_hasBeginWS = false; - m_hasEndWS = false; - - const Font& f = style()->font(); // FIXME: This ignores first-line. - int wordSpacing = style()->wordSpacing(); - int len = textLength(); - //const UChar* txt = characters(); - bool needsWordSpacing = false; - bool ignoringSpaces = false; - bool isSpace = false; - bool firstWord = true; - bool firstLine = true; - int nextBreakable = -1; - int lastWordBoundary = 0; - - bool breakNBSP = style()->autoWrap() && style()->nbspMode() == SPACE; - bool breakAll = (style()->wordBreak() == BreakAllWordBreak || style()->wordBreak() == BreakWordBreak) && style()->autoWrap(); - - for (int i = 0; i < len; i++) { - UChar c = (*m_text)[i]; - - bool previousCharacterIsSpace = isSpace; - - bool isNewline = false; - if (c == '\n') { - if (style()->preserveNewline()) { - m_hasBreak = true; - isNewline = true; - isSpace = false; - } else - isSpace = true; - } else if (c == '\t') { - if (!style()->collapseWhiteSpace()) { - m_hasTab = true; - isSpace = false; - } else - isSpace = true; - } else - isSpace = c == ' '; - - if ((isSpace || isNewline) && !i) - m_hasBeginWS = true; - if ((isSpace || isNewline) && i == len - 1) - m_hasEndWS = true; - - if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace) - ignoringSpaces = true; - - if (ignoringSpaces && !isSpace) - ignoringSpaces = false; - - // Ignore spaces and soft hyphens - if (ignoringSpaces) { - ASSERT(lastWordBoundary == i); - lastWordBoundary++; - continue; - } else if (c == softHyphen) { - currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth); - lastWordBoundary = i + 1; - continue; - } - - bool hasBreak = breakAll || isBreakable(m_text->characters(), i, len, nextBreakable, breakNBSP); - bool betweenWords = true; - int j = i; - while (c != '\n' && !isSpaceAccordingToStyle(c, style()) && c != '\t' && c != softHyphen) { - j++; - if (j == len) - break; - c = (*m_text)[j]; - if (isBreakable(m_text->characters(), j, len, nextBreakable, breakNBSP)) - break; - if (breakAll) { - betweenWords = false; - break; - } - } - - int wordLen = j - i; - if (wordLen) { - int w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth); - currMinWidth += w; - if (betweenWords) { - if (lastWordBoundary == i) - currMaxWidth += w; - else - currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth); - lastWordBoundary = j; - } - - bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style()); - bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c); - if (j < len && style()->autoWrap()) - m_hasBreakableChar = true; - - // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the - // last word in the run. - if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j)) - currMaxWidth += wordSpacing; - - if (firstWord) { - firstWord = false; - // If the first character in the run is breakable, then we consider ourselves to have a beginning - // minimum width of 0, since a break could occur right before our run starts, preventing us from ever - // being appended to a previous text run when considering the total minimum width of the containing block. - if (hasBreak) - m_hasBreakableChar = true; - m_beginMinWidth = hasBreak ? 0 : w; - } - m_endMinWidth = w; - - if (currMinWidth > m_minWidth) - m_minWidth = currMinWidth; - currMinWidth = 0; - - i += wordLen - 1; - } else { - // Nowrap can never be broken, so don't bother setting the - // breakable character boolean. Pre can only be broken if we encounter a newline. - if (style()->autoWrap() || isNewline) - m_hasBreakableChar = true; - - if (currMinWidth > m_minWidth) - m_minWidth = currMinWidth; - currMinWidth = 0; - - if (isNewline) { // Only set if preserveNewline was true and we saw a newline. - if (firstLine) { - firstLine = false; - leadWidth = 0; - if (!style()->autoWrap()) - m_beginMinWidth = currMaxWidth; - } - - if (currMaxWidth > m_maxWidth) - m_maxWidth = currMaxWidth; - currMaxWidth = 0; - } else { - currMaxWidth += f.width(TextRun(m_text->characters() + i, 1, allowTabs(), leadWidth + currMaxWidth)); - needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; - } - ASSERT(lastWordBoundary == i); - lastWordBoundary++; - } - } - - if (needsWordSpacing && len > 1 || ignoringSpaces && !firstWord) - currMaxWidth += wordSpacing; - - m_minWidth = max(currMinWidth, m_minWidth); - m_maxWidth = max(currMaxWidth, m_maxWidth); - - if (!style()->autoWrap()) - m_minWidth = m_maxWidth; - - if (style()->whiteSpace() == PRE) { - if (firstLine) - m_beginMinWidth = m_maxWidth; - m_endMinWidth = currMaxWidth; - } - - setPrefWidthsDirty(false); -} - -bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const -{ - unsigned currPos; - for (currPos = from; - currPos < from + len && ((*m_text)[currPos] == '\n' || (*m_text)[currPos] == ' ' || (*m_text)[currPos] == '\t'); - currPos++) { } - return currPos >= (from + len); -} - -int RenderText::minXPos() const -{ - if (!m_firstTextBox) - return 0; - - // FIXME: we should not use an arbitrary value like this. Perhaps we should use INT_MAX. - int minXPos = 6666666; - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - minXPos = min(minXPos, static_cast<int>(box->m_x)); - return minXPos; -} - -int RenderText::xPos() const -{ - return m_firstTextBox ? m_firstTextBox->m_x : 0; -} - -int RenderText::yPos() const -{ - return m_firstTextBox ? m_firstTextBox->m_y : 0; -} - -void RenderText::setSelectionState(SelectionState state) -{ - InlineTextBox* box; - - m_selectionState = state; - if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) { - int startPos, endPos; - selectionStartEnd(startPos, endPos); - if (selectionState() == SelectionStart) { - endPos = textLength(); - - // to handle selection from end of text to end of line - if (startPos != 0 && startPos == endPos) - startPos = endPos - 1; - } else if (selectionState() == SelectionEnd) - startPos = 0; - - for (box = firstTextBox(); box; box = box->nextTextBox()) { - if (box->isSelected(startPos, endPos)) { - RootInlineBox* line = box->root(); - if (line) - line->setHasSelectedChildren(true); - } - } - } else { - for (box = firstTextBox(); box; box = box->nextTextBox()) { - RootInlineBox* line = box->root(); - if (line) - line->setHasSelectedChildren(state == SelectionInside); - } - } - - containingBlock()->setSelectionState(state); -} - -void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force) -{ - unsigned oldLen = textLength(); - unsigned newLen = text->length(); - int delta = newLen - oldLen; - unsigned end = len ? offset + len - 1 : offset; - - RootInlineBox* firstRootBox = 0; - RootInlineBox* lastRootBox = 0; - - bool dirtiedLines = false; - - // Dirty all text boxes that include characters in between offset and offset+len. - for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { - // Text run is entirely before the affected range. - if (curr->end() < offset) - continue; - - // Text run is entirely after the affected range. - if (curr->start() > end) { - curr->offsetRun(delta); - RootInlineBox* root = curr->root(); - if (!firstRootBox) { - firstRootBox = root; - if (!dirtiedLines) { - // The affected area was in between two runs. Go ahead and mark the root box of - // the run after the affected area as dirty. - firstRootBox->markDirty(); - dirtiedLines = true; - } - } - lastRootBox = root; - } else if (curr->end() >= offset && curr->end() <= end) { - // Text run overlaps with the left end of the affected range. - curr->dirtyLineBoxes(); - dirtiedLines = true; - } else if (curr->start() <= offset && curr->end() >= end) { - // Text run subsumes the affected range. - curr->dirtyLineBoxes(); - dirtiedLines = true; - } else if (curr->start() <= end && curr->end() >= end) { - // Text run overlaps with right end of the affected range. - curr->dirtyLineBoxes(); - dirtiedLines = true; - } - } - - // Now we have to walk all of the clean lines and adjust their cached line break information - // to reflect our updated offsets. - if (lastRootBox) - lastRootBox = lastRootBox->nextRootBox(); - if (firstRootBox) { - RootInlineBox* prev = firstRootBox->prevRootBox(); - if (prev) - firstRootBox = prev; - } - for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) { - if (curr->lineBreakObj() == this && curr->lineBreakPos() > end) - curr->setLineBreakPos(curr->lineBreakPos() + delta); - } - - // If the text node is empty, dirty the line where new text will be inserted. - if (!firstTextBox() && parent()) { - parent()->dirtyLinesFromChangedChild(this); - dirtiedLines = true; - } - - m_linesDirty = dirtiedLines; - setText(text, force); -} - -static inline bool isInlineFlowOrEmptyText(RenderObject* o) -{ - if (o->isInlineFlow()) - return true; - if (!o->isText()) - return false; - StringImpl* text = static_cast<RenderText*>(o)->text(); - if (!text) - return true; - return !text->length(); -} - -UChar RenderText::previousCharacter() -{ - // find previous text renderer if one exists - RenderObject* previousText = this; - while ((previousText = previousText->previousInPreOrder())) - if (!isInlineFlowOrEmptyText(previousText)) - break; - UChar prev = ' '; - if (previousText && previousText->isText()) - if (StringImpl* previousString = static_cast<RenderText*>(previousText)->text()) - prev = (*previousString)[previousString->length() - 1]; - return prev; -} - -void RenderText::setTextInternal(PassRefPtr<StringImpl> text) -{ - m_text = text; - ASSERT(m_text); - - m_text = m_text->replace('\\', backslashAsCurrencySymbol()); - -#if ENABLE(SVG) - if (isSVGText()) { - if (style() && style()->whiteSpace() == PRE) { - // Spec: When xml:space="preserve", the SVG user agent will do the following using a - // copy of the original character data content. It will convert all newline and tab - // characters into space characters. Then, it will draw all space characters, including - // leading, trailing and multiple contiguous space characters. - - m_text = m_text->replace('\n', ' '); - - // If xml:space="preserve" is set, white-space is set to "pre", which - // preserves leading, trailing & contiguous space character for us. - } else { - // Spec: When xml:space="default", the SVG user agent will do the following using a - // copy of the original character data content. First, it will remove all newline - // characters. Then it will convert all tab characters into space characters. - // Then, it will strip off all leading and trailing space characters. - // Then, all contiguous space characters will be consolidated. - - m_text = m_text->replace('\n', StringImpl::empty()); - - // If xml:space="default" is set, white-space is set to "nowrap", which handles - // leading, trailing & contiguous space character removal for us. - } - - m_text = m_text->replace('\t', ' '); - } -#endif - - if (style()) { - switch (style()->textTransform()) { - case TTNONE: - break; - case CAPITALIZE: { - m_text = m_text->capitalize(previousCharacter()); - break; - } - case UPPERCASE: - m_text = m_text->upper(); - break; - case LOWERCASE: - m_text = m_text->lower(); - break; - } - - // We use the same characters here as for list markers. - // See the listMarkerText function in RenderListMarker.cpp. - switch (style()->textSecurity()) { - case TSNONE: - break; - case TSCIRCLE: - m_text = m_text->secure(whiteBullet); - break; - case TSDISC: - m_text = m_text->secure(bullet); - break; - case TSSQUARE: - m_text = m_text->secure(blackSquare); - } - } - - ASSERT(m_text); - ASSERT(!isBR() || (textLength() == 1 && (*m_text)[0] == '\n')); - - m_isAllASCII = charactersAreAllASCII(m_text.get()); -} - -void RenderText::setText(PassRefPtr<StringImpl> text, bool force) -{ - ASSERT(text); - - if (!force && equal(m_text.get(), text.get())) - return; - - setTextInternal(text); - setNeedsLayoutAndPrefWidthsRecalc(); -} - -int RenderText::height() const -{ - int retval = 0; - if (firstTextBox()) - retval = lastTextBox()->m_y + lastTextBox()->height() - firstTextBox()->m_y; - return retval; -} - -int RenderText::lineHeight(bool firstLine, bool) const -{ - // Always use the interior line height of the parent (e.g., if our parent is an inline block). - return parent()->lineHeight(firstLine, true); -} - -void RenderText::dirtyLineBoxes(bool fullLayout, bool) -{ - if (fullLayout) - deleteTextBoxes(); - else if (!m_linesDirty) { - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - box->dirtyLineBoxes(); - } - m_linesDirty = false; -} - -InlineTextBox* RenderText::createInlineTextBox() -{ - return new (renderArena()) InlineTextBox(this); -} - -InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox, bool) -{ - ASSERT(!isRootLineBox); - InlineTextBox* textBox = createInlineTextBox(); - if (!m_firstTextBox) - m_firstTextBox = m_lastTextBox = textBox; - else { - m_lastTextBox->setNextLineBox(textBox); - textBox->setPreviousLineBox(m_lastTextBox); - m_lastTextBox = textBox; - } - return textBox; -} - -void RenderText::position(InlineBox* box) -{ - InlineTextBox* s = static_cast<InlineTextBox*>(box); - - // FIXME: should not be needed!!! - if (!s->m_len) { - // We want the box to be destroyed. - s->remove(); - s->destroy(renderArena()); - m_firstTextBox = m_lastTextBox = 0; - return; - } - - m_containsReversedText |= s->direction() == RTL; -} - -unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bool firstLine) const -{ - if (from >= textLength()) - return 0; - - if (from + len > textLength()) - len = textLength() - from; - - return width(from, len, style(firstLine)->font(), xPos); -} - -unsigned int RenderText::width(unsigned int from, unsigned int len, const Font& f, int xPos) const -{ - if (!characters() || from > textLength()) - return 0; - - if (from + len > textLength()) - len = textLength() - from; - - int w; - if (&f == &style()->font()) { - if (!style()->preserveNewline() && !from && len == textLength()) - w = maxPrefWidth(); - else - w = widthFromCache(f, from, len, xPos); - } else - w = f.width(TextRun(text()->characters() + from, len, allowTabs(), xPos)); - - return w; -} - -int RenderText::width() const -{ - // FIXME: we should not use an arbitrary value like this. Perhaps we should use INT_MAX. - int minx = 100000000; - int maxx = 0; - // slooow - for (InlineTextBox* s = firstTextBox(); s; s = s->nextTextBox()) { - if (s->m_x < minx) - minx = s->m_x; - if (s->m_x + s->m_width > maxx) - maxx = s->m_x + s->m_width; - } - - return max(0, maxx - minx); -} - -IntRect RenderText::absoluteClippedOverflowRect() -{ - RenderObject* cb = containingBlock(); - return cb->absoluteClippedOverflowRect(); -} - -IntRect RenderText::selectionRect(bool clipToVisibleContent) -{ - ASSERT(!needsLayout()); - - IntRect rect; - if (selectionState() == SelectionNone) - return rect; - RenderBlock* cb = containingBlock(); - if (!cb) - return rect; - - // Now calculate startPos and endPos for painting selection. - // We include a selection while endPos > 0 - int startPos, endPos; - if (selectionState() == SelectionInside) { - // We are fully selected. - startPos = 0; - endPos = textLength(); - } else { - selectionStartEnd(startPos, endPos); - if (selectionState() == SelectionStart) - endPos = textLength(); - else if (selectionState() == SelectionEnd) - startPos = 0; - } - - if (startPos == endPos) - return rect; - - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - rect.unite(box->selectionRect(0, 0, startPos, endPos)); - - if (clipToVisibleContent) - computeAbsoluteRepaintRect(rect); - else { - if (cb->hasColumns()) - cb->adjustRectForColumns(rect); - int absx, absy; - absolutePosition(absx, absy); - rect.move(absx, absy); - } - - return rect; -} - -int RenderText::verticalPositionHint(bool firstLine) const -{ - if (parent()->isReplaced()) - return 0; // Treat inline blocks just like blocks. There can't be any vertical position hint. - return parent()->verticalPositionHint(firstLine); -} - -int RenderText::caretMinOffset() const -{ - InlineTextBox* box = firstTextBox(); - if (!box) - return 0; - int minOffset = box->m_start; - for (box = box->nextTextBox(); box; box = box->nextTextBox()) - minOffset = min(minOffset, box->m_start); - return minOffset; -} - -int RenderText::caretMaxOffset() const -{ - InlineTextBox* box = lastTextBox(); - if (!box) - return textLength(); - int maxOffset = box->m_start + box->m_len; - for (box = box->prevTextBox(); box; box = box->prevTextBox()) - maxOffset = max(maxOffset, box->m_start + box->m_len); - return maxOffset; -} - -unsigned RenderText::caretMaxRenderedOffset() const -{ - int l = 0; - for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) - l += box->m_len; - return l; -} - -int RenderText::previousOffset(int current) const -{ - StringImpl* si = m_text.get(); - TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length()); - if (!iterator) - return current - 1; - - long result = textBreakPreceding(iterator, current); - if (result == TextBreakDone) - result = current - 1; - - return result; -} - -int RenderText::nextOffset(int current) const -{ - StringImpl* si = m_text.get(); - TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length()); - if (!iterator) - return current + 1; - - long result = textBreakFollowing(iterator, current); - if (result == TextBreakDone) - result = current + 1; - - return result; -} - -#ifndef NDEBUG - -void RenderText::checkConsistency() const -{ -#ifdef CHECK_CONSISTENCY - const InlineTextBox* prev = 0; - for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) { - ASSERT(child->object() == this); - ASSERT(child->prevTextBox() == prev); - prev = child; - } - ASSERT(prev == m_lastTextBox); -#endif -} - -#endif - -} // namespace WebCore diff --git a/webkit/pending/RenderTextControl.cpp b/webkit/pending/RenderTextControl.cpp deleted file mode 100644 index ee74714..0000000 --- a/webkit/pending/RenderTextControl.cpp +++ /dev/null @@ -1,1306 +0,0 @@ -/** - * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "RenderTextControl.h" - -#include "CharacterNames.h" -#include "CSSStyleSelector.h" -#include "Document.h" -#include "Editor.h" -#include "EditorClient.h" -#include "Event.h" -#include "EventNames.h" -#include "FontSelector.h" -#include "Frame.h" -#include "HTMLBRElement.h" -#include "HTMLInputElement.h" -#include "HTMLNames.h" -#include "HTMLTextAreaElement.h" -#include "HitTestResult.h" -#include "LocalizedStrings.h" -#include "MouseEvent.h" -#include "PlatformKeyboardEvent.h" -#include "PlatformScrollBar.h" -#include "RenderTheme.h" -#include "SearchPopupMenu.h" -#include "SelectionController.h" -#include "Settings.h" -#include "SimpleFontData.h" -#include "Text.h" -#include "TextControlInnerElements.h" -#include "TextIterator.h" -#include "htmlediting.h" -#include "visible_units.h" -#include <math.h> - -using namespace std; - -namespace WebCore { - -using namespace EventNames; -using namespace HTMLNames; - -class RenderTextControlInnerBlock : public RenderBlock { -public: - RenderTextControlInnerBlock(Node* node, bool isMultiLine) : RenderBlock(node), m_multiLine(isMultiLine) { } - - virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); - virtual VisiblePosition positionForCoordinates(int x, int y); -private: - bool m_multiLine; -}; - -VisiblePosition RenderTextControlInnerBlock::positionForCoordinates(int x, int y) -{ - int contentsX = x; - int contentsY = y; - - // Multiline text controls have the scroll on shadowAncestorNode, so we need to take that - // into account here. - if (m_multiLine) { - RenderTextControl* renderer = static_cast<RenderTextControl*>(node()->shadowAncestorNode()->renderer()); - if (renderer->hasOverflowClip()) - renderer->layer()->scrollOffset(contentsX, contentsY); - } - - return RenderBlock::positionForCoordinates(contentsX, contentsY); -} - -RenderTextControl::RenderTextControl(Node* node, bool multiLine) - : RenderBlock(node) - , m_dirty(false) - , m_multiLine(multiLine) - , m_placeholderVisible(false) - , m_userEdited(false) - , m_shouldDrawCapsLockIndicator(false) - , m_searchPopup(0) - , m_searchPopupIsVisible(false) - , m_searchEventTimer(this, &RenderTextControl::searchEventTimerFired) -{ -} - -RenderTextControl::~RenderTextControl() -{ - if (m_searchPopup) { - m_searchPopup->disconnectClient(); - m_searchPopup = 0; - } - if (m_multiLine && node()) - static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed(); - // The children renderers have already been destroyed by destroyLeftoverChildren - if (m_innerBlock) - m_innerBlock->detach(); - else if (m_innerText) - m_innerText->detach(); -} - -void RenderTextControl::setStyle(RenderStyle* style) -{ - RenderBlock::setStyle(style); - if (m_innerBlock) { - // We may have set the width and the height in the old style in layout(). Reset them now to avoid - // getting a spurious layout hint. - m_innerBlock->renderer()->style()->setHeight(Length()); - m_innerBlock->renderer()->style()->setWidth(Length()); - m_innerBlock->renderer()->setStyle(createInnerBlockStyle(style)); - } - - if (m_innerText) { - RenderBlock* textBlockRenderer = static_cast<RenderBlock*>(m_innerText->renderer()); - RenderStyle* textBlockStyle = createInnerTextStyle(style); - // We may have set the width and the height in the old style in layout(). Reset them now to avoid - // getting a spurious layout hint. - textBlockRenderer->style()->setHeight(Length()); - textBlockRenderer->style()->setWidth(Length()); - textBlockRenderer->setStyle(textBlockStyle); - for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) { - if (n->renderer()) - n->renderer()->setStyle(textBlockStyle); - } - } - if (m_resultsButton) - m_resultsButton->renderer()->setStyle(createResultsButtonStyle(style)); - - if (m_cancelButton) - m_cancelButton->renderer()->setStyle(createCancelButtonStyle(style)); - - if (!m_multiLine) - setHasOverflowClip(false); - - setReplaced(isInline()); -} - -static Color disabledTextColor(const Color& textColor, const Color& backgroundColor) -{ - // The explcit check for black is an optimization for the 99% case (black on white). - // This also means that black on black will turn into grey on black when disabled. - if (textColor.rgb() == Color::black || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white)) - return textColor.light(); - return textColor.dark(); -} - -RenderStyle* RenderTextControl::createInnerBlockStyle(RenderStyle* startStyle) -{ - RenderStyle* innerBlockStyle = new (renderArena()) RenderStyle(); - - innerBlockStyle->inheritFrom(startStyle); - innerBlockStyle->setDisplay(BLOCK); - innerBlockStyle->setDirection(LTR); - // We don't want the shadow dom to be editable, so we set this block to read-only in case the input itself is editable. - innerBlockStyle->setUserModify(READ_ONLY); - - return innerBlockStyle; -} - -RenderStyle* RenderTextControl::createInnerTextStyle(RenderStyle* startStyle) -{ - RenderStyle* textBlockStyle = new (renderArena()) RenderStyle(); - HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node()); - - textBlockStyle->inheritFrom(startStyle); - // The inner block, if present, always has its direction set to LTR, - // so we need to inherit the direction from the element. - textBlockStyle->setDirection(style()->direction()); - textBlockStyle->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); - if (m_innerBlock) - textBlockStyle->setDisplay(INLINE_BLOCK); - else - textBlockStyle->setDisplay(BLOCK); - - if (m_multiLine) { - // The inner text block should not ever have scrollbars. - textBlockStyle->setOverflowX(OVISIBLE); - textBlockStyle->setOverflowY(OVISIBLE); - - // Set word wrap property based on wrap attribute. - if (!static_cast<HTMLTextAreaElement*>(element)->shouldWrapText()) { - textBlockStyle->setWhiteSpace(PRE); - textBlockStyle->setWordWrap(NormalWordWrap); - } else { - textBlockStyle->setWhiteSpace(PRE_WRAP); - textBlockStyle->setWordWrap(BreakWordWrap); - } - } else { - textBlockStyle->setWhiteSpace(PRE); - textBlockStyle->setWordWrap(NormalWordWrap); - textBlockStyle->setOverflowX(OHIDDEN); - textBlockStyle->setOverflowY(OHIDDEN); - - // Do not allow line-height to be smaller than our default. - if (textBlockStyle->font().lineSpacing() > lineHeight(true, true)) - textBlockStyle->setLineHeight(Length(-100.0f, Percent)); - } - - if (!m_multiLine) { - // We're adding one extra pixel of padding to match WinIE. - textBlockStyle->setPaddingLeft(Length(1, Fixed)); - textBlockStyle->setPaddingRight(Length(1, Fixed)); - } - - if (!element->isEnabled()) - textBlockStyle->setColor(disabledTextColor(startStyle->color(), startStyle->backgroundColor())); - - return textBlockStyle; -} - -RenderStyle* RenderTextControl::createResultsButtonStyle(RenderStyle* startStyle) -{ - ASSERT(!m_multiLine); - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); - RenderStyle* resultsBlockStyle; - if (input->maxResults() < 0) - resultsBlockStyle = getPseudoStyle(RenderStyle::SEARCH_DECORATION); - else if (!input->maxResults()) - resultsBlockStyle = getPseudoStyle(RenderStyle::SEARCH_RESULTS_DECORATION); - else - resultsBlockStyle = getPseudoStyle(RenderStyle::SEARCH_RESULTS_BUTTON); - - if (!resultsBlockStyle) - resultsBlockStyle = new (renderArena()) RenderStyle(); - - if (startStyle) - resultsBlockStyle->inheritFrom(startStyle); - - resultsBlockStyle->setDisplay(INLINE_BLOCK); - - return resultsBlockStyle; -} - -RenderStyle* RenderTextControl::createCancelButtonStyle(RenderStyle* startStyle) -{ - RenderStyle* cancelBlockStyle; - - if (RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SEARCH_CANCEL_BUTTON)) - // We may be sharing style with another search field, but we must not share the cancel button style. - cancelBlockStyle = new (renderArena()) RenderStyle(*pseudoStyle); - else - cancelBlockStyle = new (renderArena()) RenderStyle(); - - if (startStyle) - cancelBlockStyle->inheritFrom(startStyle); - - cancelBlockStyle->setDisplay(INLINE_BLOCK); - - updateCancelButtonVisibility(cancelBlockStyle); - - return cancelBlockStyle; -} - -void RenderTextControl::updatePlaceholder() -{ - bool oldPlaceholderVisible = m_placeholderVisible; - - String placeholder; - if (!m_multiLine) { - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); - if (input->value().isEmpty() && document()->focusedNode() != node()) - placeholder = input->getAttribute(placeholderAttr); - } - - if (!placeholder.isEmpty() || m_placeholderVisible) { - ExceptionCode ec = 0; - m_innerText->setInnerText(placeholder, ec); - m_placeholderVisible = !placeholder.isEmpty(); - } - - Color color; - if (!placeholder.isEmpty()) - color = Color::darkGray; - else if (node()->isEnabled()) - color = style()->color(); - else - color = disabledTextColor(style()->color(), style()->backgroundColor()); - - RenderObject* renderer = m_innerText->renderer(); - RenderStyle* innerStyle = renderer->style(); - if (innerStyle->color() != color) { - innerStyle->setColor(color); - renderer->repaint(); - } - - // temporary disable textSecurity if placeholder is visible - if (style()->textSecurity() != TSNONE && oldPlaceholderVisible != m_placeholderVisible) { - RenderStyle* newInnerStyle = new (renderArena()) RenderStyle(*innerStyle); - newInnerStyle->setTextSecurity(m_placeholderVisible ? TSNONE : style()->textSecurity()); - renderer->setStyle(newInnerStyle); - for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) { - if (n->renderer()) - n->renderer()->setStyle(newInnerStyle); - } - } -} - -void RenderTextControl::createSubtreeIfNeeded() -{ - // When adding these elements, create the renderer & style first before adding to the DOM. - // Otherwise, the render tree will create some anonymous blocks that will mess up our layout. - bool isSearchField = !m_multiLine && static_cast<HTMLInputElement*>(node())->isSearchField(); - if (isSearchField && !m_innerBlock) { - // Create the inner block element and give it a parent, renderer, and style - m_innerBlock = new TextControlInnerElement(document(), node()); - RenderBlock* innerBlockRenderer = new (renderArena()) RenderBlock(m_innerBlock.get()); - m_innerBlock->setRenderer(innerBlockRenderer); - m_innerBlock->setAttached(); - m_innerBlock->setInDocument(true); - innerBlockRenderer->setStyle(createInnerBlockStyle(style())); - - // Add inner block renderer to Render tree - RenderBlock::addChild(innerBlockRenderer); - } - if (isSearchField && !m_resultsButton) { - // Create the results block element and give it a parent, renderer, and style - m_resultsButton = new SearchFieldResultsButtonElement(document()); - RenderBlock* resultsBlockRenderer = new (renderArena()) RenderBlock(m_resultsButton.get()); - m_resultsButton->setRenderer(resultsBlockRenderer); - m_resultsButton->setAttached(); - m_resultsButton->setInDocument(true); - - RenderStyle* resultsBlockStyle = createResultsButtonStyle(m_innerBlock->renderer()->style()); - resultsBlockRenderer->setStyle(resultsBlockStyle); - - // Add results renderer to DOM & Render tree - m_innerBlock->renderer()->addChild(resultsBlockRenderer); - ExceptionCode ec = 0; - m_innerBlock->appendChild(m_resultsButton, ec); - } - if (!m_innerText) { - // Create the text block element and give it a parent, renderer, and style - // For non-search fields, there is no intermediate m_innerBlock as the shadow node. - // m_innerText will be the shadow node in that case. - m_innerText = new TextControlInnerTextElement(document(), m_innerBlock ? 0 : node()); - RenderTextControlInnerBlock* textBlockRenderer = new (renderArena()) RenderTextControlInnerBlock(m_innerText.get(), m_multiLine); - m_innerText->setRenderer(textBlockRenderer); - m_innerText->setAttached(); - m_innerText->setInDocument(true); - - RenderStyle* parentStyle = style(); - if (m_innerBlock) - parentStyle = m_innerBlock->renderer()->style(); - RenderStyle* textBlockStyle = createInnerTextStyle(parentStyle); - textBlockRenderer->setStyle(textBlockStyle); - - // Add text block renderer to Render tree - if (m_innerBlock) { - m_innerBlock->renderer()->addChild(textBlockRenderer); - ExceptionCode ec = 0; - // Add text block to the DOM - m_innerBlock->appendChild(m_innerText, ec); - } else - RenderBlock::addChild(textBlockRenderer); - } - if (isSearchField && !m_cancelButton) { - // Create the close block element and give it a parent, renderer, and style - m_cancelButton = new SearchFieldCancelButtonElement(document()); - RenderBlock* closeBlockRenderer = new (renderArena()) RenderBlock(m_cancelButton.get()); - m_cancelButton->setRenderer(closeBlockRenderer); - m_cancelButton->setAttached(); - m_cancelButton->setInDocument(true); - - RenderStyle* closeBlockStyle = createCancelButtonStyle(m_innerBlock->renderer()->style()); - closeBlockRenderer->setStyle(closeBlockStyle); - - // Add close block renderer to DOM & Render tree - m_innerBlock->renderer()->addChild(closeBlockRenderer); - ExceptionCode ec = 0; - m_innerBlock->appendChild(m_cancelButton, ec); - } -} - -void RenderTextControl::updateFromElement() -{ - HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node()); - - createSubtreeIfNeeded(); - - if (m_cancelButton) - updateCancelButtonVisibility(m_cancelButton->renderer()->style()); - - updatePlaceholder(); - - m_innerText->renderer()->style()->setUserModify(element->isReadOnlyControl() || element->disabled() ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); - - if ((!element->valueMatchesRenderer() || m_multiLine) && !m_placeholderVisible) { - String value; - if (m_multiLine) - value = static_cast<HTMLTextAreaElement*>(element)->value(); - else - value = static_cast<HTMLInputElement*>(element)->value(); - if (value.isNull()) - value = ""; - else - value = value.replace('\\', backslashAsCurrencySymbol()); - if (value != text() || !m_innerText->hasChildNodes()) { - if (value != text()) { - if (Frame* frame = document()->frame()) - frame->editor()->clearUndoRedoOperations(); - } - ExceptionCode ec = 0; - m_innerText->setInnerText(value, ec); - if (value.endsWith("\n") || value.endsWith("\r")) - m_innerText->appendChild(new HTMLBRElement(document()), ec); - m_dirty = false; - m_userEdited = false; - } - element->setValueMatchesRenderer(); - } - - if (m_searchPopupIsVisible) - m_searchPopup->updateFromElement(); -} - -void RenderTextControl::setUserEdited(bool isUserEdited) -{ - m_userEdited = isUserEdited; - document()->setIgnoreAutofocus(isUserEdited); -} - -int RenderTextControl::selectionStart() -{ - Frame* frame = document()->frame(); - if (!frame) - return 0; - return indexForVisiblePosition(frame->selection()->start()); -} - -int RenderTextControl::selectionEnd() -{ - Frame* frame = document()->frame(); - if (!frame) - return 0; - return indexForVisiblePosition(frame->selection()->end()); -} - -void RenderTextControl::setSelectionStart(int start) -{ - setSelectionRange(start, max(start, selectionEnd())); -} - -void RenderTextControl::setSelectionEnd(int end) -{ - setSelectionRange(min(end, selectionStart()), end); -} - -void RenderTextControl::select() -{ - setSelectionRange(0, text().length()); -} - -void RenderTextControl::setSelectionRange(int start, int end) -{ - end = max(end, 0); - start = min(max(start, 0), end); - - document()->updateLayout(); - - if (style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderer()->height()) { - if (m_multiLine) - static_cast<HTMLTextAreaElement*>(node())->cacheSelection(start, end); - else - static_cast<HTMLInputElement*>(node())->cacheSelection(start, end); - return; - } - VisiblePosition startPosition = visiblePositionForIndex(start); - VisiblePosition endPosition; - if (start == end) - endPosition = startPosition; - else - endPosition = visiblePositionForIndex(end); - - ASSERT(startPosition.isNotNull() && endPosition.isNotNull()); - ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node()); - - Selection newSelection = Selection(startPosition, endPosition); - - if (Frame* frame = document()->frame()) - frame->selection()->setSelection(newSelection); - - // FIXME: Granularity is stored separately on the frame, but also in the selection controller. - // The granularity in the selection controller should be used, and then this line of code would not be needed. - if (Frame* frame = document()->frame()) - frame->setSelectionGranularity(CharacterGranularity); -} - -Selection RenderTextControl::selection(int start, int end) const -{ - return Selection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY), - VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY)); -} - -VisiblePosition RenderTextControl::visiblePositionForIndex(int index) -{ - if (index <= 0) - return VisiblePosition(m_innerText.get(), 0, DOWNSTREAM); - ExceptionCode ec = 0; - RefPtr<Range> range = Range::create(document()); - range->selectNodeContents(m_innerText.get(), ec); - CharacterIterator it(range.get()); - it.advance(index - 1); - return VisiblePosition(it.range()->endContainer(ec), it.range()->endOffset(ec), UPSTREAM); -} - -int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos) -{ - Position indexPosition = pos.deepEquivalent(); - if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != m_innerText) - return 0; - ExceptionCode ec = 0; - RefPtr<Range> range = Range::create(document()); - range->setStart(m_innerText.get(), 0, ec); - range->setEnd(indexPosition.node(), indexPosition.offset(), ec); - return TextIterator::rangeLength(range.get()); -} - -void RenderTextControl::updateCancelButtonVisibility(RenderStyle* style) -{ - ASSERT(!m_multiLine); - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); - if (input->value().isEmpty()) - style->setVisibility(HIDDEN); - else - style->setVisibility(VISIBLE); -} - -void RenderTextControl::subtreeHasChanged() -{ - bool wasDirty = m_dirty; - m_dirty = true; - m_userEdited = true; - HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node()); - if (m_multiLine) { - element->setValueMatchesRenderer(false); - if (element->focused()) - if (Frame* frame = document()->frame()) - frame->textDidChangeInTextArea(element); - } else { - HTMLInputElement* input = static_cast<HTMLInputElement*>(element); - input->setValueFromRenderer(input->constrainValue(text())); - if (m_cancelButton) - updateCancelButtonVisibility(m_cancelButton->renderer()->style()); - - // If the incremental attribute is set, then dispatch the search event - if (!input->getAttribute(incrementalAttr).isNull()) - startSearchEventTimer(); - - if (!wasDirty) { - if (input->focused()) - if (Frame* frame = document()->frame()) - frame->textFieldDidBeginEditing(input); - } - if (input->focused()) - if (Frame* frame = document()->frame()) - frame->textDidChangeInTextField(input); - } -} - -String RenderTextControl::finishText(Vector<UChar>& result) const -{ - // Remove one trailing newline; there's always one that's collapsed out by rendering. - size_t size = result.size(); - if (size && result[size - 1] == '\n') - result.shrink(--size); - - // Convert backslash to currency symbol. - UChar symbol = backslashAsCurrencySymbol(); - if (symbol != '\\') { - for (size_t i = 0; i < size; ++i) { - if (result[i] == '\\') - result[i] = symbol; - } - } - - return String::adopt(result); -} - -HTMLElement* RenderTextControl::innerTextElement() const -{ - return m_innerText.get(); -} - -String RenderTextControl::text() -{ - if (!m_innerText) - return ""; - - Frame* frame = document()->frame(); - Text* compositionNode = frame ? frame->editor()->compositionNode() : 0; - - Vector<UChar> result; - - for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) { - if (n->isTextNode()) { - Text* text = static_cast<Text*>(n); - String data = text->data(); - unsigned length = data.length(); - if (text != compositionNode) - result.append(data.characters(), length); - else { - unsigned compositionStart = min(frame->editor()->compositionStart(), length); - unsigned compositionEnd = min(max(compositionStart, frame->editor()->compositionEnd()), length); - result.append(data.characters(), compositionStart); - result.append(data.characters() + compositionEnd, length - compositionEnd); - } - } - } - - return finishText(result); -} - -static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset) -{ - RootInlineBox* next; - for (; line; line = next) { - next = line->nextRootBox(); - if (next && !line->endsWithBreak()) { - ASSERT(line->lineBreakObj()); - breakNode = line->lineBreakObj()->node(); - breakOffset = line->lineBreakPos(); - line = next; - return; - } - } - breakNode = 0; -} - -String RenderTextControl::textWithHardLineBreaks() -{ - if (!m_innerText) - return ""; - Node* firstChild = m_innerText->firstChild(); - if (!firstChild) - return ""; - - document()->updateLayout(); - - RenderObject* renderer = firstChild->renderer(); - if (!renderer) - return ""; - - InlineBox* box = renderer->isText() ? static_cast<RenderText*>(renderer)->firstTextBox() : renderer->inlineBoxWrapper(); - if (!box) - return ""; - - Frame* frame = document()->frame(); - Text* compositionNode = frame ? frame->editor()->compositionNode() : 0; - - Node* breakNode; - unsigned breakOffset; - RootInlineBox* line = box->root(); - getNextSoftBreak(line, breakNode, breakOffset); - - Vector<UChar> result; - - for (Node* n = firstChild; n; n = n->traverseNextNode(m_innerText.get())) { - if (n->hasTagName(brTag)) - result.append(&newlineCharacter, 1); - else if (n->isTextNode()) { - Text* text = static_cast<Text*>(n); - String data = text->data(); - unsigned length = data.length(); - unsigned compositionStart = (text == compositionNode) - ? min(frame->editor()->compositionStart(), length) : 0; - unsigned compositionEnd = (text == compositionNode) - ? min(max(compositionStart, frame->editor()->compositionEnd()), length) : 0; - unsigned position = 0; - while (breakNode == n && breakOffset < compositionStart) { - result.append(data.characters() + position, breakOffset - position); - position = breakOffset; - result.append(&newlineCharacter, 1); - getNextSoftBreak(line, breakNode, breakOffset); - } - result.append(data.characters() + position, compositionStart - position); - position = compositionEnd; - while (breakNode == n && breakOffset <= length) { - if (breakOffset > position) { - result.append(data.characters() + position, breakOffset - position); - position = breakOffset; - result.append(&newlineCharacter, 1); - } - getNextSoftBreak(line, breakNode, breakOffset); - } - result.append(data.characters() + position, length - position); - } - while (breakNode == n) - getNextSoftBreak(line, breakNode, breakOffset); - } - - return finishText(result); -} - -void RenderTextControl::calcHeight() -{ - int rows = 1; - if (m_multiLine) - rows = static_cast<HTMLTextAreaElement*>(node())->rows(); - - int line = m_innerText->renderer()->lineHeight(true, true); - int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom(); - - int innerToAdd = m_innerText->renderer()->borderTop() + m_innerText->renderer()->borderBottom() + - m_innerText->renderer()->paddingTop() + m_innerText->renderer()->paddingBottom() + - m_innerText->renderer()->marginTop() + m_innerText->renderer()->marginBottom(); - - if (m_resultsButton) { - static_cast<RenderBlock*>(m_resultsButton->renderer())->calcHeight(); - innerToAdd = max(innerToAdd, - m_resultsButton->renderer()->borderTop() + m_resultsButton->renderer()->borderBottom() + - m_resultsButton->renderer()->paddingTop() + m_resultsButton->renderer()->paddingBottom() + - m_resultsButton->renderer()->marginTop() + m_resultsButton->renderer()->marginBottom()); - line = max(line, m_resultsButton->renderer()->height()); - } - if (m_cancelButton) { - static_cast<RenderBlock*>(m_cancelButton->renderer())->calcHeight(); - innerToAdd = max(innerToAdd, - m_cancelButton->renderer()->borderTop() + m_cancelButton->renderer()->borderBottom() + - m_cancelButton->renderer()->paddingTop() + m_cancelButton->renderer()->paddingBottom() + - m_cancelButton->renderer()->marginTop() + m_cancelButton->renderer()->marginBottom()); - line = max(line, m_cancelButton->renderer()->height()); - } - toAdd += innerToAdd; - - // FIXME: We should get the size of the scrollbar from the RenderTheme instead. - int scrollbarSize = 0; - // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap. - if (style()->overflowX() == OSCROLL || (style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap)) - scrollbarSize = PlatformScrollbar::horizontalScrollbarHeight(); - - m_height = line * rows + toAdd + scrollbarSize; - - RenderBlock::calcHeight(); -} - -int RenderTextControl::baselinePosition(bool b, bool isRootLineBox) const -{ - if (m_multiLine) - return height() + marginTop() + marginBottom(); - return RenderBlock::baselinePosition(b, isRootLineBox); -} - -bool RenderTextControl::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) -{ - // If we're within the text control, we want to act as if we've hit the inner text block element, in case the point - // was on the control but not on the inner element (see Radar 4617841). - - // In a search field, we want to act as if we've hit the results block if we're to the left of the inner text block, - // and act as if we've hit the close block if we're to the right of the inner text block. - - if (RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction) && - (m_multiLine || result.innerNode() == element() || result.innerNode() == m_innerBlock)) { - IntPoint localPoint = IntPoint(x - tx - m_x, y - ty - m_y); - if (m_innerBlock) { - int textLeft = tx + m_x + m_innerBlock->renderer()->xPos() + m_innerText->renderer()->xPos(); - int textRight = textLeft + m_innerText->renderer()->width(); - if (m_resultsButton && x < textLeft) { - result.setInnerNode(m_resultsButton.get()); - result.setLocalPoint(IntPoint(localPoint.x() - m_innerText->renderer()->xPos() - m_innerBlock->renderer()->xPos() - m_resultsButton->renderer()->xPos(), - localPoint.y() - m_innerText->renderer()->yPos() - m_innerBlock->renderer()->yPos() - m_resultsButton->renderer()->yPos())); - return true; - } - if (m_cancelButton && x > textRight) { - result.setInnerNode(m_cancelButton.get()); - result.setLocalPoint(IntPoint(localPoint.x() - m_innerText->renderer()->xPos() - m_innerBlock->renderer()->xPos() - m_cancelButton->renderer()->xPos(), - localPoint.y() - m_innerText->renderer()->yPos() - m_innerBlock->renderer()->yPos() - m_cancelButton->renderer()->yPos())); - return true; - } - } - - // Hit the inner text block. - result.setInnerNode(m_innerText.get()); - result.setInnerNonSharedNode(m_innerText.get()); - result.setLocalPoint(IntPoint(localPoint.x() - m_innerText->renderer()->xPos() - (m_innerBlock.get() ? m_innerBlock->renderer()->xPos() : 0), - localPoint.y() - m_innerText->renderer()->yPos() - (m_innerBlock.get() ? m_innerBlock->renderer()->yPos() : 0))); - - return true; - } - - return false; -} - -IntRect RenderTextControl::controlClipRect(int tx, int ty) const -{ - IntRect clipRect = contentBox(); - clipRect.move(tx, ty); - return clipRect; -} - -void RenderTextControl::layout() -{ - int oldHeight = m_height; - calcHeight(); - bool relayoutChildren = oldHeight != m_height; - - // Set the text block's height - int textBlockHeight = m_height - paddingTop() - paddingBottom() - borderTop() - borderBottom(); - int currentTextBlockHeight = m_innerText->renderer()->height(); - if (m_multiLine || m_innerBlock || currentTextBlockHeight > m_height) { - if (textBlockHeight != currentTextBlockHeight) - relayoutChildren = true; - if (!m_multiLine) - m_innerText->renderer()->style()->setHeight(Length(textBlockHeight, Fixed)); - } - if (m_innerBlock) { - if (textBlockHeight != m_innerBlock->renderer()->height()) - relayoutChildren = true; - m_innerBlock->renderer()->style()->setHeight(Length(textBlockHeight, Fixed)); - } - - int oldWidth = m_width; - calcWidth(); - if (oldWidth != m_width) - relayoutChildren = true; - - int searchExtrasWidth = 0; - if (m_resultsButton) { - m_resultsButton->renderer()->calcWidth(); - searchExtrasWidth += m_resultsButton->renderer()->width(); - } - if (m_cancelButton) { - m_cancelButton->renderer()->calcWidth(); - searchExtrasWidth += m_cancelButton->renderer()->width(); - } - - int scrollbarSize = 0; - if (m_multiLine && style()->overflowY() != OHIDDEN) - // FIXME: We should get the size of the scrollbar from the RenderTheme instead. - scrollbarSize = PlatformScrollbar::verticalScrollbarWidth(); - - // Set the text block's width - int textBlockWidth = m_width - paddingLeft() - paddingRight() - borderLeft() - borderRight() - - m_innerText->renderer()->paddingLeft() - m_innerText->renderer()->paddingRight() - searchExtrasWidth - - scrollbarSize; - - if (m_multiLine && style()->htmlHacks()) - // Matches width in IE quirksmode. We can't just remove the CSS padding in - // quirks.css because then text will wrap differently than in IE. - textBlockWidth -= 2; - - if (textBlockWidth != m_innerText->renderer()->width()) - relayoutChildren = true; - m_innerText->renderer()->style()->setWidth(Length(textBlockWidth, Fixed)); - if (m_innerBlock) { - int innerBlockWidth = m_width - paddingLeft() - paddingRight() - borderLeft() - borderRight(); - if (innerBlockWidth != m_innerBlock->renderer()->width()) - relayoutChildren = true; - m_innerBlock->renderer()->style()->setWidth(Length(innerBlockWidth, Fixed)); - } - - RenderBlock::layoutBlock(relayoutChildren); - - // For text fields, center the inner text vertically - // Don't do this for search fields, since we don't honor height for them - if (!m_multiLine) { - currentTextBlockHeight = m_innerText->renderer()->height(); - if (!m_innerBlock && currentTextBlockHeight < m_height) - m_innerText->renderer()->setPos(m_innerText->renderer()->xPos(), (m_height - currentTextBlockHeight) / 2); - } -} - -void RenderTextControl::paint(PaintInfo& paintInfo, int tx, int ty) -{ - RenderBlock::paint(paintInfo, tx, ty); - if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) - theme()->paintCapsLockIndicator(this, paintInfo, absoluteContentBox()); -} - -void RenderTextControl::calcPrefWidths() -{ - ASSERT(prefWidthsDirty()); - - m_minPrefWidth = 0; - m_maxPrefWidth = 0; - - if (style()->width().isFixed() && style()->width().value() > 0) - m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); - else { - int factor; - int scrollbarSize = 0; - if (m_multiLine) { - factor = static_cast<HTMLTextAreaElement*>(node())->cols(); - // FIXME: We should get the size of the scrollbar from the RenderTheme instead. - if (style()->overflowY() != OHIDDEN) - scrollbarSize = PlatformScrollbar::verticalScrollbarWidth(); - } else { - factor = static_cast<HTMLInputElement*>(node())->size(); - if (factor <= 0) - factor = 20; - } - - // Use average character width. Matches IE. - int avgCharWidth = style()->font().primaryFont()->avgCharWidth(); - m_maxPrefWidth = (avgCharWidth * factor) + scrollbarSize + - m_innerText->renderer()->paddingLeft() + m_innerText->renderer()->paddingRight(); - - // For text inputs, IE adds some extra width. - if (!m_multiLine) - m_maxPrefWidth += style()->font().primaryFont()->maxCharWidth() - avgCharWidth; - - if (m_resultsButton) - m_maxPrefWidth += m_resultsButton->renderer()->borderLeft() + m_resultsButton->renderer()->borderRight() + - m_resultsButton->renderer()->paddingLeft() + m_resultsButton->renderer()->paddingRight(); - if (m_cancelButton) - m_maxPrefWidth += m_cancelButton->renderer()->borderLeft() + m_cancelButton->renderer()->borderRight() + - m_cancelButton->renderer()->paddingLeft() + m_cancelButton->renderer()->paddingRight(); - } - - if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { - m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); - m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); - } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) - m_minPrefWidth = 0; - else - m_minPrefWidth = m_maxPrefWidth; - - if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { - m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); - m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); - } - - int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); - - m_minPrefWidth += toAdd; - m_maxPrefWidth += toAdd; - - setPrefWidthsDirty(false); -} - -void RenderTextControl::forwardEvent(Event* evt) -{ - if (evt->type() == blurEvent) { - RenderObject* innerRenderer = m_innerText->renderer(); - if (innerRenderer) { - RenderLayer* innerLayer = innerRenderer->layer(); - if (innerLayer && !m_multiLine) - innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0); - } - updatePlaceholder(); - capsLockStateMayHaveChanged(); - } else if (evt->type() == focusEvent) { - updatePlaceholder(); - capsLockStateMayHaveChanged(); - } else { - if (evt->isMouseEvent() && m_resultsButton && static_cast<MouseEvent*>(evt)->x() < m_innerText->renderer()->absoluteBoundingBoxRect().x()) - m_resultsButton->defaultEventHandler(evt); - else if (evt->isMouseEvent() && m_cancelButton && static_cast<MouseEvent*>(evt)->x() > m_innerText->renderer()->absoluteBoundingBoxRect().right()) - m_cancelButton->defaultEventHandler(evt); - else - m_innerText->defaultEventHandler(evt); - } -} - -void RenderTextControl::selectionChanged(bool userTriggered) -{ - HTMLFormControlElement* element = static_cast<HTMLFormControlElement*>(node()); - if (m_multiLine) - static_cast<HTMLTextAreaElement*>(element)->cacheSelection(selectionStart(), selectionEnd()); - else - static_cast<HTMLInputElement*>(element)->cacheSelection(selectionStart(), selectionEnd()); - if (Frame* frame = document()->frame()) - if (frame->selection()->isRange() && userTriggered) - element->dispatchHTMLEvent(selectEvent, true, false); -} - -void RenderTextControl::autoscroll() -{ - if (m_multiLine) { - layer()->autoscroll(); - } else { - RenderLayer* layer = m_innerText->renderer()->layer(); - if (layer) - layer->autoscroll(); - } -} - -int RenderTextControl::scrollWidth() const -{ - if (!m_multiLine && m_innerText) - return m_innerText->scrollWidth(); - return RenderBlock::scrollWidth(); -} - -int RenderTextControl::scrollHeight() const -{ - if (m_innerText) { - if (m_multiLine) { - // This matches IEs behavior of giving the height of the innerText block - // as the scrollHeight. - return m_innerText->scrollHeight() + paddingTop() + paddingBottom(); - } else { - return m_innerText->scrollHeight(); - } - } - return RenderBlock::scrollHeight(); -} - -int RenderTextControl::scrollLeft() const -{ - if (!m_multiLine && m_innerText) - return m_innerText->scrollLeft(); - return RenderBlock::scrollLeft(); -} - -int RenderTextControl::scrollTop() const -{ - if (!m_multiLine && m_innerText) - return m_innerText->scrollTop(); - return RenderBlock::scrollTop(); -} - -void RenderTextControl::setScrollLeft(int newLeft) -{ - if (m_multiLine) { - RenderBlock::setScrollLeft(newLeft); - } else if (m_innerText) { - m_innerText->setScrollLeft(newLeft); - } -} - -void RenderTextControl::setScrollTop(int newTop) -{ - if (m_multiLine) { - RenderBlock::setScrollTop(newTop); - } else if (m_innerText) { - m_innerText->setScrollTop(newTop); - } -} - -const AtomicString& RenderTextControl::autosaveName() const -{ - return static_cast<Element*>(node())->getAttribute(autosaveAttr); -} - -void RenderTextControl::addSearchResult() -{ - ASSERT(!m_multiLine); - - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); - if (input->maxResults() <= 0) - return; - - String value = input->value(); - if (value.isEmpty()) - return; - - Settings* settings = document()->settings(); - if (!settings || settings->privateBrowsingEnabled()) - return; - - int size = static_cast<int>(m_recentSearches.size()); - for (int i = size - 1; i >= 0; --i) - if (m_recentSearches[i] == value) - m_recentSearches.remove(i); - - m_recentSearches.insert(0, value); - while (static_cast<int>(m_recentSearches.size()) > input->maxResults()) - m_recentSearches.removeLast(); - - const AtomicString& name = autosaveName(); - if (!m_searchPopup) - m_searchPopup = SearchPopupMenu::create(this); - m_searchPopup->saveRecentSearches(name, m_recentSearches); -} - -void RenderTextControl::showPopup() -{ - if (m_searchPopupIsVisible) - return; - - if (!m_searchPopup) - m_searchPopup = SearchPopupMenu::create(this); - - if (!m_searchPopup->enabled()) - return; - - m_searchPopupIsVisible = true; - - const AtomicString& name = autosaveName(); - m_searchPopup->loadRecentSearches(name, m_recentSearches); - - // Trim the recent searches list if the maximum size has changed since we last saved. - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); - if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) { - do - m_recentSearches.removeLast(); - while (static_cast<int>(m_recentSearches.size()) > input->maxResults()); - m_searchPopup->saveRecentSearches(name, m_recentSearches); - } - - m_searchPopup->show(absoluteBoundingBoxRect(), document()->view(), -1); -} - -void RenderTextControl::hidePopup() -{ - if (m_searchPopup) - m_searchPopup->hide(); - m_searchPopupIsVisible = false; -} - -void RenderTextControl::valueChanged(unsigned listIndex, bool fireEvents) -{ - ASSERT(static_cast<int>(listIndex) < listSize()); - HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); - if (static_cast<int>(listIndex) == (listSize() - 1)) { - if (fireEvents) { - m_recentSearches.clear(); - const AtomicString& name = autosaveName(); - if (!name.isEmpty()) { - if (!m_searchPopup) - m_searchPopup = SearchPopupMenu::create(this); - m_searchPopup->saveRecentSearches(name, m_recentSearches); - } - } - } else { - input->setValue(itemText(listIndex)); - if (fireEvents) - input->onSearch(); - input->select(); - } -} - -String RenderTextControl::itemText(unsigned listIndex) const -{ - int size = listSize(); - if (size == 1) { - ASSERT(!listIndex); - return searchMenuNoRecentSearchesText(); - } - if (!listIndex) - return searchMenuRecentSearchesText(); - if (itemIsSeparator(listIndex)) - return String(); - if (static_cast<int>(listIndex) == (size - 1)) - return searchMenuClearRecentSearchesText(); - return m_recentSearches[listIndex - 1]; -} - -bool RenderTextControl::itemIsEnabled(unsigned listIndex) const -{ - if (!listIndex || itemIsSeparator(listIndex)) - return false; - return true; -} - -RenderStyle* RenderTextControl::itemStyle(unsigned listIndex) const -{ - return style(); -} - -Color RenderTextControl::itemBackgroundColor(unsigned listIndex) const -{ - return style()->backgroundColor(); -} - -RenderStyle* RenderTextControl::clientStyle() const -{ - return style(); -} - -Document* RenderTextControl::clientDocument() const -{ - return document(); -} - -int RenderTextControl::clientInsetLeft() const -{ - // Inset the menu by the radius of the cap on the left so that - // it only runs along the straight part of the bezel. - return height() / 2; -} - -int RenderTextControl::clientInsetRight() const -{ - // Inset the menu by the radius of the cap on the right so that - // it only runs along the straight part of the bezel (unless it needs - // to be wider). - return height() / 2; -} - -int RenderTextControl::clientPaddingLeft() const -{ - return paddingLeft() + m_resultsButton->renderer()->width(); -} - -int RenderTextControl::clientPaddingRight() const -{ - return paddingRight() + m_cancelButton->renderer()->width(); -} - -int RenderTextControl::listSize() const -{ - // If there are no recent searches, then our menu will have 1 "No recent searches" item. - if (!m_recentSearches.size()) - return 1; - // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item. - return m_recentSearches.size() + 3; -} - -int RenderTextControl::selectedIndex() const -{ - return -1; -} - -bool RenderTextControl::itemIsSeparator(unsigned listIndex) const -{ - // The separator will be the second to last item in our list. - return static_cast<int>(listIndex) == (listSize() - 2); -} - -bool RenderTextControl::itemIsLabel(unsigned listIndex) const -{ - return listIndex == 0; -} - -bool RenderTextControl::itemIsSelected(unsigned listIndex) const -{ - return false; -} - -void RenderTextControl::setTextFromItem(unsigned listIndex) -{ - static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex)); -} - -bool RenderTextControl::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) -{ - RenderLayer* layer = m_innerText->renderer()->layer(); - if (!m_multiLine && layer && layer->scroll(direction, granularity, multiplier)) - return true; - return RenderObject::scroll(direction, granularity, multiplier); -} - -void RenderTextControl::searchEventTimerFired(Timer<RenderTextControl>*) -{ - static_cast<HTMLInputElement*>(node())->onSearch(); -} - -void RenderTextControl::stopSearchEventTimer() -{ - m_searchEventTimer.stop(); -} - -void RenderTextControl::startSearchEventTimer() -{ - unsigned length = text().length(); - - // If there's no text, fire the event right away. - if (!length) { - m_searchEventTimer.stop(); - static_cast<HTMLInputElement*>(node())->onSearch(); - return; - } - - // After typing the first key, we wait 0.5 seconds. - // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on. - m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length)); -} - -bool RenderTextControl::isScrollable() const -{ - if (!m_multiLine && m_innerText && m_innerText->renderer()->isScrollable()) - return true; - return RenderObject::isScrollable(); -} - -FontSelector* RenderTextControl::fontSelector() const -{ - return document()->styleSelector()->fontSelector(); -} - -void RenderTextControl::capsLockStateMayHaveChanged() -{ - // Only draw the caps lock indicator if these things are true: - // 1) The field is a password field - // 2) The frame is active - // 3) The element is focused - // 4) The caps lock is on - - bool shouldDrawCapsLockIndicator = false; - if (Node* n = node()) - if (Document* d = document()) - if (Frame* f = d->frame()) - shouldDrawCapsLockIndicator = !m_multiLine && static_cast<HTMLInputElement*>(n)->inputType() == HTMLInputElement::PASSWORD && - f->selection()->isFocusedAndActive() && d->focusedNode() == n && PlatformKeyboardEvent::currentCapsLockState(); - - if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) { - m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator; - repaint(); - } -} - -} // namespace WebCore diff --git a/webkit/pending/Selection.cpp b/webkit/pending/Selection.cpp deleted file mode 100644 index 478dd5a..0000000 --- a/webkit/pending/Selection.cpp +++ /dev/null @@ -1,607 +0,0 @@ -/* - * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "Selection.h" - -#include "CString.h" -#include "Document.h" -#include "Element.h" -#include "htmlediting.h" -#include "VisiblePosition.h" -#include "visible_units.h" -#include "Range.h" -#include <unicode/ubrk.h> -#include <wtf/Assertions.h> -#include <stdio.h> - -namespace WebCore { - -Selection::Selection() - : m_affinity(DOWNSTREAM) - , m_granularity(CharacterGranularity) - , m_state(NONE) - , m_baseIsFirst(true) -{ -} - -Selection::Selection(const Position& pos, EAffinity affinity) - : m_base(pos) - , m_extent(pos) - , m_affinity(affinity) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const Position& base, const Position& extent, EAffinity affinity) - : m_base(base) - , m_extent(extent) - , m_affinity(affinity) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const VisiblePosition& pos) - : m_base(pos.deepEquivalent()) - , m_extent(pos.deepEquivalent()) - , m_affinity(pos.affinity()) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const VisiblePosition& base, const VisiblePosition& extent) - : m_base(base.deepEquivalent()) - , m_extent(extent.deepEquivalent()) - , m_affinity(base.affinity()) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection::Selection(const Range* range, EAffinity affinity) - : m_base(range->startPosition()) - , m_extent(range->endPosition()) - , m_affinity(affinity) - , m_granularity(CharacterGranularity) -{ - validate(); -} - -Selection Selection::selectionFromContentsOfNode(Node* node) -{ - return Selection(Position(node, 0), Position(node, maxDeepOffset(node)), DOWNSTREAM); -} - -void Selection::setBase(const Position& position) -{ - m_base = position; - validate(); -} - -void Selection::setBase(const VisiblePosition& visiblePosition) -{ - m_base = visiblePosition.deepEquivalent(); - validate(); -} - -void Selection::setExtent(const Position& position) -{ - m_extent = position; - validate(); -} - -void Selection::setExtent(const VisiblePosition& visiblePosition) -{ - m_extent = visiblePosition.deepEquivalent(); - validate(); -} - -PassRefPtr<Range> Selection::toRange() const -{ - if (isNone()) - return 0; - - // Make sure we have an updated layout since this function is called - // in the course of running edit commands which modify the DOM. - // Failing to call this can result in equivalentXXXPosition calls returning - // incorrect results. - m_start.node()->document()->updateLayout(); - - // Check again, because updating layout can clear the selection. - if (isNone()) - return 0; - - Position s, e; - if (isCaret()) { - // If the selection is a caret, move the range start upstream. This helps us match - // the conventions of text editors tested, which make style determinations based - // on the character before the caret, if any. - s = rangeCompliantEquivalent(m_start.upstream()); - e = s; - } else { - // If the selection is a range, select the minimum range that encompasses the selection. - // Again, this is to match the conventions of text editors tested, which make style - // determinations based on the first character of the selection. - // For instance, this operation helps to make sure that the "X" selected below is the - // only thing selected. The range should not be allowed to "leak" out to the end of the - // previous text node, or to the beginning of the next text node, each of which has a - // different style. - // - // On a treasure map, <b>X</b> marks the spot. - // ^ selected - // - ASSERT(isRange()); - s = m_start.downstream(); - e = m_end.upstream(); - if (Range::compareBoundaryPoints(s.node(), s.offset(), e.node(), e.offset()) > 0) { - // Make sure the start is before the end. - // The end can wind up before the start if collapsed whitespace is the only thing selected. - Position tmp = s; - s = e; - e = tmp; - } - s = rangeCompliantEquivalent(s); - e = rangeCompliantEquivalent(e); - } - - ExceptionCode ec = 0; - RefPtr<Range> result(Range::create(s.node()->document())); - result->setStart(s.node(), s.offset(), ec); - if (ec) { - LOG_ERROR("Exception setting Range start from Selection: %d", ec); - return 0; - } - result->setEnd(e.node(), e.offset(), ec); - if (ec) { - LOG_ERROR("Exception setting Range end from Selection: %d", ec); - return 0; - } - return result.release(); -} - -bool Selection::expandUsingGranularity(TextGranularity granularity) -{ - if (isNone()) - return false; - - m_granularity = granularity; - validate(); - return true; -} - -void Selection::validate() -{ - // Move the selection to rendered positions, if possible. - bool baseAndExtentEqual = m_base == m_extent; - if (m_base.isNotNull()) { - m_base = VisiblePosition(m_base, m_affinity).deepEquivalent(); - if (baseAndExtentEqual) - m_extent = m_base; - } - if (m_extent.isNotNull() && !baseAndExtentEqual) - m_extent = VisiblePosition(m_extent, m_affinity).deepEquivalent(); - - // Make sure we do not have a dangling base or extent. - if (m_base.isNull() && m_extent.isNull()) - m_baseIsFirst = true; - else if (m_base.isNull()) { - m_base = m_extent; - m_baseIsFirst = true; - } else if (m_extent.isNull()) { - m_extent = m_base; - m_baseIsFirst = true; - } else { - m_baseIsFirst = comparePositions(m_base, m_extent) <= 0; - } - - if (m_baseIsFirst) { - m_start = m_base; - m_end = m_extent; - } else { - m_start = m_extent; - m_end = m_base; - } - - // Expand the selection if requested. - switch (m_granularity) { - case CharacterGranularity: - // Don't do any expansion. - break; - case WordGranularity: { - // General case: Select the word the caret is positioned inside of, or at the start of (RightWordIfOnBoundary). - // Edge case: If the caret is after the last word in a soft-wrapped line or the last word in - // the document, select that last word (LeftWordIfOnBoundary). - // Edge case: If the caret is after the last word in a paragraph, select from the the end of the - // last word to the line break (also RightWordIfOnBoundary); - VisiblePosition start = VisiblePosition(m_start, m_affinity); - VisiblePosition originalEnd(m_end, m_affinity); - EWordSide side = RightWordIfOnBoundary; - if (isEndOfDocument(start) || (isEndOfLine(start) && !isStartOfLine(start) && !isEndOfParagraph(start))) - side = LeftWordIfOnBoundary; - m_start = startOfWord(start, side).deepEquivalent(); - side = RightWordIfOnBoundary; - if (isEndOfDocument(originalEnd) || (isEndOfLine(originalEnd) && !isStartOfLine(originalEnd) && !isEndOfParagraph(originalEnd))) - side = LeftWordIfOnBoundary; - - VisiblePosition wordEnd(endOfWord(originalEnd, side)); - VisiblePosition end(wordEnd); - - if (isEndOfParagraph(originalEnd)) { - // Select the paragraph break (the space from the end of a paragraph to the start of - // the next one) to match TextEdit. - end = wordEnd.next(); - - if (Node* table = isFirstPositionAfterTable(end)) { - // The paragraph break after the last paragraph in the last cell of a block table ends - // at the start of the paragraph after the table. - if (isBlock(table)) - end = end.next(true); - else - end = wordEnd; - } - - if (end.isNull()) - end = wordEnd; - - } - -#if PLATFORM(WIN_OS) - // Windows platform requires us to select trailing whitespace after selecting words. - while (u_isblank(end.characterAfter())) { - end = end.next(); - } - if (end.isNull()) - end = wordEnd; -#endif - - m_end = end.deepEquivalent(); - break; - } - case SentenceGranularity: { - m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - } - case LineGranularity: { - m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - VisiblePosition end = endOfLine(VisiblePosition(m_end, m_affinity)); - // If the end of this line is at the end of a paragraph, include the space - // after the end of the line in the selection. - if (isEndOfParagraph(end)) { - VisiblePosition next = end.next(); - if (next.isNotNull()) - end = next; - } - m_end = end.deepEquivalent(); - break; - } - case LineBoundary: - m_start = startOfLine(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfLine(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - case ParagraphGranularity: { - VisiblePosition pos(m_start, m_affinity); - if (isStartOfLine(pos) && isEndOfDocument(pos)) - pos = pos.previous(); - m_start = startOfParagraph(pos).deepEquivalent(); - VisiblePosition visibleParagraphEnd = endOfParagraph(VisiblePosition(m_end, m_affinity)); - - // Include the "paragraph break" (the space from the end of this paragraph to the start - // of the next one) in the selection. - VisiblePosition end(visibleParagraphEnd.next()); - - if (Node* table = isFirstPositionAfterTable(end)) { - // The paragraph break after the last paragraph in the last cell of a block table ends - // at the start of the paragraph after the table, not at the position just after the table. - if (isBlock(table)) - end = end.next(true); - // There is no parargraph break after the last paragraph in the last cell of an inline table. - else - end = visibleParagraphEnd; - } - - if (end.isNull()) - end = visibleParagraphEnd; - - m_end = end.deepEquivalent(); - break; - } - case DocumentBoundary: - m_start = startOfDocument(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfDocument(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - case ParagraphBoundary: - m_start = startOfParagraph(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfParagraph(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - case SentenceBoundary: - m_start = startOfSentence(VisiblePosition(m_start, m_affinity)).deepEquivalent(); - m_end = endOfSentence(VisiblePosition(m_end, m_affinity)).deepEquivalent(); - break; - } - - // Make sure we do not have a dangling start or end. - if (m_start.isNull()) - m_start = m_end; - if (m_end.isNull()) - m_end = m_start; - - adjustForEditableContent(); - - // adjust the state - if (m_start.isNull()) { - ASSERT(m_end.isNull()); - m_state = NONE; - - // enforce downstream affinity if not caret, as affinity only - // makes sense for caret - m_affinity = DOWNSTREAM; - } else if (m_start == m_end || m_start.upstream() == m_end.upstream()) { - m_state = CARET; - } else { - m_state = RANGE; - - // enforce downstream affinity if not caret, as affinity only - // makes sense for caret - m_affinity = DOWNSTREAM; - - // "Constrain" the selection to be the smallest equivalent range of nodes. - // This is a somewhat arbitrary choice, but experience shows that it is - // useful to make to make the selection "canonical" (if only for - // purposes of comparing selections). This is an ideal point of the code - // to do this operation, since all selection changes that result in a RANGE - // come through here before anyone uses it. - // FIXME: Canonicalizing is good, but haven't we already done it (when we - // set these two positions to VisiblePosition deepEquivalent()s above)? - m_start = m_start.downstream(); - m_end = m_end.upstream(); - } -} - -// FIXME: This function breaks the invariant of this class. -// But because we use Selection to store values in editing commands for use when -// undoing the command, we need to be able to create a selection that while currently -// invalid, will be valid once the changes are undone. This is a design problem. -// To fix it we either need to change the invariants of Selection or create a new -// class for editing to use that can manipulate selections that are not currently valid. -void Selection::setWithoutValidation(const Position& base, const Position& extent) -{ - ASSERT(!base.isNull()); - ASSERT(!extent.isNull()); - ASSERT(base != extent); - ASSERT(m_affinity == DOWNSTREAM); - ASSERT(m_granularity == CharacterGranularity); - m_base = base; - m_extent = extent; - m_baseIsFirst = comparePositions(base, extent) <= 0; - if (m_baseIsFirst) { - m_start = base; - m_end = extent; - } else { - m_start = extent; - m_end = base; - } - m_state = RANGE; -} - -void Selection::adjustForEditableContent() -{ - if (m_base.isNull() || m_start.isNull() || m_end.isNull()) - return; - - Node* baseRoot = highestEditableRoot(m_base); - Node* startRoot = highestEditableRoot(m_start); - Node* endRoot = highestEditableRoot(m_end); - - Node* baseEditableAncestor = lowestEditableAncestor(m_base.node()); - - // The base, start and end are all in the same region. No adjustment necessary. - if (baseRoot == startRoot && baseRoot == endRoot) - return; - - // The selection is based in editable content. - if (baseRoot) { - // If the start is outside the base's editable root, cap it at the start of that root. - // If the start is in non-editable content that is inside the base's editable root, put it - // at the first editable position after start inside the base's editable root. - if (startRoot != baseRoot) { - VisiblePosition first = firstEditablePositionAfterPositionInRoot(m_start, baseRoot); - m_start = first.deepEquivalent(); - if (m_start.isNull()) { - ASSERT_NOT_REACHED(); - m_start = m_end; - } - } - // If the end is outside the base's editable root, cap it at the end of that root. - // If the end is in non-editable content that is inside the base's root, put it - // at the last editable position before the end inside the base's root. - if (endRoot != baseRoot) { - VisiblePosition last = lastEditablePositionBeforePositionInRoot(m_end, baseRoot); - m_end = last.deepEquivalent(); - if (m_end.isNull()) { - ASSERT_NOT_REACHED(); - m_end = m_start; - } - } - // The selection is based in non-editable content. - } else { - // FIXME: Non-editable pieces inside editable content should be atomic, in the same way that editable - // pieces in non-editable content are atomic. - - // The selection ends in editable content or non-editable content inside a different editable ancestor, - // move backward until non-editable content inside the same lowest editable ancestor is reached. - Node* endEditableAncestor = lowestEditableAncestor(m_end.node()); - if (endRoot || endEditableAncestor != baseEditableAncestor) { - - Position p = previousVisuallyDistinctCandidate(m_end); - Node* shadowAncestor = endRoot ? endRoot->shadowAncestorNode() : 0; - if (p.isNull() && endRoot && (shadowAncestor != endRoot)) - p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); - while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { - Node* root = editableRootForPosition(p); - shadowAncestor = root ? root->shadowAncestorNode() : 0; - p = isAtomicNode(p.node()) ? positionBeforeNode(p.node()) : previousVisuallyDistinctCandidate(p); - if (p.isNull() && (shadowAncestor != root)) - p = Position(shadowAncestor, maxDeepOffset(shadowAncestor)); - } - VisiblePosition previous(p); - - if (previous.isNull()) { - ASSERT_NOT_REACHED(); - m_base = Position(); - m_extent = Position(); - validate(); - return; - } - m_end = previous.deepEquivalent(); - } - - // The selection starts in editable content or non-editable content inside a different editable ancestor, - // move forward until non-editable content inside the same lowest editable ancestor is reached. - Node* startEditableAncestor = lowestEditableAncestor(m_start.node()); - if (startRoot || startEditableAncestor != baseEditableAncestor) { - Position p = nextVisuallyDistinctCandidate(m_start); - Node* shadowAncestor = startRoot ? startRoot->shadowAncestorNode() : 0; - if (p.isNull() && startRoot && (shadowAncestor != startRoot)) - p = Position(shadowAncestor, 0); - while (p.isNotNull() && !(lowestEditableAncestor(p.node()) == baseEditableAncestor && !isEditablePosition(p))) { - Node* root = editableRootForPosition(p); - shadowAncestor = root ? root->shadowAncestorNode() : 0; - p = isAtomicNode(p.node()) ? positionAfterNode(p.node()) : nextVisuallyDistinctCandidate(p); - if (p.isNull() && (shadowAncestor != root)) - p = Position(shadowAncestor, 0); - } - VisiblePosition next(p); - - if (next.isNull()) { - ASSERT_NOT_REACHED(); - m_base = Position(); - m_extent = Position(); - validate(); - return; - } - m_start = next.deepEquivalent(); - } - } - - // Correct the extent if necessary. - if (baseEditableAncestor != lowestEditableAncestor(m_extent.node())) - m_extent = m_baseIsFirst ? m_end : m_start; -} - -bool Selection::isContentEditable() const -{ - return isEditablePosition(start()); -} - -bool Selection::isContentRichlyEditable() const -{ - return isRichlyEditablePosition(start()); -} - -Element* Selection::rootEditableElement() const -{ - return editableRootForPosition(start()); -} - -Node* Selection::shadowTreeRootNode() const -{ - return start().node() ? start().node()->shadowTreeRootNode() : 0; -} - -void Selection::debugPosition() const -{ - if (!m_start.node()) - return; - - fprintf(stderr, "Selection =================\n"); - - if (m_start == m_end) { - Position pos = m_start; - fprintf(stderr, "pos: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); - } else { - Position pos = m_start; - fprintf(stderr, "start: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); - fprintf(stderr, "-----------------------------------\n"); - pos = m_end; - fprintf(stderr, "end: %s %p:%d\n", pos.node()->nodeName().utf8().data(), pos.node(), pos.offset()); - fprintf(stderr, "-----------------------------------\n"); - } - - fprintf(stderr, "================================\n"); -} - -#ifndef NDEBUG - -void Selection::formatForDebugger(char* buffer, unsigned length) const -{ - String result; - String s; - - if (isNone()) { - result = "<none>"; - } else { - const int FormatBufferSize = 1024; - char s[FormatBufferSize]; - result += "from "; - start().formatForDebugger(s, FormatBufferSize); - result += s; - result += " to "; - end().formatForDebugger(s, FormatBufferSize); - result += s; - } - - strncpy(buffer, result.utf8().data(), length - 1); -} - -void Selection::showTreeForThis() const -{ - if (start().node()) { - start().node()->showTreeAndMark(start().node(), "S", end().node(), "E"); - fprintf(stderr, "start offset: %d, end offset: %d\n", start().offset(), end().offset()); - } -} - -#endif - -} // namespace WebCore - -#ifndef NDEBUG - -void showTree(const WebCore::Selection& sel) -{ - sel.showTreeForThis(); -} - -void showTree(const WebCore::Selection* sel) -{ - if (sel) - sel->showTreeForThis(); -} - -#endif diff --git a/webkit/pending/Settings.cpp b/webkit/pending/Settings.cpp deleted file mode 100644 index 9e7e82a..0000000 --- a/webkit/pending/Settings.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "Settings.h" - -#include "Frame.h" -#include "FrameTree.h" -#include "Page.h" -#include "PageCache.h" -#include "HistoryItem.h" - -#if ENABLE(DATABASE) -#include "DatabaseTracker.h" -#endif - -namespace WebCore { - -static void setNeedsReapplyStylesInAllFrames(Page* page) -{ - for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) - frame->setNeedsReapplyStyles(); -} - -Settings::Settings(Page* page) - : m_page(page) - , m_editableLinkBehavior(EditableLinkDefaultBehavior) - , m_minimumFontSize(0) - , m_minimumLogicalFontSize(0) - , m_defaultFontSize(0) - , m_defaultFixedFontSize(0) - , m_isJavaEnabled(false) - , m_loadsImagesAutomatically(false) - , m_privateBrowsingEnabled(false) - , m_arePluginsEnabled(false) - , m_isJavaScriptEnabled(false) - , m_javaScriptCanOpenWindowsAutomatically(false) - , m_shouldPrintBackgrounds(false) - , m_textAreasAreResizable(false) -#if ENABLE(DASHBOARD_SUPPORT) - , m_usesDashboardBackwardCompatibilityMode(false) -#endif - , m_needsAdobeFrameReloadingQuirk(false) - , m_needsKeyboardEventDisambiguationQuirks(false) - , m_isDOMPasteAllowed(false) - , m_shrinksStandaloneImagesToFit(true) - , m_usesPageCache(false) - , m_showsURLsInToolTips(false) - , m_forceFTPDirectoryListings(false) - , m_developerExtrasEnabled(false) - , m_authorAndUserStylesEnabled(true) - , m_needsSiteSpecificQuirks(false) - , m_fontRenderingMode(0) - , m_webArchiveDebugModeEnabled(false) - , m_inApplicationChromeMode(false) - , m_offlineWebApplicationCacheEnabled(false) - , m_rangeMutationDisabledForOldAppleMail(false) - , m_shouldPaintCustomScrollbars(false) - , m_zoomsTextOnly(false) - , m_enforceCSSMIMETypeInStrictMode(true) - , m_usesEncodingDetector(false) - , m_allow_scripts_to_close_windows(false) -{ - // A Frame may not have been created yet, so we initialize the AtomicString - // hash before trying to use it. - AtomicString::init(); -} - -void Settings::setStandardFontFamily(const AtomicString& standardFontFamily) -{ - if (standardFontFamily == m_standardFontFamily) - return; - - m_standardFontFamily = standardFontFamily; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setFixedFontFamily(const AtomicString& fixedFontFamily) -{ - if (m_fixedFontFamily == fixedFontFamily) - return; - - m_fixedFontFamily = fixedFontFamily; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setSerifFontFamily(const AtomicString& serifFontFamily) -{ - if (m_serifFontFamily == serifFontFamily) - return; - - m_serifFontFamily = serifFontFamily; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setSansSerifFontFamily(const AtomicString& sansSerifFontFamily) -{ - if (m_sansSerifFontFamily == sansSerifFontFamily) - return; - - m_sansSerifFontFamily = sansSerifFontFamily; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setCursiveFontFamily(const AtomicString& cursiveFontFamily) -{ - if (m_cursiveFontFamily == cursiveFontFamily) - return; - - m_cursiveFontFamily = cursiveFontFamily; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setFantasyFontFamily(const AtomicString& fantasyFontFamily) -{ - if (m_fantasyFontFamily == fantasyFontFamily) - return; - - m_fantasyFontFamily = fantasyFontFamily; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setMinimumFontSize(int minimumFontSize) -{ - if (m_minimumFontSize == minimumFontSize) - return; - - m_minimumFontSize = minimumFontSize; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setMinimumLogicalFontSize(int minimumLogicalFontSize) -{ - if (m_minimumLogicalFontSize == minimumLogicalFontSize) - return; - - m_minimumLogicalFontSize = minimumLogicalFontSize; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setDefaultFontSize(int defaultFontSize) -{ - if (m_defaultFontSize == defaultFontSize) - return; - - m_defaultFontSize = defaultFontSize; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setDefaultFixedFontSize(int defaultFontSize) -{ - if (m_defaultFixedFontSize == defaultFontSize) - return; - - m_defaultFixedFontSize = defaultFontSize; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setLoadsImagesAutomatically(bool loadsImagesAutomatically) -{ - m_loadsImagesAutomatically = loadsImagesAutomatically; -} - -void Settings::setJavaScriptEnabled(bool isJavaScriptEnabled) -{ - m_isJavaScriptEnabled = isJavaScriptEnabled; -} - -void Settings::setJavaEnabled(bool isJavaEnabled) -{ - m_isJavaEnabled = isJavaEnabled; -} - -void Settings::setPluginsEnabled(bool arePluginsEnabled) -{ - m_arePluginsEnabled = arePluginsEnabled; -} - -void Settings::setPrivateBrowsingEnabled(bool privateBrowsingEnabled) -{ - m_privateBrowsingEnabled = privateBrowsingEnabled; -} - -void Settings::setJavaScriptCanOpenWindowsAutomatically(bool javaScriptCanOpenWindowsAutomatically) -{ - m_javaScriptCanOpenWindowsAutomatically = javaScriptCanOpenWindowsAutomatically; -} - -void Settings::setDefaultTextEncodingName(const String& defaultTextEncodingName) -{ - m_defaultTextEncodingName = defaultTextEncodingName; -} - -void Settings::setUserStyleSheetLocation(const KURL& userStyleSheetLocation) -{ - if (m_userStyleSheetLocation == userStyleSheetLocation) - return; - - m_userStyleSheetLocation = userStyleSheetLocation; - - m_page->userStyleSheetLocationChanged(); - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setShouldPrintBackgrounds(bool shouldPrintBackgrounds) -{ - m_shouldPrintBackgrounds = shouldPrintBackgrounds; -} - -void Settings::setTextAreasAreResizable(bool textAreasAreResizable) -{ - if (m_textAreasAreResizable == textAreasAreResizable) - return; - - m_textAreasAreResizable = textAreasAreResizable; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setEditableLinkBehavior(EditableLinkBehavior editableLinkBehavior) -{ - m_editableLinkBehavior = editableLinkBehavior; -} - -#if ENABLE(DASHBOARD_SUPPORT) -void Settings::setUsesDashboardBackwardCompatibilityMode(bool usesDashboardBackwardCompatibilityMode) -{ - m_usesDashboardBackwardCompatibilityMode = usesDashboardBackwardCompatibilityMode; -} -#endif - -// FIXME: This quirk is needed because of Radar 4674537 and 5211271. We need to phase it out once Adobe -// can fix the bug from their end. -void Settings::setNeedsAdobeFrameReloadingQuirk(bool shouldNotReloadIFramesForUnchangedSRC) -{ - m_needsAdobeFrameReloadingQuirk = shouldNotReloadIFramesForUnchangedSRC; -} - -// This is a quirk we are pro-actively applying to old applications. It changes keyboard event dispatching, -// making keyIdentifier available on keypress events, making charCode available on keydown/keyup events, -// and getting keypress dispatched in more cases. -void Settings::setNeedsKeyboardEventDisambiguationQuirks(bool needsQuirks) -{ - m_needsKeyboardEventDisambiguationQuirks = needsQuirks; -} - -void Settings::setDOMPasteAllowed(bool DOMPasteAllowed) -{ - m_isDOMPasteAllowed = DOMPasteAllowed; -} - -void Settings::setUsesPageCache(bool usesPageCache) -{ - if (m_usesPageCache == usesPageCache) - return; - - m_usesPageCache = usesPageCache; - if (!m_usesPageCache) { - HistoryItemVector& historyItems = m_page->backForwardList()->entries(); - for (unsigned i = 0; i < historyItems.size(); i++) - pageCache()->remove(historyItems[i].get()); - pageCache()->releaseAutoreleasedPagesNow(); - } -} - -void Settings::setShrinksStandaloneImagesToFit(bool shrinksStandaloneImagesToFit) -{ - m_shrinksStandaloneImagesToFit = shrinksStandaloneImagesToFit; -} - -void Settings::setShowsURLsInToolTips(bool showsURLsInToolTips) -{ - m_showsURLsInToolTips = showsURLsInToolTips; -} - -void Settings::setFTPDirectoryTemplatePath(const String& path) -{ - m_ftpDirectoryTemplatePath = path; -} - -void Settings::setForceFTPDirectoryListings(bool force) -{ - m_forceFTPDirectoryListings = force; -} - -void Settings::setDeveloperExtrasEnabled(bool developerExtrasEnabled) -{ - m_developerExtrasEnabled = developerExtrasEnabled; -} - -void Settings::setAuthorAndUserStylesEnabled(bool authorAndUserStylesEnabled) -{ - if (m_authorAndUserStylesEnabled == authorAndUserStylesEnabled) - return; - - m_authorAndUserStylesEnabled = authorAndUserStylesEnabled; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setFontRenderingMode(FontRenderingMode mode) -{ - if (fontRenderingMode() == mode) - return; - m_fontRenderingMode = mode; - setNeedsReapplyStylesInAllFrames(m_page); -} - -FontRenderingMode Settings::fontRenderingMode() const -{ - return static_cast<FontRenderingMode>(m_fontRenderingMode); -} - -void Settings::setNeedsSiteSpecificQuirks(bool needsQuirks) -{ - m_needsSiteSpecificQuirks = needsQuirks; -} - -void Settings::setWebArchiveDebugModeEnabled(bool enabled) -{ - m_webArchiveDebugModeEnabled = enabled; -} - -void Settings::setLocalStorageDatabasePath(const String& path) -{ - m_localStorageDatabasePath = path; -} - -void Settings::disableRangeMutationForOldAppleMail(bool disable) -{ - m_rangeMutationDisabledForOldAppleMail = disable; -} - -void Settings::setApplicationChromeMode(bool mode) -{ - m_inApplicationChromeMode = mode; -} - -void Settings::setOfflineWebApplicationCacheEnabled(bool enabled) -{ - m_offlineWebApplicationCacheEnabled = enabled; -} - -void Settings::setShouldPaintCustomScrollbars(bool shouldPaintCustomScrollbars) -{ - m_shouldPaintCustomScrollbars = shouldPaintCustomScrollbars; -} - -void Settings::setZoomsTextOnly(bool zoomsTextOnly) -{ - if (zoomsTextOnly == m_zoomsTextOnly) - return; - - m_zoomsTextOnly = zoomsTextOnly; - setNeedsReapplyStylesInAllFrames(m_page); -} - -void Settings::setEnforceCSSMIMETypeInStrictMode(bool enforceCSSMIMETypeInStrictMode) -{ - m_enforceCSSMIMETypeInStrictMode = enforceCSSMIMETypeInStrictMode; -} - -void Settings::setUsesUniversalDetector(bool usesEncodingDetector) -{ - m_usesEncodingDetector = usesEncodingDetector; -} - -void Settings::setAllowScriptsToCloseWindows(bool allow_scripts_to_close_windows) -{ - m_allow_scripts_to_close_windows = allow_scripts_to_close_windows; -} - -} // namespace WebCore diff --git a/webkit/pending/Settings.h b/webkit/pending/Settings.h deleted file mode 100644 index 0d12520..0000000 --- a/webkit/pending/Settings.h +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. - * (C) 2006 Graham Dennis (graham.dennis@gmail.com) - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef Settings_h -#define Settings_h - -#include "AtomicString.h" -#include "FontDescription.h" -#include "KURL.h" - -namespace WebCore { - - class Page; - - enum EditableLinkBehavior { - EditableLinkDefaultBehavior = 0, - EditableLinkAlwaysLive, - EditableLinkOnlyLiveWithShiftKey, - EditableLinkLiveWhenNotFocused, - EditableLinkNeverLive - }; - - class Settings { - public: - Settings(Page*); - - void setStandardFontFamily(const AtomicString&); - const AtomicString& standardFontFamily() const { return m_standardFontFamily; } - - void setFixedFontFamily(const AtomicString&); - const AtomicString& fixedFontFamily() const { return m_fixedFontFamily; } - - void setSerifFontFamily(const AtomicString&); - const AtomicString& serifFontFamily() const { return m_serifFontFamily; } - - void setSansSerifFontFamily(const AtomicString&); - const AtomicString& sansSerifFontFamily() const { return m_sansSerifFontFamily; } - - void setCursiveFontFamily(const AtomicString&); - const AtomicString& cursiveFontFamily() const { return m_cursiveFontFamily; } - - void setFantasyFontFamily(const AtomicString&); - const AtomicString& fantasyFontFamily() const { return m_fantasyFontFamily; } - - void setMinimumFontSize(int); - int minimumFontSize() const { return m_minimumFontSize; } - - void setMinimumLogicalFontSize(int); - int minimumLogicalFontSize() const { return m_minimumLogicalFontSize; } - - void setDefaultFontSize(int); - int defaultFontSize() const { return m_defaultFontSize; } - - void setDefaultFixedFontSize(int); - int defaultFixedFontSize() const { return m_defaultFixedFontSize; } - - void setLoadsImagesAutomatically(bool); - bool loadsImagesAutomatically() const { return m_loadsImagesAutomatically; } - - void setJavaScriptEnabled(bool); - bool isJavaScriptEnabled() const { return m_isJavaScriptEnabled; } - - void setJavaScriptCanOpenWindowsAutomatically(bool); - bool JavaScriptCanOpenWindowsAutomatically() const { return m_javaScriptCanOpenWindowsAutomatically; } - - void setJavaEnabled(bool); - bool isJavaEnabled() const { return m_isJavaEnabled; } - - void setPluginsEnabled(bool); - bool arePluginsEnabled() const { return m_arePluginsEnabled; } - - void setPrivateBrowsingEnabled(bool); - bool privateBrowsingEnabled() const { return m_privateBrowsingEnabled; } - - void setDefaultTextEncodingName(const String&); - const String& defaultTextEncodingName() const { return m_defaultTextEncodingName; } - - void setUserStyleSheetLocation(const KURL&); - const KURL& userStyleSheetLocation() const { return m_userStyleSheetLocation; } - - void setShouldPrintBackgrounds(bool); - bool shouldPrintBackgrounds() const { return m_shouldPrintBackgrounds; } - - void setTextAreasAreResizable(bool); - bool textAreasAreResizable() const { return m_textAreasAreResizable; } - - void setEditableLinkBehavior(EditableLinkBehavior); - EditableLinkBehavior editableLinkBehavior() const { return m_editableLinkBehavior; } - -#if ENABLE(DASHBOARD_SUPPORT) - void setUsesDashboardBackwardCompatibilityMode(bool); - bool usesDashboardBackwardCompatibilityMode() const { return m_usesDashboardBackwardCompatibilityMode; } -#endif - - void setNeedsAdobeFrameReloadingQuirk(bool); - bool needsAcrobatFrameReloadingQuirk() const { return m_needsAdobeFrameReloadingQuirk; } - - void setNeedsKeyboardEventDisambiguationQuirks(bool); - bool needsKeyboardEventDisambiguationQuirks() const { return m_needsKeyboardEventDisambiguationQuirks; } - - void setDOMPasteAllowed(bool); - bool isDOMPasteAllowed() const { return m_isDOMPasteAllowed; } - - void setUsesPageCache(bool); - bool usesPageCache() const { return m_usesPageCache; } - - void setShrinksStandaloneImagesToFit(bool); - bool shrinksStandaloneImagesToFit() const { return m_shrinksStandaloneImagesToFit; } - - void setShowsURLsInToolTips(bool); - bool showsURLsInToolTips() const { return m_showsURLsInToolTips; } - - void setFTPDirectoryTemplatePath(const String&); - const String& ftpDirectoryTemplatePath() const { return m_ftpDirectoryTemplatePath; } - - void setForceFTPDirectoryListings(bool); - bool forceFTPDirectoryListings() const { return m_forceFTPDirectoryListings; } - - void setDeveloperExtrasEnabled(bool); - bool developerExtrasEnabled() const { return m_developerExtrasEnabled; } - - void setUsesUniversalDetector(bool); - bool usesEncodingDetector() const { return m_usesEncodingDetector; } - - void setAuthorAndUserStylesEnabled(bool); - bool authorAndUserStylesEnabled() const { return m_authorAndUserStylesEnabled; } - - void setFontRenderingMode(FontRenderingMode mode); - FontRenderingMode fontRenderingMode() const; - - void setNeedsSiteSpecificQuirks(bool); - bool needsSiteSpecificQuirks() const { return m_needsSiteSpecificQuirks; } - - void setWebArchiveDebugModeEnabled(bool); - bool webArchiveDebugModeEnabled() const { return m_webArchiveDebugModeEnabled; } - - void setLocalStorageDatabasePath(const String&); - const String& localStorageDatabasePath() const { return m_localStorageDatabasePath; } - - void disableRangeMutationForOldAppleMail(bool); - bool rangeMutationDisabledForOldAppleMail() const { return m_rangeMutationDisabledForOldAppleMail; } - - void setApplicationChromeMode(bool); - bool inApplicationChromeMode() const { return m_inApplicationChromeMode; } - - void setOfflineWebApplicationCacheEnabled(bool); - bool offlineWebApplicationCacheEnabled() const { return m_offlineWebApplicationCacheEnabled; } - - void setShouldPaintCustomScrollbars(bool); - bool shouldPaintCustomScrollbars() const { return m_shouldPaintCustomScrollbars; } - - void setZoomsTextOnly(bool); - bool zoomsTextOnly() const { return m_zoomsTextOnly; } - - void setEnforceCSSMIMETypeInStrictMode(bool); - bool enforceCSSMIMETypeInStrictMode() { return m_enforceCSSMIMETypeInStrictMode; } - - void setAllowScriptsToCloseWindows(bool); - bool allowScriptsToCloseWindows() const { return m_allow_scripts_to_close_windows; } - - private: - Page* m_page; - - String m_defaultTextEncodingName; - String m_ftpDirectoryTemplatePath; - String m_localStorageDatabasePath; - KURL m_userStyleSheetLocation; - AtomicString m_standardFontFamily; - AtomicString m_fixedFontFamily; - AtomicString m_serifFontFamily; - AtomicString m_sansSerifFontFamily; - AtomicString m_cursiveFontFamily; - AtomicString m_fantasyFontFamily; - EditableLinkBehavior m_editableLinkBehavior; - int m_minimumFontSize; - int m_minimumLogicalFontSize; - int m_defaultFontSize; - int m_defaultFixedFontSize; - bool m_isJavaEnabled : 1; - bool m_loadsImagesAutomatically : 1; - bool m_privateBrowsingEnabled : 1; - bool m_arePluginsEnabled : 1; - bool m_isJavaScriptEnabled : 1; - bool m_javaScriptCanOpenWindowsAutomatically : 1; - bool m_shouldPrintBackgrounds : 1; - bool m_textAreasAreResizable : 1; -#if ENABLE(DASHBOARD_SUPPORT) - bool m_usesDashboardBackwardCompatibilityMode : 1; -#endif - bool m_needsAdobeFrameReloadingQuirk : 1; - bool m_needsKeyboardEventDisambiguationQuirks : 1; - bool m_isDOMPasteAllowed : 1; - bool m_shrinksStandaloneImagesToFit : 1; - bool m_usesPageCache: 1; - bool m_showsURLsInToolTips : 1; - bool m_forceFTPDirectoryListings : 1; - bool m_developerExtrasEnabled : 1; - bool m_authorAndUserStylesEnabled : 1; - bool m_needsSiteSpecificQuirks : 1; - unsigned m_fontRenderingMode : 1; - bool m_webArchiveDebugModeEnabled : 1; - bool m_inApplicationChromeMode : 1; - bool m_offlineWebApplicationCacheEnabled : 1; - bool m_rangeMutationDisabledForOldAppleMail : 1; - bool m_shouldPaintCustomScrollbars : 1; - bool m_zoomsTextOnly : 1; - bool m_enforceCSSMIMETypeInStrictMode : 1; - bool m_usesEncodingDetector : 1; - bool m_allow_scripts_to_close_windows : 1; - }; - -} // namespace WebCore - -#endif // Settings_h diff --git a/webkit/pending/SimpleFontData.cpp b/webkit/pending/SimpleFontData.cpp deleted file mode 100644 index 14578a8..0000000 --- a/webkit/pending/SimpleFontData.cpp +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Copyright (C) 2005, 2008 Apple Computer, Inc. All rights reserved. - * Copyright (C) 2006 Alexey Proskuryakov - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "SimpleFontData.h" - -#include "FontCache.h" - -#if ENABLE(SVG_FONTS) -#include "SVGFontData.h" -#include "SVGFontFaceElement.h" -#endif - -#include <wtf/MathExtras.h> - -namespace WebCore { - -SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool loading, SVGFontData* svgFontData) - : m_font(f) - , m_treatAsFixedPitch(false) -#if ENABLE(SVG_FONTS) - , m_svgFontData(svgFontData) -#endif - , m_isCustomFont(customFont) - , m_isLoading(loading) - , m_smallCapsFontData(0) - , m_zeroWidthFontData(new ZeroWidthFontData()) - , m_cjkWidthFontData(new CJKWidthFontData()) -{ -#if ENABLE(SVG_FONTS) && !PLATFORM(QT) - if (SVGFontFaceElement* svgFontFaceElement = svgFontData ? svgFontData->svgFontFaceElement() : 0) { - m_unitsPerEm = svgFontFaceElement->unitsPerEm(); - - double scale = f.size(); - if (m_unitsPerEm) - scale /= m_unitsPerEm; - - m_ascent = static_cast<int>(svgFontFaceElement->ascent() * scale); - m_descent = static_cast<int>(svgFontFaceElement->descent() * scale); - // TODO(ojan): What should SVG fonts use for avgCharWidth/maxCharWidth? - // This is currently only used in RenderTextControl.cpp for the width - // of textareas with a non-fixed width. - m_avgCharWidth = 0; - m_maxCharWidth = 0; - m_xHeight = static_cast<int>(svgFontFaceElement->xHeight() * scale); - m_lineGap = 0.1f * f.size(); - m_lineSpacing = m_ascent + m_descent + m_lineGap; - - m_spaceGlyph = 0; - m_spaceWidth = 0; - m_adjustedSpaceWidth = 0; - determinePitch(); - m_missingGlyphData.fontData = this; - m_missingGlyphData.glyph = 0; - m_zeroWidthFontData->init(this); - m_cjkWidthFontData->init(this); - return; - } -#endif - - platformInit(); - - GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); - if (!glyphPageZero) { - LOG_ERROR("Failed to get glyph page zero."); - m_spaceGlyph = 0; - m_spaceWidth = 0; - m_adjustedSpaceWidth = 0; - determinePitch(); - m_missingGlyphData.fontData = this; - m_missingGlyphData.glyph = 0; - m_zeroWidthFontData->init(this); - m_cjkWidthFontData->init(this); - return; - } - - // Nasty hack to determine if we should round or ceil space widths. - // If the font is monospace or fake monospace we ceil to ensure that - // every character and the space are the same width. Otherwise we round. - static const UChar32 space_char = ' '; - m_spaceGlyph = glyphPageZero->glyphDataForCharacter(space_char).glyph; - float width = widthForGlyph(m_spaceGlyph); - m_spaceWidth = width; - determinePitch(); - m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width); - - // TODO(dglazkov): Investigate and implement across platforms, if needed -#if PLATFORM(WIN) - // ZERO WIDTH SPACES are explicitly mapped to share the glyph - // with SPACE (with width adjusted to 0) during GlyphPage::fill - // This is currently only implemented for Windows port. The FontData - // remapping may very well be needed for other platforms. -#else - // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE. - // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph. - // See <http://bugs.webkit.org/show_bug.cgi?id=13178> - // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0, - // are mapped to the ZERO WIDTH SPACE glyph. - Glyph zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph; - if (zeroWidthSpaceGlyph) { - if (zeroWidthSpaceGlyph != m_spaceGlyph) - m_glyphToWidthMap.setWidthForGlyph(zeroWidthSpaceGlyph, 0); - else - LOG_ERROR("Font maps SPACE and ZERO WIDTH SPACE to the same glyph. Glyph width not overridden."); - } -#endif - - m_missingGlyphData.fontData = this; - m_missingGlyphData.glyph = 0; - m_zeroWidthFontData->init(this); - m_cjkWidthFontData->init(this); -} - -SimpleFontData::SimpleFontData() - : m_treatAsFixedPitch(false) -#if ENABLE(SVG_FONTS) - , m_svgFontData(0) -#endif - , m_isCustomFont(0) - , m_isLoading(0) - , m_smallCapsFontData(0) -{ -} - -SimpleFontData::~SimpleFontData() -{ - if (!isCustomFont()) { - if (m_smallCapsFontData) - FontCache::releaseFontData(m_smallCapsFontData); - GlyphPageTreeNode::pruneTreeFontData(this); - } - -#if ENABLE(SVG_FONTS) && !PLATFORM(QT) - if (!m_svgFontData || !m_svgFontData->svgFontFaceElement()) -#endif - platformDestroy(); -} - -float SimpleFontData::widthForGlyph(Glyph glyph) const -{ - float width = m_glyphToWidthMap.widthForGlyph(glyph); - if (width != cGlyphWidthUnknown) - return width; - - width = platformWidthForGlyph(glyph); - m_glyphToWidthMap.setWidthForGlyph(glyph, width); - - return width; -} - -const SimpleFontData* SimpleFontData::fontDataForCharacter(UChar32) const -{ - return this; -} - -bool SimpleFontData::isSegmented() const -{ - return false; -} - -const SimpleFontData* SimpleFontData::zeroWidthFontData() const -{ - return m_zeroWidthFontData.get(); -} - -const SimpleFontData* SimpleFontData::cjkWidthFontData() const -{ - return m_cjkWidthFontData.get(); -} - -void ZeroWidthFontData::init(SimpleFontData* fontData) -{ - m_font = fontData->m_font; - m_smallCapsFontData = fontData->m_smallCapsFontData; - m_ascent = fontData->m_ascent; - m_descent = fontData->m_descent; - m_lineSpacing = fontData->m_lineSpacing; - m_lineGap = fontData->m_lineGap; - m_maxCharWidth = 0; - m_avgCharWidth = 0; - m_xHeight = fontData->m_xHeight; - m_unitsPerEm = fontData->m_unitsPerEm; - m_spaceWidth = 0; - m_spaceGlyph = 0; - m_adjustedSpaceWidth = fontData->m_adjustedSpaceWidth; -#if PLATFORM(WIN) - m_scriptCache = 0; - m_scriptFontProperties = 0; -#endif -} - -CJKWidthFontData::CJKWidthFontData() - : m_cjkGlyphWidth(cGlyphWidthUnknown) -{ -} - -float CJKWidthFontData::widthForGlyph(Glyph glyph) const -{ - if (m_cjkGlyphWidth != cGlyphWidthUnknown) - return m_cjkGlyphWidth; - - float width = platformWidthForGlyph(glyph); - m_cjkGlyphWidth = width; - -#ifndef NDEBUG - // Test our optimization that assuming all CGK glyphs have the same width - const float actual_width = platformWidthForGlyph(glyph); - ASSERT((cGlyphWidthUnknown == width) || (actual_width == width)); -#endif - - return width; -} - -// static -// TODO(dglazkov): Move to Font::isCJKCodePoint for consistency -bool SimpleFontData::isCJKCodePoint(UChar32 c) -{ - // AC00..D7AF; Hangul Syllables - if ((0xAC00 <= c) && (c <= 0xD7AF)) - return true; - - // CJK ideographs - UErrorCode errorCode = U_ZERO_ERROR; // has to be initialized. - return uscript_getScript(c, &errorCode) == USCRIPT_HAN && - U_SUCCESS(errorCode); -} - - -} // namespace WebCore diff --git a/webkit/pending/SimpleFontData.h b/webkit/pending/SimpleFontData.h deleted file mode 100644 index 1c066a4..0000000 --- a/webkit/pending/SimpleFontData.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * This file is part of the internal font implementation. - * - * Copyright (C) 2006, 2008 Apple Computer, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef SimpleFontData_h -#define SimpleFontData_h - -#include "FontData.h" -#include "FontPlatformData.h" -#include "GlyphPageTreeNode.h" -#include "GlyphWidthMap.h" -#include <wtf/OwnPtr.h> - -#if PLATFORM(MAC) -typedef struct OpaqueATSUStyle* ATSUStyle; -#endif - -#if PLATFORM(WIN) -#include <usp10.h> -#endif - -#if PLATFORM(CAIRO) -#include <cairo.h> -#endif - -namespace WebCore { - -class FontDescription; -class FontPlatformData; -class SharedBuffer; -class SVGFontData; -class WidthMap; -class ZeroWidthFontData; -class CJKWidthFontData; - -enum Pitch { UnknownPitch, FixedPitch, VariablePitch }; - -class SimpleFontData : public FontData { -public: - SimpleFontData(const FontPlatformData&, bool customFont = false, bool loading = false, SVGFontData* data = 0); - virtual ~SimpleFontData(); - -protected: - // sub-class constructor - SimpleFontData(); - -public: - const FontPlatformData& platformData() const { return m_font; } - SimpleFontData* smallCapsFontData(const FontDescription& fontDescription) const; - - // vertical metrics - int ascent() const { return m_ascent; } - int descent() const { return m_descent; } - int lineSpacing() const { return m_lineSpacing; } - int lineGap() const { return m_lineGap; } - int maxCharWidth() const { return m_maxCharWidth; } - int avgCharWidth() const { return m_avgCharWidth; } - - float xHeight() const { return m_xHeight; } - unsigned unitsPerEm() const { return m_unitsPerEm; } - - virtual float widthForGlyph(Glyph) const; - float platformWidthForGlyph(Glyph) const; - - virtual const SimpleFontData* fontDataForCharacter(UChar32) const; - virtual bool containsCharacters(const UChar*, int length) const; - - void determinePitch(); - Pitch pitch() const { return m_treatAsFixedPitch ? FixedPitch : VariablePitch; } - - static bool isCJKCodePoint(UChar32 c); - -#if ENABLE(SVG_FONTS) - SVGFontData* svgFontData() const { return m_svgFontData.get(); } - bool isSVGFont() const { return m_svgFontData; } -#else - bool isSVGFont() const { return false; } -#endif - - virtual bool isCustomFont() const { return m_isCustomFont; } - virtual bool isLoading() const { return m_isLoading; } - virtual bool isSegmented() const; - - const GlyphData& missingGlyphData() const { return m_missingGlyphData; } - -#if PLATFORM(MAC) - NSFont* getNSFont() const { return m_font.font(); } - void checkShapesArabic() const; - bool shapesArabic() const - { - if (!m_checkedShapesArabic) - checkShapesArabic(); - return m_shapesArabic; - } -#endif - -#if PLATFORM(WIN) - bool isSystemFont() const { return m_isSystemFont; } - SCRIPT_FONTPROPERTIES* scriptFontProperties() const; - SCRIPT_CACHE* scriptCache() const { return &m_scriptCache; } - - static void setShouldApplyMacAscentHack(bool); - static bool shouldApplyMacAscentHack(); -#endif - -#if PLATFORM(GTK) && PLATFORM(CAIRO) - void setFont(cairo_t*) const; -#endif - -#if PLATFORM(WX) - wxFont getWxFont() const { return m_font.font(); } -#endif - - const SimpleFontData* zeroWidthFontData() const; - const SimpleFontData* cjkWidthFontData() const; - -private: - void platformInit(); - void platformDestroy(); - - void commonInit(); - -#if PLATFORM(WIN) - void initGDIFont(); - void platformCommonDestroy(); - float widthForGDIGlyph(Glyph glyph) const; -#endif - -public: - int m_ascent; - int m_descent; - int m_lineSpacing; - int m_lineGap; - int m_maxCharWidth; - int m_avgCharWidth; - float m_xHeight; - unsigned m_unitsPerEm; - - FontPlatformData m_font; - - mutable GlyphWidthMap m_glyphToWidthMap; - - bool m_treatAsFixedPitch; - -#if ENABLE(SVG_FONTS) - OwnPtr<SVGFontData> m_svgFontData; -#endif - - bool m_isCustomFont; // Whether or not we are custom font loaded via @font-face - bool m_isLoading; // Whether or not this custom font is still in the act of loading. - - Glyph m_spaceGlyph; - float m_spaceWidth; - float m_adjustedSpaceWidth; - - GlyphData m_missingGlyphData; - - mutable SimpleFontData* m_smallCapsFontData; - -#if PLATFORM(CG) || PLATFORM(WIN) - float m_syntheticBoldOffset; -#endif - -#if PLATFORM(MAC) - void* m_styleGroup; - mutable ATSUStyle m_ATSUStyle; - mutable bool m_ATSUStyleInitialized; - mutable bool m_ATSUMirrors; - mutable bool m_checkedShapesArabic; - mutable bool m_shapesArabic; -#endif - -#if PLATFORM(WIN) - bool m_isSystemFont; - mutable SCRIPT_CACHE m_scriptCache; - mutable SCRIPT_FONTPROPERTIES* m_scriptFontProperties; -#endif - -private: - OwnPtr<ZeroWidthFontData> m_zeroWidthFontData; - OwnPtr<CJKWidthFontData> m_cjkWidthFontData; -}; - -// SimpleFontData sub-classes: - -// Has a single zero-width glyph, used for ZWS and -// UCHAR_DEFAULT_IGNORABLE_CODE_POINT characters -class ZeroWidthFontData : public SimpleFontData { -public: - void init(SimpleFontData*); - virtual float widthForGlyph(Glyph) const { return 0.0f; } -}; - -// Monospaced, single glyph and width, used for CJK characters -// The assumption made here can break for some high-quality CJK fonts with -// proportional CJK glyphs. -class CJKWidthFontData : public ZeroWidthFontData { -public: - CJKWidthFontData(); - - virtual float widthForGlyph(Glyph) const; - -private: - // Optimization for CJK glyphs - mutable float m_cjkGlyphWidth; -}; - -} // namespace WebCore - -#endif // SimpleFontData_h diff --git a/webkit/pending/TextCodecICU.cpp b/webkit/pending/TextCodecICU.cpp deleted file mode 100644 index 3dfadea..0000000 --- a/webkit/pending/TextCodecICU.cpp +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2006 Alexey Proskuryakov <ap@nypop.com> - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR - * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY - * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#include "config.h" -#include "TextCodecICU.h" - -#include "CharacterNames.h" -#include "CString.h" -#include "PlatformString.h" -#include <unicode/ucnv.h> -#include <unicode/ucnv_cb.h> -#include <wtf/Assertions.h> - -using std::auto_ptr; -using std::min; - -namespace WebCore { - -const size_t ConversionBufferSize = 16384; - -static UConverter* cachedConverterICU; - -static auto_ptr<TextCodec> newTextCodecICU(const TextEncoding& encoding, const void*) -{ - return auto_ptr<TextCodec>(new TextCodecICU(encoding)); -} - -void TextCodecICU::registerBaseEncodingNames(EncodingNameRegistrar registrar) -{ - registrar("UTF-8", "UTF-8"); -} - -void TextCodecICU::registerBaseCodecs(TextCodecRegistrar registrar) -{ - registrar("UTF-8", newTextCodecICU, 0); -} - -// FIXME: Registering all the encodings we get from ucnv_getAvailableName -// includes encodings we don't want or need. For example: UTF16_PlatformEndian, -// UTF16_OppositeEndian, UTF32_PlatformEndian, UTF32_OppositeEndian, and all -// the encodings with commas and version numbers. - -void TextCodecICU::registerExtendedEncodingNames(EncodingNameRegistrar registrar) -{ - // We register Hebrew with logical ordering using a separate name. - // Otherwise, this would share the same canonical name as the - // visual ordering case, and then TextEncoding could not tell them - // apart; ICU works with either name. - registrar("ISO-8859-8-I", "ISO-8859-8-I"); - - int32_t numEncodings = ucnv_countAvailable(); - for (int32_t i = 0; i < numEncodings; ++i) { - const char* name = ucnv_getAvailableName(i); - UErrorCode error = U_ZERO_ERROR; - // Try MIME before trying IANA to pick up commonly used names like - // 'EUC-JP' instead of horrendeously long names like - // 'Extended_UNIX_Code_Packed_Format_for_Japanese'. - const char* standardName = ucnv_getStandardName(name, "MIME", &error); - if (!U_SUCCESS(error) || !standardName) { - error = U_ZERO_ERROR; - // Try IANA to pick up 'windows-12xx' and other names - // which are not preferred MIME names but are widely used. - standardName = ucnv_getStandardName(name, "IANA", &error); - if (!U_SUCCESS(error) || !standardName) - continue; - } - - // Here, we used to alias GB2312 and GB2312-80 to GBK, but our copy - // of ICU treats GB2312/GB2312-80 as synonyms of GBK so that we - // don't need that any more. - // Similarly, EUC-KR encodings all map to an extended version. - else if (strcmp(standardName, "KS_C_5601-1987") == 0 || strcmp(standardName, "EUC-KR") == 0) - standardName = "windows-949-2000"; - // And so on. - else if (strcmp(standardName, "ISO_8859-9:1989") == 0) - standardName = "windows-1254"; - else if (strcmp(standardName, "TIS-620") == 0) - standardName = "windows-874-2000"; - - registrar(standardName, standardName); - - uint16_t numAliases = ucnv_countAliases(name, &error); - ASSERT(U_SUCCESS(error)); - if (U_SUCCESS(error)) - for (uint16_t j = 0; j < numAliases; ++j) { - error = U_ZERO_ERROR; - const char* alias = ucnv_getAlias(name, j, &error); - ASSERT(U_SUCCESS(error)); - if (U_SUCCESS(error) && alias != standardName) - registrar(alias, standardName); - } - } - - // We used to map macroman and xmacroman to macintosh, but - // we don't need them any more because they're added to our - // local copy of ICU. - - // Additional aliases that historically were present in the encoding - // table in WebKit on Macintosh that don't seem to be present in ICU. - // Perhaps we can prove these are not used on the web and remove them. - // Or perhaps we can get them added to ICU. - registrar("cnbig5", "Big5"); - registrar("cngb", "EUC-CN"); - registrar("csISO88598I", "ISO_8859-8-I"); - registrar("csgb231280", "EUC-CN"); - registrar("dos720", "cp864"); - registrar("dos874", "TIS-620"); - registrar("jis7", "ISO-2022-JP"); - registrar("koi", "KOI8-R"); - registrar("logical", "ISO-8859-8-I"); - registrar("unicode11utf8", "UTF-8"); - registrar("unicode20utf8", "UTF-8"); - registrar("visual", "ISO-8859-8"); - registrar("winarabic", "windows-1256"); - registrar("winbaltic", "windows-1257"); - registrar("wincyrillic", "windows-1251"); - registrar("windows874", "windows874-2000"); - registrar("iso885911", "windows874-2000"); - registrar("wingreek", "windows-1253"); - registrar("winhebrew", "windows-1255"); - registrar("winlatin2", "windows-1250"); - registrar("winturkish", "windows-1254"); - registrar("winvietnamese", "windows-1258"); - registrar("xcp1250", "windows-1250"); - registrar("xcp1251", "windows-1251"); - registrar("xeuc", "EUC-JP"); - registrar("xeuccn", "EUC-CN"); - registrar("xgbk", "EUC-CN"); - registrar("xunicode20utf8", "UTF-8"); - registrar("xwindows949", "windows-949-2000"); - registrar("xxbig5", "Big5"); -} - -void TextCodecICU::registerExtendedCodecs(TextCodecRegistrar registrar) -{ - // See comment above in registerEncodingNames. - registrar("ISO-8859-8-I", newTextCodecICU, 0); - - int32_t numEncodings = ucnv_countAvailable(); - for (int32_t i = 0; i < numEncodings; ++i) { - const char* name = ucnv_getAvailableName(i); - UErrorCode error = U_ZERO_ERROR; - // Try MIME before trying IANA to pick up commonly used names like - // 'EUC-JP' instead of horrendeously long names like - // 'Extended_UNIX_Code_Packed_Format_for_Japanese'. - const char* standardName = ucnv_getStandardName(name, "MIME", &error); - if (!U_SUCCESS(error) || !standardName) { - error = U_ZERO_ERROR; - // Try IANA to pick up 'windows-12xx' and other names - // which are not preferred MIME names but are widely used. - standardName = ucnv_getStandardName(name, "IANA", &error); - if (!U_SUCCESS(error) || !standardName) - continue; - } - registrar(standardName, newTextCodecICU, 0); - } -} - -TextCodecICU::TextCodecICU(const TextEncoding& encoding) - : m_encoding(encoding) - , m_numBufferedBytes(0) - , m_converterICU(0) - , m_needsGBKFallbacks(false) -{ -} - -TextCodecICU::~TextCodecICU() -{ - releaseICUConverter(); -} - -void TextCodecICU::releaseICUConverter() const -{ - if (m_converterICU) { - if (cachedConverterICU) - ucnv_close(cachedConverterICU); - cachedConverterICU = m_converterICU; - m_converterICU = 0; - } -} - -void TextCodecICU::createICUConverter() const -{ - ASSERT(!m_converterICU); - - const char* name = m_encoding.name(); - m_needsGBKFallbacks = name[0] == 'G' && name[1] == 'B' && name[2] == 'K' && !name[3]; - - UErrorCode err; - - if (cachedConverterICU) { - err = U_ZERO_ERROR; - const char* cachedName = ucnv_getName(cachedConverterICU, &err); - if (U_SUCCESS(err) && m_encoding == cachedName) { - m_converterICU = cachedConverterICU; - cachedConverterICU = 0; - return; - } - } - - err = U_ZERO_ERROR; - m_converterICU = ucnv_open(m_encoding.name(), &err); -#if !LOG_DISABLED - if (err == U_AMBIGUOUS_ALIAS_WARNING) - LOG_ERROR("ICU ambiguous alias warning for encoding: %s", m_encoding.name()); -#endif - if (m_converterICU) - ucnv_setFallback(m_converterICU, TRUE); -} - -int TextCodecICU::decodeToBuffer(UChar* target, UChar* targetLimit, const char*& source, const char* sourceLimit, int32_t* offsets, bool flush, UErrorCode& err) -{ - UChar* targetStart = target; - err = U_ZERO_ERROR; - ucnv_toUnicode(m_converterICU, &target, targetLimit, &source, sourceLimit, offsets, flush, &err); - return target - targetStart; -} - -class ErrorCallbackSetter { -public: - ErrorCallbackSetter(UConverter* converter, bool stopOnError) - : m_converter(converter) - , m_shouldStopOnEncodingErrors(stopOnError) - { - if (m_shouldStopOnEncodingErrors) { - UErrorCode err = U_ZERO_ERROR; - ucnv_setToUCallBack(m_converter, UCNV_TO_U_CALLBACK_SUBSTITUTE, - UCNV_SUB_STOP_ON_ILLEGAL, &m_savedAction, - &m_savedContext, &err); - ASSERT(err == U_ZERO_ERROR); - } - } - ~ErrorCallbackSetter() - { - if (m_shouldStopOnEncodingErrors) { - UErrorCode err = U_ZERO_ERROR; - const void* oldContext; - UConverterToUCallback oldAction; - ucnv_setToUCallBack(m_converter, m_savedAction, - m_savedContext, &oldAction, - &oldContext, &err); - ASSERT(oldAction == UCNV_TO_U_CALLBACK_SUBSTITUTE); - ASSERT(!strcmp(static_cast<const char*>(oldContext), UCNV_SUB_STOP_ON_ILLEGAL)); - ASSERT(err == U_ZERO_ERROR); - } - } -private: - UConverter* m_converter; - bool m_shouldStopOnEncodingErrors; - const void* m_savedContext; - UConverterToUCallback m_savedAction; -}; - -String TextCodecICU::decode(const char* bytes, size_t length, bool flush, bool stopOnError, bool& sawError) -{ - // Get a converter for the passed-in encoding. - if (!m_converterICU) { - createICUConverter(); - ASSERT(m_converterICU); - if (!m_converterICU) { - LOG_ERROR("error creating ICU encoder even though encoding was in table"); - return String(); - } - } - - ErrorCallbackSetter callbackSetter(m_converterICU, stopOnError); - - Vector<UChar> result; - - UChar buffer[ConversionBufferSize]; - UChar* bufferLimit = buffer + ConversionBufferSize; - const char* source = reinterpret_cast<const char*>(bytes); - const char* sourceLimit = source + length; - int32_t* offsets = NULL; - UErrorCode err = U_ZERO_ERROR; - - do { - int ucharsDecoded = decodeToBuffer(buffer, bufferLimit, source, sourceLimit, offsets, flush, err); - result.append(buffer, ucharsDecoded); - } while (err == U_BUFFER_OVERFLOW_ERROR); - - if (U_FAILURE(err)) { - // flush the converter so it can be reused, and not be bothered by this error. - do { - decodeToBuffer(buffer, bufferLimit, source, sourceLimit, offsets, true, err); - } while (source < sourceLimit); - sawError = true; - } - - String resultString = String::adopt(result); - - // <http://bugs.webkit.org/show_bug.cgi?id=17014> - // Simplified Chinese pages use the code A3A0 to mean "full-width space", but ICU decodes it as U+E5E5. - if (m_encoding == "GBK" || m_encoding == "gb18030") - resultString.replace(0xE5E5, ideographicSpace); - - return resultString; -} - -// We need to apply these fallbacks ourselves as they are not currently supported by ICU and -// they were provided by the old TEC encoding path -// Needed to fix <rdar://problem/4708689> -static UChar getGbkEscape(UChar32 codePoint) -{ - switch (codePoint) { - case 0x01F9: - return 0xE7C8; - case 0x1E3F: - return 0xE7C7; - case 0x22EF: - return 0x2026; - case 0x301C: - return 0xFF5E; - default: - return 0; - } -} - -// Invalid character handler when writing escaped entities for unrepresentable -// characters. See the declaration of TextCodec::encode for more. -static void urlEscapedEntityCallback(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - if (reason == UCNV_UNASSIGNED) { - *err = U_ZERO_ERROR; - - UnencodableReplacementArray entity; - int entityLen = TextCodec::getUnencodableReplacement(codePoint, URLEncodedEntitiesForUnencodables, entity); - ucnv_cbFromUWriteBytes(fromUArgs, entity, entityLen, 0, err); - } else - UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, reason, err); -} - -// Substitutes special GBK characters, escaping all other unassigned entities. -static void gbkCallbackEscape(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - UChar outChar; - if (reason == UCNV_UNASSIGNED && (outChar = getGbkEscape(codePoint))) { - const UChar* source = &outChar; - *err = U_ZERO_ERROR; - ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); - return; - } - UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, reason, err); -} - -// Combines both gbkUrlEscapedEntityCallback and GBK character substitution. -static void gbkUrlEscapedEntityCallack(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - if (reason == UCNV_UNASSIGNED) { - if (UChar outChar = getGbkEscape(codePoint)) { - const UChar* source = &outChar; - *err = U_ZERO_ERROR; - ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); - return; - } - urlEscapedEntityCallback(context, fromUArgs, codeUnits, length, codePoint, reason, err); - return; - } - UCNV_FROM_U_CALLBACK_ESCAPE(context, fromUArgs, codeUnits, length, codePoint, reason, err); -} - -static void gbkCallbackSubstitute(const void* context, UConverterFromUnicodeArgs* fromUArgs, const UChar* codeUnits, int32_t length, - UChar32 codePoint, UConverterCallbackReason reason, UErrorCode* err) -{ - UChar outChar; - if (reason == UCNV_UNASSIGNED && (outChar = getGbkEscape(codePoint))) { - const UChar* source = &outChar; - *err = U_ZERO_ERROR; - ucnv_cbFromUWriteUChars(fromUArgs, &source, source + 1, 0, err); - return; - } - UCNV_FROM_U_CALLBACK_SUBSTITUTE(context, fromUArgs, codeUnits, length, codePoint, reason, err); -} - -CString TextCodecICU::encode(const UChar* characters, size_t length, UnencodableHandling handling) -{ - if (!length) - return ""; - - if (!m_converterICU) - createICUConverter(); - if (!m_converterICU) - return CString(); - - // FIXME: We should see if there is "force ASCII range" mode in ICU; - // until then, we change the backslash into a yen sign. - // Encoding will change the yen sign back into a backslash. - String copy(characters, length); - copy.replace('\\', m_encoding.backslashAsCurrencySymbol()); - - const UChar* source = copy.characters(); - const UChar* sourceLimit = source + copy.length(); - - UErrorCode err = U_ZERO_ERROR; - - switch (handling) { - case QuestionMarksForUnencodables: - ucnv_setSubstChars(m_converterICU, "?", 1, &err); - ucnv_setFromUCallBack(m_converterICU, m_needsGBKFallbacks ? gbkCallbackSubstitute : UCNV_FROM_U_CALLBACK_SUBSTITUTE, 0, 0, 0, &err); - break; - case EntitiesForUnencodables: - ucnv_setFromUCallBack(m_converterICU, m_needsGBKFallbacks ? gbkCallbackEscape : UCNV_FROM_U_CALLBACK_ESCAPE, UCNV_ESCAPE_XML_DEC, 0, 0, &err); - break; - case URLEncodedEntitiesForUnencodables: - ucnv_setFromUCallBack(m_converterICU, m_needsGBKFallbacks ? gbkUrlEscapedEntityCallack : urlEscapedEntityCallback, 0, 0, 0, &err); - break; - } - - ASSERT(U_SUCCESS(err)); - if (U_FAILURE(err)) - return CString(); - - Vector<char> result; - size_t size = 0; - do { - char buffer[ConversionBufferSize]; - char* target = buffer; - char* targetLimit = target + ConversionBufferSize; - err = U_ZERO_ERROR; - ucnv_fromUnicode(m_converterICU, &target, targetLimit, &source, sourceLimit, 0, true, &err); - size_t count = target - buffer; - result.grow(size + count); - memcpy(result.data() + size, buffer, count); - size += count; - } while (err == U_BUFFER_OVERFLOW_ERROR); - - return CString(result.data(), size); -} - - -} // namespace WebCore diff --git a/webkit/pending/TextResourceDecoder.cpp b/webkit/pending/TextResourceDecoder.cpp deleted file mode 100644 index fc4e10e1..0000000 --- a/webkit/pending/TextResourceDecoder.cpp +++ /dev/null @@ -1,894 +0,0 @@ -/* - Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. - Copyright (C) 2005, 2006, 2007 Alexey Proskuryakov (ap@nypop.com) - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. -*/ - - -#include "config.h" -#include "TextResourceDecoder.h" - -#include "DOMImplementation.h" -#include "HTMLNames.h" -#include "TextCodec.h" -#include <wtf/ASCIICType.h> - -#include "unicode/ucnv.h" -#include "unicode/ucsdet.h" - -using namespace WTF; - -namespace WebCore { - -using namespace HTMLNames; - -// You might think we should put these find functions elsewhere, perhaps with the -// similar functions that operate on UChar, but arguably only the decoder has -// a reason to process strings of char rather than UChar. - -static int find(const char* subject, size_t subjectLength, const char* target) -{ - size_t targetLength = strlen(target); - if (targetLength > subjectLength) - return -1; - for (size_t i = 0; i <= subjectLength - targetLength; ++i) { - bool match = true; - for (size_t j = 0; j < targetLength; ++j) { - if (subject[i + j] != target[j]) { - match = false; - break; - } - } - if (match) - return i; - } - return -1; -} - -static int findIgnoringCase(const char* subject, size_t subjectLength, const char* target) -{ - size_t targetLength = strlen(target); - if (targetLength > subjectLength) - return -1; -#ifndef NDEBUG - for (size_t i = 0; i < targetLength; ++i) - ASSERT(isASCIILower(target[i])); -#endif - for (size_t i = 0; i <= subjectLength - targetLength; ++i) { - bool match = true; - for (size_t j = 0; j < targetLength; ++j) { - if (toASCIILower(subject[i + j]) != target[j]) { - match = false; - break; - } - } - if (match) - return i; - } - return -1; -} - -static TextEncoding findTextEncoding(const char* encodingName, int length) -{ - Vector<char, 64> buffer(length + 1); - memcpy(buffer.data(), encodingName, length); - buffer[length] = '\0'; - return buffer.data(); -} - -class KanjiCode { -public: - enum Type { ASCII, JIS, EUC, SJIS, UTF16, UTF8 }; - static enum Type judge(const char* str, int length); - static const int ESC = 0x1b; - static const unsigned char sjisMap[256]; - static int ISkanji(int code) - { - if (code >= 0x100) - return 0; - return sjisMap[code & 0xff] & 1; - } - static int ISkana(int code) - { - if (code >= 0x100) - return 0; - return sjisMap[code & 0xff] & 2; - } -}; - -const unsigned char KanjiCode::sjisMap[256] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0 -}; - -/* - * EUC-JP is - * [0xa1 - 0xfe][0xa1 - 0xfe] - * 0x8e[0xa1 - 0xfe](SS2) - * 0x8f[0xa1 - 0xfe][0xa1 - 0xfe](SS3) - * - * Shift_Jis is - * [0x81 - 0x9f, 0xe0 - 0xef(0xfe?)][0x40 - 0x7e, 0x80 - 0xfc] - * - * Shift_Jis Hankaku Kana is - * [0xa1 - 0xdf] - */ - -/* - * KanjiCode::judge() is based on judge_jcode() from jvim - * http://hp.vector.co.jp/authors/VA003457/vim/ - * - * Special Thanks to Kenichi Tsuchida - */ - -enum KanjiCode::Type KanjiCode::judge(const char* str, int size) -{ - enum Type code; - int i; - int bfr = false; /* Kana Moji */ - int bfk = 0; /* EUC Kana */ - int sjis = 0; - int euc = 0; - - const unsigned char* ptr = reinterpret_cast<const unsigned char*>(str); - - code = ASCII; - - i = 0; - while (i < size) { - if (ptr[i] == ESC && (size - i >= 3)) { - if ((ptr[i + 1] == '$' && ptr[i + 2] == 'B') - || (ptr[i + 1] == '(' && ptr[i + 2] == 'B')) { - code = JIS; - goto breakBreak; - } else if ((ptr[i + 1] == '$' && ptr[i + 2] == '@') - || (ptr[i + 1] == '(' && ptr[i + 2] == 'J')) { - code = JIS; - goto breakBreak; - } else if (ptr[i + 1] == '(' && ptr[i + 2] == 'I') { - code = JIS; - i += 3; - } else if (ptr[i + 1] == ')' && ptr[i + 2] == 'I') { - code = JIS; - i += 3; - } else { - i++; - } - bfr = false; - bfk = 0; - } else { - if (ptr[i] < 0x20) { - bfr = false; - bfk = 0; - /* ?? check kudokuten ?? && ?? hiragana ?? */ - if ((i >= 2) && (ptr[i - 2] == 0x81) - && (0x41 <= ptr[i - 1] && ptr[i - 1] <= 0x49)) { - code = SJIS; - sjis += 100; /* kudokuten */ - } else if ((i >= 2) && (ptr[i - 2] == 0xa1) - && (0xa2 <= ptr[i - 1] && ptr[i - 1] <= 0xaa)) { - code = EUC; - euc += 100; /* kudokuten */ - } else if ((i >= 2) && (ptr[i - 2] == 0x82) && (0xa0 <= ptr[i - 1])) { - sjis += 40; /* hiragana */ - } else if ((i >= 2) && (ptr[i - 2] == 0xa4) && (0xa0 <= ptr[i - 1])) { - euc += 40; /* hiragana */ - } - } else { - /* ?? check hiragana or katana ?? */ - if ((size - i > 1) && (ptr[i] == 0x82) && (0xa0 <= ptr[i + 1])) { - sjis++; /* hiragana */ - } else if ((size - i > 1) && (ptr[i] == 0x83) - && (0x40 <= ptr[i + 1] && ptr[i + 1] <= 0x9f)) { - sjis++; /* katakana */ - } else if ((size - i > 1) && (ptr[i] == 0xa4) && (0xa0 <= ptr[i + 1])) { - euc++; /* hiragana */ - } else if ((size - i > 1) && (ptr[i] == 0xa5) && (0xa0 <= ptr[i + 1])) { - euc++; /* katakana */ - } - if (bfr) { - if ((i >= 1) && (0x40 <= ptr[i] && ptr[i] <= 0xa0) && ISkanji(ptr[i - 1])) { - code = SJIS; - goto breakBreak; - } else if ((i >= 1) && (0x81 <= ptr[i - 1] && ptr[i - 1] <= 0x9f) && ((0x40 <= ptr[i] && ptr[i] < 0x7e) || (0x7e < ptr[i] && ptr[i] <= 0xfc))) { - code = SJIS; - goto breakBreak; - } else if ((i >= 1) && (0xfd <= ptr[i] && ptr[i] <= 0xfe) && (0xa1 <= ptr[i - 1] && ptr[i - 1] <= 0xfe)) { - code = EUC; - goto breakBreak; - } else if ((i >= 1) && (0xfd <= ptr[i - 1] && ptr[i - 1] <= 0xfe) && (0xa1 <= ptr[i] && ptr[i] <= 0xfe)) { - code = EUC; - goto breakBreak; - } else if ((i >= 1) && (ptr[i] < 0xa0 || 0xdf < ptr[i]) && (0x8e == ptr[i - 1])) { - code = SJIS; - goto breakBreak; - } else if (ptr[i] <= 0x7f) { - code = SJIS; - goto breakBreak; - } else { - if (0xa1 <= ptr[i] && ptr[i] <= 0xa6) { - euc++; /* sjis hankaku kana kigo */ - } else if (0xa1 <= ptr[i] && ptr[i] <= 0xdf) { - ; /* sjis hankaku kana */ - } else if (0xa1 <= ptr[i] && ptr[i] <= 0xfe) { - euc++; - } else if (0x8e == ptr[i]) { - euc++; - } else if (0x20 <= ptr[i] && ptr[i] <= 0x7f) { - sjis++; - } - bfr = false; - bfk = 0; - } - } else if (0x8e == ptr[i]) { - if (size - i <= 1) { - ; - } else if (0xa1 <= ptr[i + 1] && ptr[i + 1] <= 0xdf) { - /* EUC KANA or SJIS KANJI */ - if (bfk == 1) { - euc += 100; - } - bfk++; - i++; - } else { - /* SJIS only */ - code = SJIS; - goto breakBreak; - } - } else if (0x81 <= ptr[i] && ptr[i] <= 0x9f) { - /* SJIS only */ - code = SJIS; - if ((size - i >= 1) - && ((0x40 <= ptr[i + 1] && ptr[i + 1] <= 0x7e) - || (0x80 <= ptr[i + 1] && ptr[i + 1] <= 0xfc))) { - goto breakBreak; - } - } else if (0xfd <= ptr[i] && ptr[i] <= 0xfe) { - /* EUC only */ - code = EUC; - if ((size - i >= 1) - && (0xa1 <= ptr[i + 1] && ptr[i + 1] <= 0xfe)) { - goto breakBreak; - } - } else if (ptr[i] <= 0x7f) { - ; - } else { - bfr = true; - bfk = 0; - } - } - i++; - } - } - if (code == ASCII) { - if (sjis > euc) { - code = SJIS; - } else if (sjis < euc) { - code = EUC; - } - } -breakBreak: - return (code); -} - -TextResourceDecoder::ContentType TextResourceDecoder::determineContentType(const String& mimeType) -{ - if (equalIgnoringCase(mimeType, "text/css")) - return CSS; - if (equalIgnoringCase(mimeType, "text/html")) - return HTML; - if (DOMImplementation::isXMLMIMEType(mimeType)) - return XML; - return PlainText; -} - -const TextEncoding& TextResourceDecoder::defaultEncoding(ContentType contentType, const TextEncoding& specifiedDefaultEncoding) -{ - // Despite 8.5 "Text/xml with Omitted Charset" of RFC 3023, we assume UTF-8 instead of US-ASCII - // for text/xml. This matches Firefox. - if (contentType == XML) - return UTF8Encoding(); - if (!specifiedDefaultEncoding.isValid()) - return Latin1Encoding(); - return specifiedDefaultEncoding; -} - -TextResourceDecoder::TextResourceDecoder(const String& mimeType, const TextEncoding& specifiedDefaultEncoding, bool usesEncodingDetector, const TextResourceDecoder* hintDecoder) - : m_contentType(determineContentType(mimeType)) - , m_decoder(defaultEncoding(m_contentType, specifiedDefaultEncoding)) - , m_hintDecoder(hintDecoder) - , m_source(DefaultEncoding) - , m_checkedForBOM(false) - , m_checkedForCSSCharset(false) - , m_checkedForHeadCharset(false) - , m_sawError(false) - , m_usesEncodingDetector(usesEncodingDetector) -{ -} - -TextResourceDecoder::~TextResourceDecoder() -{ -} - -void TextResourceDecoder::setEncoding(const TextEncoding& encoding, EncodingSource source) -{ - // In case the encoding didn't exist, we keep the old one (helps some sites specifying invalid encodings). - if (!encoding.isValid()) - return; - - if (source == EncodingFromMetaTag || source == EncodingFromXMLHeader || source == EncodingFromCSSCharset) - m_decoder.reset(encoding.closest8BitEquivalent()); - else - m_decoder.reset(encoding); - - m_source = source; -} - -// Returns the position of the encoding string. -static int findXMLEncoding(const char* str, int len, int& encodingLength) -{ - int pos = find(str, len, "encoding"); - if (pos == -1) - return -1; - pos += 8; - - // Skip spaces and stray control characters. - while (pos < len && str[pos] <= ' ') - ++pos; - - // Skip equals sign. - if (pos >= len || str[pos] != '=') - return -1; - ++pos; - - // Skip spaces and stray control characters. - while (pos < len && str[pos] <= ' ') - ++pos; - - // Skip quotation mark. - if (pos >= len) - return - 1; - char quoteMark = str[pos]; - if (quoteMark != '"' && quoteMark != '\'') - return -1; - ++pos; - - // Find the trailing quotation mark. - int end = pos; - while (end < len && str[end] != quoteMark) - ++end; - - if (end >= len) - return -1; - encodingLength = end - pos; - return pos; -} - -// true if there is more to parse -static inline bool skipWhitespace(const char*& pos, const char* dataEnd) -{ - while (pos < dataEnd && (*pos == '\t' || *pos == ' ')) - ++pos; - return pos != dataEnd; -} - -void TextResourceDecoder::checkForBOM(const char* data, size_t len) -{ - // Check for UTF-16/32 or UTF-8 BOM mark at the beginning, which is a sure sign of a Unicode encoding. - - if (m_source == UserChosenEncoding) { - // FIXME: Maybe a BOM should override even a user-chosen encoding. - m_checkedForBOM = true; - return; - } - - // Check if we have enough data. - size_t bufferLength = m_buffer.size(); - if (bufferLength + len < 4) - return; - - m_checkedForBOM = true; - - // Extract the first four bytes. - // Handle the case where some of bytes are already in the buffer. - // The last byte is always guaranteed to not be in the buffer. - const unsigned char* udata = reinterpret_cast<const unsigned char*>(data); - unsigned char c1 = bufferLength >= 1 ? m_buffer[0] : *udata++; - unsigned char c2 = bufferLength >= 2 ? m_buffer[1] : *udata++; - unsigned char c3 = bufferLength >= 3 ? m_buffer[2] : *udata++; - ASSERT(bufferLength < 4); - unsigned char c4 = *udata; - - // Check for the BOM. - if (c1 == 0xFF && c2 == 0xFE) { - if (c3 !=0 || c4 != 0) - setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding); - else - setEncoding(UTF32LittleEndianEncoding(), AutoDetectedEncoding); - } - else if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) - setEncoding(UTF8Encoding(), AutoDetectedEncoding); - else if (c1 == 0xFE && c2 == 0xFF) - setEncoding(UTF16BigEndianEncoding(), AutoDetectedEncoding); - else if (c1 == 0 && c2 == 0 && c3 == 0xFE && c4 == 0xFF) - setEncoding(UTF32BigEndianEncoding(), AutoDetectedEncoding); -} - -bool TextResourceDecoder::checkForCSSCharset(const char* data, size_t len, bool& movedDataToBuffer) -{ - if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) { - m_checkedForCSSCharset = true; - return true; - } - - size_t oldSize = m_buffer.size(); - m_buffer.grow(oldSize + len); - memcpy(m_buffer.data() + oldSize, data, len); - - movedDataToBuffer = true; - - if (m_buffer.size() > 8) { // strlen("@charset") == 8 - const char* dataStart = m_buffer.data(); - const char* dataEnd = dataStart + m_buffer.size(); - - if (dataStart[0] == '@' && dataStart[1] == 'c' && dataStart[2] == 'h' && dataStart[3] == 'a' && dataStart[4] == 'r' && - dataStart[5] == 's' && dataStart[6] == 'e' && dataStart[7] == 't') { - - dataStart += 8; - const char* pos = dataStart; - if (!skipWhitespace(pos, dataEnd)) - return false; - - if (*pos == '"' || *pos == '\'') { - char quotationMark = *pos; - ++pos; - dataStart = pos; - - while (pos < dataEnd && *pos != quotationMark) - ++pos; - if (pos == dataEnd) - return false; - - int encodingNameLength = pos - dataStart + 1; - - ++pos; - if (!skipWhitespace(pos, dataEnd)) - return false; - - if (*pos == ';') - setEncoding(findTextEncoding(dataStart, encodingNameLength), EncodingFromCSSCharset); - } - } - m_checkedForCSSCharset = true; - return true; - } - return false; -} - -// Other browsers allow comments in the head section, so we need to also. -// It's important not to look for tags inside the comments. -static inline void skipComment(const char*& ptr, const char* pEnd) -{ - const char* p = ptr; - // Allow <!-->; other browsers do. - if (*p == '>') { - p++; - } else { - while (p != pEnd) { - if (*p == '-') { - // This is the real end of comment, "-->". - if (p[1] == '-' && p[2] == '>') { - p += 3; - break; - } - // This is the incorrect end of comment that other browsers allow, "--!>". - if (p[1] == '-' && p[2] == '!' && p[3] == '>') { - p += 4; - break; - } - } - p++; - } - } - ptr = p; -} - -const int bytesToCheckUnconditionally = 1024; // That many input bytes will be checked for meta charset even if <head> section is over. - -bool TextResourceDecoder::checkForHeadCharset(const char* data, size_t len, bool& movedDataToBuffer) -{ - if (m_source != DefaultEncoding && m_source != EncodingFromParentFrame) { - m_checkedForHeadCharset = true; - return true; - } - - // This is not completely efficient, since the function might go - // through the HTML head several times. - - size_t oldSize = m_buffer.size(); - m_buffer.grow(oldSize + len); - memcpy(m_buffer.data() + oldSize, data, len); - - movedDataToBuffer = true; - - const char* ptr = m_buffer.data(); - const char* pEnd = ptr + m_buffer.size(); - - // Is there enough data available to check for XML declaration? - if (m_buffer.size() < 8) - return false; - - // Handle XML declaration, which can have encoding in it. This encoding is honored even for HTML documents. - // It is an error for an XML declaration not to be at the start of an XML document, and it is ignored in HTML documents in such case. - if (ptr[0] == '<' && ptr[1] == '?' && ptr[2] == 'x' && ptr[3] == 'm' && ptr[4] == 'l') { - const char* xmlDeclarationEnd = ptr; - while (xmlDeclarationEnd != pEnd && *xmlDeclarationEnd != '>') - ++xmlDeclarationEnd; - if (xmlDeclarationEnd == pEnd) - return false; - // No need for +1, because we have an extra "?" to lose at the end of XML declaration. - int len; - int pos = findXMLEncoding(ptr, xmlDeclarationEnd - ptr, len); - if (pos != -1) - setEncoding(findTextEncoding(ptr + pos, len), EncodingFromXMLHeader); - // continue looking for a charset - it may be specified in an HTTP-Equiv meta - } else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == '?' && ptr[3] == 0 && ptr[4] == 'x' && ptr[5] == 0) { - setEncoding(UTF16LittleEndianEncoding(), AutoDetectedEncoding); - return true; - } else if (ptr[0] == 0 && ptr[1] == '<' && ptr[2] == 0 && ptr[3] == '?' && ptr[4] == 0 && ptr[5] == 'x') { - setEncoding(UTF16BigEndianEncoding(), AutoDetectedEncoding); - return true; - } else if (ptr[0] == '<' && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == 0 && ptr[4] == '?' && ptr[5] == 0 && ptr[6] == 0 && ptr[7] == 0) { - setEncoding(UTF32LittleEndianEncoding(), AutoDetectedEncoding); - return true; - } else if (ptr[0] == 0 && ptr[1] == 0 && ptr[2] == 0 && ptr[3] == '<' && ptr[4] == 0 && ptr[5] == 0 && ptr[6] == 0 && ptr[7] == '?') { - setEncoding(UTF32BigEndianEncoding(), AutoDetectedEncoding); - return true; - } - - // we still don't have an encoding, and are in the head - // the following tags are allowed in <head>: - // SCRIPT|STYLE|META|LINK|OBJECT|TITLE|BASE - - // We stop scanning when a tag that is not permitted in <head> - // is seen, rather when </head> is seen, because that more closely - // matches behavior in other browsers; more details in - // <http://bugs.webkit.org/show_bug.cgi?id=3590>. - - // Additionally, we ignore things that looks like tags in <title>, <script> and <noscript>; see - // <http://bugs.webkit.org/show_bug.cgi?id=4560>, <http://bugs.webkit.org/show_bug.cgi?id=12165> - // and <http://bugs.webkit.org/show_bug.cgi?id=12389>. - - // Since many sites have charset declarations after <body> or other tags that are disallowed in <head>, - // we don't bail out until we've checked at least bytesToCheckUnconditionally bytes of input. - // we don't bail out until we've checked at least 2048 bytes of input. - // TODO(jungshik): Webkit upstream uses 512 bytes, but that seems to be - // a way too short. IE and Firefox go well beyond that. There might be - // a performance issue if we make it too large. Ideally, we should run - // mapreduce to come up with a number to cover 9x% of cases that still - // does not hurt the performance. - - AtomicStringImpl* enclosingTagName = 0; - bool inHeadSection = true; // Becomes false when </head> or any tag not allowed in head is encountered. - - // the HTTP-EQUIV meta has no effect on XHTML - if (m_contentType == XML) - return true; - - while (ptr + 3 < pEnd) { // +3 guarantees that "<!--" fits in the buffer - and certainly we aren't going to lose any "charset" that way. - if (*ptr == '<') { - bool end = false; - ptr++; - - // Handle comments. - if (ptr[0] == '!' && ptr[1] == '-' && ptr[2] == '-') { - ptr += 3; - skipComment(ptr, pEnd); - if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) { - // Some pages that test bandwidth from within the browser do it by having - // huge comments and measuring the time they take to load. Repeatedly scanning - // these comments can take a lot of CPU time. - m_checkedForHeadCharset = true; - return true; - } - continue; - } - - if (*ptr == '/') { - ++ptr; - end = true; - } - - // Grab the tag name, but mostly ignore namespaces. - bool sawNamespace = false; - char tagBuffer[20]; - int len = 0; - while (len < 19) { - if (ptr == pEnd) - return false; - char c = *ptr; - if (c == ':') { - len = 0; - sawNamespace = true; - ptr++; - continue; - } - if (c >= 'a' && c <= 'z' || c >= '0' && c <= '9') - ; - else if (c >= 'A' && c <= 'Z') - c += 'a' - 'A'; - else - break; - tagBuffer[len++] = c; - ptr++; - } - tagBuffer[len] = 0; - AtomicString tag(tagBuffer); - - if (enclosingTagName) { - if (end && tag.impl() == enclosingTagName) - enclosingTagName = 0; - } else { - if (tag == titleTag) - enclosingTagName = titleTag.localName().impl(); - else if (tag == scriptTag) - enclosingTagName = scriptTag.localName().impl(); - else if (tag == noscriptTag) - enclosingTagName = noscriptTag.localName().impl(); - } - - // Find where the opening tag ends. - const char* tagContentStart = ptr; - if (!end) { - while (ptr != pEnd && *ptr != '>') { - if (*ptr == '\'' || *ptr == '"') { - char quoteMark = *ptr; - ++ptr; - while (ptr != pEnd && *ptr != quoteMark) - ++ptr; - if (ptr == pEnd) - return false; - } - ++ptr; - } - if (ptr == pEnd) - return false; - ++ptr; - } - - if (!end && tag == metaTag && !sawNamespace) { - const char* str = tagContentStart; - int length = ptr - tagContentStart; - int pos = 0; - while (pos < length) { - int charsetPos = findIgnoringCase(str + pos, length - pos, "charset"); - if (charsetPos == -1) - break; - pos += charsetPos + 7; - // skip whitespace - while (pos < length && str[pos] <= ' ') - pos++; - if (pos == length) - break; - if (str[pos++] != '=') - continue; - while (pos < length && - (str[pos] <= ' ') || str[pos] == '=' || str[pos] == '"' || str[pos] == '\'') - pos++; - - // end ? - if (pos == length) - break; - int end = pos; - while (end < length && - str[end] != ' ' && str[end] != '"' && str[end] != '\'' && - str[end] != ';' && str[end] != '>') - end++; - setEncoding(findTextEncoding(str + pos, end - pos), EncodingFromMetaTag); - if (m_source == EncodingFromMetaTag) - return true; - - if (end >= length || str[end] == '/' || str[end] == '>') - break; - - pos = end + 1; - } - } else { - if (!enclosingTagName && tag != scriptTag && tag != noscriptTag && tag != styleTag - && tag != linkTag && tag != metaTag && tag != objectTag && tag != titleTag && tag != baseTag - && (end || tag != htmlTag) && (end || tag != headTag) && isASCIIAlpha(tagBuffer[0])) { - inHeadSection = false; - } - - if (ptr - m_buffer.data() >= bytesToCheckUnconditionally && !inHeadSection) { - m_checkedForHeadCharset = true; - return true; - } - } - } else - ++ptr; - } - return false; -} - -void TextResourceDecoder::detectJapaneseEncoding(const char* data, size_t len) -{ - switch (KanjiCode::judge(data, len)) { - case KanjiCode::JIS: - setEncoding("ISO-2022-JP", AutoDetectedEncoding); - break; - case KanjiCode::EUC: - setEncoding("EUC-JP", AutoDetectedEncoding); - break; - case KanjiCode::SJIS: - setEncoding("Shift_JIS", AutoDetectedEncoding); - break; - case KanjiCode::ASCII: - case KanjiCode::UTF16: - case KanjiCode::UTF8: - break; - } -} - -void TextResourceDecoder::detectEncoding(const char* data, size_t len) -{ - // need to declare these two vars here to slience gcc's complaint about - // jumping across variable declarations. - const UCharsetMatch **matches; - const char* encoding = NULL; - int matches_count = 0; - const char* hint_encoding = m_hintDecoder ? m_hintDecoder->encoding().name() : NULL; - UErrorCode status = U_ZERO_ERROR; - UCharsetDetector* detector = ucsdet_open(&status); - if (U_FAILURE(status)) - return; - ucsdet_enableInputFilter(detector, true); - ucsdet_setText(detector, data, static_cast<int32_t>(len), &status); - if (U_FAILURE(status)) - goto fail; - // TODO(jungshik) : If we don't manage to open-source our internal - // detector, use ucsdet_detectAll and pick the most likely one - // given "the context" (parent-encoding, referrer encoding, etc). - // We can also 'emulate' Firefox/IE's non-Universal detectors (e.g. - // Chinese, Japanese, Russian, Korean and Hebrew) by picking the - // encoding with a highest confidence among the detetctor-specific - // limited set of candidate encodings. (bug 1202108) - // Below is the implementation of the first part of what's outlined - // above. - matches = ucsdet_detectAll(detector, &matches_count, &status); - if (U_FAILURE(status)) - goto fail; - if (hint_encoding) { - // 10 is the minimum confidence value consistent with the codepoint - // allocation in a given encoding. The size of a chunk passed to - // us varies even for the same html file (apparently depending on - // the network load). When we're given a rather short chunk, we - // don't have a sufficiently reliable signal other than the fact that - // the chunk is consistent with a set of encodings. So, instead of - // setting an arbitrary threshold, we have to scan all the encodings - // consistent with the data. - const int32_t kThresold = 10; - for (int i = 0; i < matches_count; ++i) { - int32_t confidence = ucsdet_getConfidence(matches[i], &status); - if (U_FAILURE(status)) { - status = U_ZERO_ERROR; - continue; - } - if (confidence < kThresold) - break; - const char* match_encoding = ucsdet_getName(matches[i], &status); - if (U_FAILURE(status)) { - status = U_ZERO_ERROR; - continue; - } - // TODO : Make this comparison even fuzzier to catch cases - // like ISO-8859-1 and Windows-1252 and ISO-8859-8-I and - // Windows-1255, ks_c_5601-1987 and EUC-KR. The last case - // can be worked around comparing canonical names. - if (!ucnv_compareNames(match_encoding, hint_encoding)) { - encoding = hint_encoding; - break; - } - } - } - // If no match is found so far, just pick the top match. - // This can happen when a parent frame in EUC-JP refers to - // a child frame in Shift_JIS although that's not very likely. - if (!encoding && matches_count > 0) - encoding = ucsdet_getName(matches[0], &status); - if (U_SUCCESS(status)) - setEncoding(encoding, AutoDetectedEncoding); -fail: - ucsdet_close(detector); -} - -String TextResourceDecoder::decode(const char* data, size_t len) -{ - if (!m_checkedForBOM) - checkForBOM(data, len); - - bool movedDataToBuffer = false; - - if (m_contentType == CSS && !m_checkedForCSSCharset) - if (!checkForCSSCharset(data, len, movedDataToBuffer)) - return ""; - - if ((m_contentType == HTML || m_contentType == XML) && !m_checkedForHeadCharset) // HTML and XML - if (!checkForHeadCharset(data, len, movedDataToBuffer)) - return ""; - - // Do the auto-detect if our default encoding is one of the Japanese ones. - // FIXME: It seems wrong to change our encoding downstream after we have already done some decoding. - // If parent frame does not have specified encoding information and does get its encoding from auto - // detection, then sub frames should also do encoding auto detection when there are no specified - // encoding in them. - if (m_source != UserChosenEncoding && m_source != AutoDetectedEncoding && encoding().isJapanese()) - detectJapaneseEncoding(data, len); - else if ((m_usesEncodingDetector && m_source == DefaultEncoding) || - (m_source == EncodingFromParentFrame && m_hintDecoder && - m_hintDecoder->source() == AutoDetectedEncoding)) - detectEncoding(data, len); - - ASSERT(encoding().isValid()); - - if (m_buffer.isEmpty()) - return m_decoder.decode(data, len, false, m_contentType == XML, m_sawError); - - if (!movedDataToBuffer) { - size_t oldSize = m_buffer.size(); - m_buffer.grow(oldSize + len); - memcpy(m_buffer.data() + oldSize, data, len); - } - - String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), false, m_contentType == XML, m_sawError); - m_buffer.clear(); - return result; -} - -String TextResourceDecoder::flush() -{ - // For HTML and XML document, if we can not find proper encoding even - // document is completely loaded, we need to use automatically detect - // the encoding of document if user has enabled this option. - if (m_buffer.size() && !m_checkedForHeadCharset && - (m_contentType == HTML || m_contentType == XML) && - ((m_usesEncodingDetector && m_source == DefaultEncoding) || - (m_source == EncodingFromParentFrame && m_hintDecoder && - m_hintDecoder->source() == AutoDetectedEncoding))) - detectEncoding(m_buffer.data(), m_buffer.size()); - - String result = m_decoder.decode(m_buffer.data(), m_buffer.size(), true, m_contentType == XML, m_sawError); - m_buffer.clear(); - return result; -} - -} diff --git a/webkit/pending/TextResourceDecoder.h b/webkit/pending/TextResourceDecoder.h deleted file mode 100644 index 476dd21..0000000 --- a/webkit/pending/TextResourceDecoder.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - Copyright (C) 1999 Lars Knoll (knoll@mpi-hd.mpg.de) - Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.com) - Copyright (C) 2006, 2008 Apple Inc. All rights reserved. - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public License - along with this library; see the file COPYING.LIB. If not, write to - the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. - -*/ - -#ifndef TextResourceDecoder_h -#define TextResourceDecoder_h - -#include "TextDecoder.h" - -namespace WebCore { - -class TextResourceDecoder : public RefCounted<TextResourceDecoder> { -public: - enum EncodingSource { - DefaultEncoding, - AutoDetectedEncoding, - EncodingFromXMLHeader, - EncodingFromMetaTag, - EncodingFromCSSCharset, - EncodingFromHTTPHeader, - UserChosenEncoding, - EncodingFromParentFrame - }; - - static PassRefPtr<TextResourceDecoder> create(const String& mimeType, const TextEncoding& defaultEncoding = TextEncoding(), bool usesEncodingDetector = false, const TextResourceDecoder* hintDecoder = NULL) - { - return adoptRef(new TextResourceDecoder(mimeType, defaultEncoding)); - } - ~TextResourceDecoder(); - - void setEncoding(const TextEncoding&, EncodingSource); - const TextEncoding& encoding() const { return m_decoder.encoding(); } - - String decode(const char* data, size_t length); - String flush(); - - bool sawError() const { return m_sawError; } - - EncodingSource source() const { return m_source; } - -private: - TextResourceDecoder(const String& mimeType, const TextEncoding& defaultEncoding, bool usesEncodingDetector = false, const TextResourceDecoder* hintDecoder = NULL); - - enum ContentType { PlainText, HTML, XML, CSS }; // PlainText is equivalent to directly using TextDecoder. - static ContentType determineContentType(const String& mimeType); - static const TextEncoding& defaultEncoding(ContentType, const TextEncoding& defaultEncoding); - - void checkForBOM(const char*, size_t); - bool checkForCSSCharset(const char*, size_t, bool& movedDataToBuffer); - bool checkForHeadCharset(const char*, size_t, bool& movedDataToBuffer); - void detectJapaneseEncoding(const char*, size_t); - void detectEncoding(const char*, size_t); - - ContentType m_contentType; - TextDecoder m_decoder; - const TextResourceDecoder* m_hintDecoder; - EncodingSource m_source; - Vector<char> m_buffer; - bool m_checkedForBOM; - bool m_checkedForCSSCharset; - bool m_checkedForHeadCharset; - bool m_sawError; - bool m_usesEncodingDetector; -}; - -} - -#endif diff --git a/webkit/pending/TreeWalker.cpp b/webkit/pending/TreeWalker.cpp deleted file mode 100644 index 32e4a37..0000000 --- a/webkit/pending/TreeWalker.cpp +++ /dev/null @@ -1,319 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#include "config.h" -#include "TreeWalker.h" - -#include "ExceptionCode.h" -#include "ExceptionContext.h" -#include "Node.h" -#include "NodeFilter.h" -#include <wtf/PassRefPtr.h> - -namespace WebCore { - -TreeWalker::TreeWalker(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) - : Traversal(rootNode, whatToShow, filter, expandEntityReferences) - , m_current(root()) -{ -} - -void TreeWalker::setCurrentNode(PassRefPtr<Node> node, ExceptionCode& ec) -{ - if (!node) { - ec = NOT_SUPPORTED_ERR; - return; - } - m_current = node; -} - -inline Node* TreeWalker::setCurrent(PassRefPtr<Node> node) -{ - m_current = node; - return m_current.get(); -} - -Node* TreeWalker::parentNode(ExceptionContext* exec) -{ - RefPtr<Node> node = m_current; - while (node != root()) { - node = node->parentNode(); - if (!node) - return 0; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); - } - return 0; -} - -Node* TreeWalker::firstChild(ExceptionContext* exec) -{ - for (RefPtr<Node> node = m_current->firstChild(); node; ) { - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - switch (acceptNodeResult) { - case NodeFilter::FILTER_ACCEPT: - m_current = node.release(); - return m_current.get(); - case NodeFilter::FILTER_SKIP: - if (node->firstChild()) { - node = node->firstChild(); - continue; - } - break; - case NodeFilter::FILTER_REJECT: - break; - } - do { - if (node->nextSibling()) { - node = node->nextSibling(); - break; - } - Node* parent = node->parentNode(); - if (!parent || parent == root() || parent == m_current) - return 0; - node = parent; - } while (node); - } - return 0; -} - -Node* TreeWalker::lastChild(ExceptionContext* exec) -{ - for (RefPtr<Node> node = m_current->lastChild(); node; ) { - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - switch (acceptNodeResult) { - case NodeFilter::FILTER_ACCEPT: - m_current = node.release(); - return m_current.get(); - case NodeFilter::FILTER_SKIP: - if (node->lastChild()) { - node = node->lastChild(); - continue; - } - break; - case NodeFilter::FILTER_REJECT: - break; - } - do { - if (node->previousSibling()) { - node = node->previousSibling(); - break; - } - Node* parent = node->parentNode(); - if (!parent || parent == root() || parent == m_current) - return 0; - node = parent; - } while (node); - } - return 0; -} - -Node* TreeWalker::previousSibling(ExceptionContext* exec) -{ - RefPtr<Node> node = m_current; - if (node == root()) - return 0; - while (1) { - for (RefPtr<Node> sibling = node->previousSibling(); sibling; ) { - short acceptNodeResult = acceptNode(exec, sibling.get()); - if (exec && exec->hadException()) - return 0; - switch (acceptNodeResult) { - case NodeFilter::FILTER_ACCEPT: - m_current = sibling.release(); - return m_current.get(); - case NodeFilter::FILTER_SKIP: - if (sibling->firstChild()) { - sibling = sibling->firstChild(); - continue; - } - break; - case NodeFilter::FILTER_REJECT: - break; - } - sibling = sibling->previousSibling(); - } - node = node->parentNode(); - if (!node || node == root()) - return 0; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return 0; - } -} - -Node* TreeWalker::nextSibling(ExceptionContext* exec) -{ - RefPtr<Node> node = m_current; - if (node == root()) - return 0; - while (1) { - for (RefPtr<Node> sibling = node->nextSibling(); sibling; ) { - short acceptNodeResult = acceptNode(exec, sibling.get()); - if (exec && exec->hadException()) - return 0; - switch (acceptNodeResult) { - case NodeFilter::FILTER_ACCEPT: - m_current = sibling.release(); - return m_current.get(); - case NodeFilter::FILTER_SKIP: - if (sibling->firstChild()) { - sibling = sibling->firstChild(); - continue; - } - break; - case NodeFilter::FILTER_REJECT: - break; - } - sibling = sibling->nextSibling(); - } - node = node->parentNode(); - if (!node || node == root()) - return 0; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return 0; - } -} - -Node* TreeWalker::previousNode(ExceptionContext* exec) -{ - RefPtr<Node> node = m_current; - while (node != root()) { - while (Node* previousSibling = node->previousSibling()) { - node = previousSibling; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_REJECT) - continue; - while (Node* lastChild = node->lastChild()) { - node = lastChild; - acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - continue; - } - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) { - m_current = node.release(); - return m_current.get(); - } - } - if (node == root()) - return 0; - Node* parent = node->parentNode(); - if (!parent) - return 0; - node = parent; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); - } - return 0; -} - -Node* TreeWalker::nextNode(ExceptionContext* exec) -{ - RefPtr<Node> node = m_current; -Children: - while (Node* firstChild = node->firstChild()) { - node = firstChild; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); - if (acceptNodeResult == NodeFilter::FILTER_REJECT) - break; - } - while (Node* nextSibling = node->traverseNextSibling(root())) { - node = nextSibling; - short acceptNodeResult = acceptNode(exec, node.get()); - if (exec && exec->hadException()) - return 0; - if (acceptNodeResult == NodeFilter::FILTER_ACCEPT) - return setCurrent(node.release()); - if (acceptNodeResult == NodeFilter::FILTER_SKIP) - goto Children; - } - return 0; -} - -Node* TreeWalker::parentNode() -{ - ExceptionContext context(m_current.get()); - return parentNode(&context); -} - -Node* TreeWalker::firstChild() -{ - ExceptionContext context(m_current.get()); - return firstChild(&context); -} - -Node* TreeWalker::lastChild() -{ - ExceptionContext context(m_current.get()); - return lastChild(&context); -} - -Node* TreeWalker::previousSibling() -{ - ExceptionContext context(m_current.get()); - return previousSibling(&context); -} - -Node* TreeWalker::nextSibling() -{ - ExceptionContext context(m_current.get()); - return nextSibling(&context); -} - -Node* TreeWalker::previousNode() -{ - ExceptionContext context(m_current.get()); - return previousNode(&context); -} - -Node* TreeWalker::nextNode() -{ - ExceptionContext context(m_current.get()); - return nextNode(&context); -} - -} // namespace WebCore diff --git a/webkit/pending/TreeWalker.h b/webkit/pending/TreeWalker.h deleted file mode 100644 index 5cc5f25..0000000 --- a/webkit/pending/TreeWalker.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright (C) 1999 Lars Knoll (knoll@kde.org) - * Copyright (C) 2000 Frederik Holljen (frederik.holljen@hig.no) - * Copyright (C) 2001 Peter Kelly (pmk@post.com) - * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com) - * Copyright (C) 2004, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef TreeWalker_h -#define TreeWalker_h - -#include "ExceptionContext.h" -#include "NodeFilter.h" -#include "Traversal.h" -#include <wtf/PassRefPtr.h> -#include <wtf/RefCounted.h> - -namespace WebCore { - - typedef int ExceptionCode; - - class TreeWalker : public RefCounted<TreeWalker>, public Traversal { - public: - static PassRefPtr<TreeWalker> create(PassRefPtr<Node> rootNode, unsigned whatToShow, PassRefPtr<NodeFilter> filter, bool expandEntityReferences) - { - return adoptRef(new TreeWalker(rootNode, whatToShow, filter, expandEntityReferences)); - } - - Node* currentNode() const { return m_current.get(); } - void setCurrentNode(PassRefPtr<Node>, ExceptionCode&); - - Node* parentNode(ExceptionContext*); - Node* firstChild(ExceptionContext*); - Node* lastChild(ExceptionContext*); - Node* previousSibling(ExceptionContext*); - Node* nextSibling(ExceptionContext*); - Node* previousNode(ExceptionContext*); - Node* nextNode(ExceptionContext*); - - // For non-JS bindings. Silently ignores the JavaScript exception if any. - Node* parentNode(); - Node* firstChild(); - Node* lastChild(); - Node* previousSibling(); - Node* nextSibling(); - Node* previousNode(); - Node* nextNode(); - - private: - TreeWalker(PassRefPtr<Node>, unsigned whatToShow, PassRefPtr<NodeFilter>, bool expandEntityReferences); - - Node* setCurrent(PassRefPtr<Node>); - - RefPtr<Node> m_current; - }; - -} // namespace WebCore - -#endif // TreeWalker_h diff --git a/webkit/pending/XMLHttpRequest.cpp b/webkit/pending/XMLHttpRequest.cpp deleted file mode 100644 index 7dc5c4c..0000000 --- a/webkit/pending/XMLHttpRequest.cpp +++ /dev/null @@ -1,1381 +0,0 @@ -/* - * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2005-2007 Alexey Proskuryakov <ap@webkit.org> - * Copyright (C) 2007, 2008 Julien Chaffraix <jchaffraix@webkit.org> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "config.h" -#include "XMLHttpRequest.h" - -#include "CString.h" -#include "Console.h" -#include "DOMImplementation.h" -#include "DOMWindow.h" -#include "Event.h" -#include "EventException.h" -#include "EventListener.h" -#include "EventNames.h" -#include "File.h" -#include "Frame.h" -#include "FrameLoader.h" -#include "HTTPParsers.h" -#include "InspectorController.h" -#include "KURL.h" -#include "KURLHash.h" -#include "Page.h" -#include "Settings.h" -#include "SubresourceLoader.h" -#include "SystemTime.h" -#include "TextResourceDecoder.h" -#include "XMLHttpRequestException.h" -#include "XMLHttpRequestProgressEvent.h" -#include "XMLHttpRequestUpload.h" -#include "markup.h" - -#if USE(JSC) -#include <kjs/protect.h> -#include "JSDOMBinding.h" -#endif - -#include "base/stats_counters.h" - -namespace WebCore { - -using namespace EventNames; - -struct PreflightResultCacheItem { - PreflightResultCacheItem(unsigned expiryDelta, bool credentials, HashSet<String>* methods, HashSet<String, CaseFoldingHash>* headers) - : m_absoluteExpiryTime(currentTime() + expiryDelta) - , m_credentials(credentials) - , m_methods(methods) - , m_headers(headers) - { - } - - // FIXME: A better solution to holding onto the absolute expiration time might be - // to start a timer for the expiration delta, that removes this from the cache when - // it fires. - double m_absoluteExpiryTime; - bool m_credentials; - OwnPtr<HashSet<String> > m_methods; - OwnPtr<HashSet<String, CaseFoldingHash> > m_headers; -}; - -typedef HashMap<std::pair<String, KURL>, PreflightResultCacheItem*> PreflightResultCache; - -static PreflightResultCache& preflightResultCache() -{ - static PreflightResultCache cache; - return cache; -} - -static void appendPreflightResultCacheEntry(String origin, KURL url, unsigned expiryDelta, - bool credentials, HashSet<String>* methods, HashSet<String, CaseFoldingHash>* headers) -{ - ASSERT(!preflightResultCache().contains(std::make_pair(origin, url))); - - PreflightResultCacheItem* item = new PreflightResultCacheItem(expiryDelta, credentials, methods, headers); - preflightResultCache().set(std::make_pair(origin, url), item); -} - -typedef HashSet<XMLHttpRequest*> RequestsSet; - -static HashMap<Document*, RequestsSet*>& requestsByDocument() -{ - static HashMap<Document*, RequestsSet*> map; - return map; -} - -static void addToRequestsByDocument(Document* doc, XMLHttpRequest* req) -{ - ASSERT(doc); - ASSERT(req); - - RequestsSet* requests = requestsByDocument().get(doc); - if (!requests) { - requests = new RequestsSet; - requestsByDocument().set(doc, requests); - } - - ASSERT(!requests->contains(req)); - requests->add(req); -} - -static void removeFromRequestsByDocument(Document* doc, XMLHttpRequest* req) -{ - ASSERT(doc); - ASSERT(req); - - RequestsSet* requests = requestsByDocument().get(doc); - ASSERT(requests); - ASSERT(requests->contains(req)); - requests->remove(req); - if (requests->isEmpty()) { - requestsByDocument().remove(doc); - delete requests; - } -} - -static bool isSafeRequestHeader(const String& name) -{ - static HashSet<String, CaseFoldingHash> forbiddenHeaders; - static String proxyString("proxy-"); - static String secString("sec-"); - - if (forbiddenHeaders.isEmpty()) { - forbiddenHeaders.add("accept-charset"); - forbiddenHeaders.add("accept-encoding"); - forbiddenHeaders.add("connection"); - forbiddenHeaders.add("content-length"); - forbiddenHeaders.add("content-transfer-encoding"); - forbiddenHeaders.add("date"); - forbiddenHeaders.add("expect"); - forbiddenHeaders.add("host"); - forbiddenHeaders.add("keep-alive"); - forbiddenHeaders.add("referer"); - forbiddenHeaders.add("te"); - forbiddenHeaders.add("trailer"); - forbiddenHeaders.add("transfer-encoding"); - forbiddenHeaders.add("upgrade"); - forbiddenHeaders.add("via"); - } - - return !forbiddenHeaders.contains(name) && !name.startsWith(proxyString, false) && - !name.startsWith(secString, false); -} - -static bool isOnAccessControlSimpleRequestHeaderWhitelist(const String& name) -{ - return equalIgnoringCase(name, "accept") || equalIgnoringCase(name, "accept-language") || equalIgnoringCase(name, "content-type"); -} - -static bool isOnAccessControlResponseHeaderWhitelist(const String& name) -{ - static HashSet<String, CaseFoldingHash> allowedHeaders; - if (allowedHeaders.isEmpty()) { - allowedHeaders.add("cache-control"); - allowedHeaders.add("content-language"); - allowedHeaders.add("content-type"); - allowedHeaders.add("expires"); - allowedHeaders.add("last-modified"); - allowedHeaders.add("pragma"); - } - - return allowedHeaders.contains(name); -} - -// Determines if a string is a valid token, as defined by -// "token" in section 2.2 of RFC 2616. -static bool isValidToken(const String& name) -{ - unsigned length = name.length(); - for (unsigned i = 0; i < length; i++) { - UChar c = name[i]; - - if (c >= 127 || c <= 32) - return false; - - if (c == '(' || c == ')' || c == '<' || c == '>' || c == '@' || - c == ',' || c == ';' || c == ':' || c == '\\' || c == '\"' || - c == '/' || c == '[' || c == ']' || c == '?' || c == '=' || - c == '{' || c == '}') - return false; - } - - return true; -} - -static bool isValidHeaderValue(const String& name) -{ - // FIXME: This should really match name against - // field-value in section 4.2 of RFC 2616. - - return !name.contains('\r') && !name.contains('\n'); -} - -XMLHttpRequest::XMLHttpRequest(Document* doc) - : m_doc(doc) - , m_async(true) - , m_includeCredentials(false) - , m_state(UNSENT) - , m_identifier(std::numeric_limits<unsigned long>::max()) - , m_responseText("") - , m_createdDocument(false) - , m_error(false) - , m_uploadComplete(false) - , m_sameOriginRequest(true) - , m_inPreflight(false) - , m_receivedLength(0) -{ - ASSERT(m_doc); - addToRequestsByDocument(m_doc, this); -} - -XMLHttpRequest::~XMLHttpRequest() -{ - if (m_doc) - removeFromRequestsByDocument(m_doc, this); - - if (m_upload) - m_upload->disconnectXMLHttpRequest(); -} - -XMLHttpRequest::State XMLHttpRequest::readyState() const -{ - return m_state; -} - -const JSString& XMLHttpRequest::responseText() const -{ - return m_responseText; -} - -Document* XMLHttpRequest::responseXML() const -{ - if (m_state != DONE) - return 0; - - if (!m_createdDocument) { - if (m_response.isHTTP() && !responseIsXML()) { - // The W3C spec requires this. - m_responseXML = 0; - } else { - m_responseXML = m_doc->implementation()->createDocument(0); - m_responseXML->open(); - m_responseXML->setURL(m_url); - // FIXME: set Last-Modified and cookies (currently, those are only available for HTMLDocuments). - m_responseXML->write(String(m_responseText)); - m_responseXML->finishParsing(); - m_responseXML->close(); - - if (!m_responseXML->wellFormed()) - m_responseXML = 0; - } - m_createdDocument = true; - } - - return m_responseXML.get(); -} - -XMLHttpRequestUpload* XMLHttpRequest::upload() -{ - if (!m_upload) - m_upload = XMLHttpRequestUpload::create(this); - return m_upload.get(); -} - -void XMLHttpRequest::addEventListener(const AtomicString& eventType, PassRefPtr<EventListener> eventListener, bool) -{ - EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); - if (iter == m_eventListeners.end()) { - ListenerVector listeners; - listeners.append(eventListener); - m_eventListeners.add(eventType.impl(), listeners); - } else { - ListenerVector& listeners = iter->second; - for (ListenerVector::iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) - if (*listenerIter == eventListener) - return; - - listeners.append(eventListener); - m_eventListeners.add(eventType.impl(), listeners); - } -} - -void XMLHttpRequest::removeEventListener(const AtomicString& eventType, EventListener* eventListener, bool) -{ - EventListenersMap::iterator iter = m_eventListeners.find(eventType.impl()); - if (iter == m_eventListeners.end()) - return; - - ListenerVector& listeners = iter->second; - for (ListenerVector::const_iterator listenerIter = listeners.begin(); listenerIter != listeners.end(); ++listenerIter) - if (*listenerIter == eventListener) { - listeners.remove(listenerIter - listeners.begin()); - return; - } -} - -bool XMLHttpRequest::dispatchEvent(PassRefPtr<Event> evt, ExceptionCode& ec, bool /*tempEvent*/) -{ - // FIXME: check for other error conditions enumerated in the spec. - if (evt->type().isEmpty()) { - ec = EventException::UNSPECIFIED_EVENT_TYPE_ERR; - return true; - } - - ListenerVector listenersCopy = m_eventListeners.get(evt->type().impl()); - for (ListenerVector::const_iterator listenerIter = listenersCopy.begin(); listenerIter != listenersCopy.end(); ++listenerIter) { - evt->setTarget(this); - evt->setCurrentTarget(this); - listenerIter->get()->handleEvent(evt.get(), false); - } - - return !evt->defaultPrevented(); -} - -void XMLHttpRequest::changeState(State newState) -{ - if (m_state != newState) { - m_state = newState; - callReadyStateChangeListener(); - } -} - -void XMLHttpRequest::callReadyStateChangeListener() -{ - if (!m_doc || !m_doc->frame()) - return; - - dispatchReadyStateChangeEvent(); - - if (m_state == DONE) - dispatchLoadEvent(); -} - -void XMLHttpRequest::open(const String& method, const KURL& url, bool async, ExceptionCode& ec) -{ - internalAbort(); - State previousState = m_state; - m_state = UNSENT; - m_error = false; - - m_uploadComplete = false; - - // clear stuff from possible previous load - clearResponse(); - clearRequest(); - - ASSERT(m_state == UNSENT); - - if (!isValidToken(method)) { - ec = SYNTAX_ERR; - return; - } - - // Method names are case sensitive. But since Firefox uppercases method names it knows, we'll do the same. - String methodUpper(method.upper()); - - if (methodUpper == "TRACE" || methodUpper == "TRACK" || methodUpper == "CONNECT") { - ec = SECURITY_ERR; - return; - } - - m_url = url; - - if (methodUpper == "COPY" || methodUpper == "DELETE" || methodUpper == "GET" || methodUpper == "HEAD" - || methodUpper == "INDEX" || methodUpper == "LOCK" || methodUpper == "M-POST" || methodUpper == "MKCOL" || methodUpper == "MOVE" - || methodUpper == "OPTIONS" || methodUpper == "POST" || methodUpper == "PROPFIND" || methodUpper == "PROPPATCH" || methodUpper == "PUT" - || methodUpper == "UNLOCK") - m_method = methodUpper; - else - m_method = method; - - m_async = async; - - ASSERT(!m_loader); - - // Check previous state to avoid dispatching readyState event - // when calling open several times in a row. - if (previousState != OPENED) - changeState(OPENED); - else - m_state = OPENED; -} - -void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, ExceptionCode& ec) -{ - KURL urlWithCredentials(url); - urlWithCredentials.setUser(user); - - open(method, urlWithCredentials, async, ec); -} - -void XMLHttpRequest::open(const String& method, const KURL& url, bool async, const String& user, const String& password, ExceptionCode& ec) -{ - KURL urlWithCredentials(url); - urlWithCredentials.setUser(user); - urlWithCredentials.setPass(password); - - open(method, urlWithCredentials, async, ec); -} - -bool XMLHttpRequest::initSend(ExceptionCode& ec) -{ - if (!m_doc) - return false; - - if (m_state != OPENED || m_loader) { - ec = INVALID_STATE_ERR; - return false; - } - - m_error = false; - return true; -} - -void XMLHttpRequest::send(ExceptionCode& ec) -{ - send(String(), ec); -} - -void XMLHttpRequest::send(Document* document, ExceptionCode& ec) -{ - ASSERT(document); - - if (!initSend(ec)) - return; - - if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { - String contentType = getRequestHeader("Content-Type"); - if (contentType.isEmpty()) { -#if ENABLE(DASHBOARD_SUPPORT) - Settings* settings = m_doc->settings(); - if (settings && settings->usesDashboardBackwardCompatibilityMode()) - setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); - else -#endif - // FIXME: this should include the charset used for encoding. - setRequestHeaderInternal("Content-Type", "application/xml"); - } - - // FIXME: According to XMLHttpRequest Level 2, this should use the Document.innerHTML algorithm - // from the HTML5 specification to serialize the document. - String body = createMarkup(document); - - // FIXME: this should use value of document.inputEncoding to determine the encoding to use. - TextEncoding encoding = UTF8Encoding(); - m_requestEntityBody = FormData::create(encoding.encode(body.characters(), body.length(), EntitiesForUnencodables)); - if (m_upload) - m_requestEntityBody->setAlwaysStream(true); - } - - createRequest(ec); -} - -void XMLHttpRequest::send(const String& body, ExceptionCode& ec) -{ - if (!initSend(ec)) - return; - - if (!body.isNull() && m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { - String contentType = getRequestHeader("Content-Type"); - if (contentType.isEmpty()) { -#if ENABLE(DASHBOARD_SUPPORT) - Settings* settings = m_doc->settings(); - if (settings && settings->usesDashboardBackwardCompatibilityMode()) - setRequestHeaderInternal("Content-Type", "application/x-www-form-urlencoded"); - else -#endif - setRequestHeaderInternal("Content-Type", "application/xml"); - } - - m_requestEntityBody = FormData::create(UTF8Encoding().encode(body.characters(), body.length(), EntitiesForUnencodables)); - if (m_upload) - m_requestEntityBody->setAlwaysStream(true); - } - - createRequest(ec); -} - -void XMLHttpRequest::send(File* body, ExceptionCode& ec) -{ - if (!initSend(ec)) - return; - - if (m_method != "GET" && m_method != "HEAD" && (m_url.protocolIs("http") || m_url.protocolIs("https"))) { - // FIXME: Should we set a Content-Type if one is not set. - // FIXME: add support for uploading bundles. - m_requestEntityBody = FormData::create(); - m_requestEntityBody->appendFile(body->path(), false); - } - - createRequest(ec); -} - -void XMLHttpRequest::createRequest(ExceptionCode& ec) -{ - if (m_async) { - dispatchLoadStartEvent(); - if (m_requestEntityBody && m_upload) - m_upload->dispatchLoadStartEvent(); - } - - m_sameOriginRequest = m_doc->securityOrigin()->canRequest(m_url); - - if (!m_sameOriginRequest) { - makeCrossSiteAccessRequest(ec); - return; - } - - makeSameOriginRequest(ec); -} - -void XMLHttpRequest::makeSameOriginRequest(ExceptionCode& ec) -{ - ASSERT(m_sameOriginRequest); - - ResourceRequest request(m_url); - request.setHTTPMethod(m_method); - - if (m_requestEntityBody) { - ASSERT(m_method != "GET"); - request.setHTTPBody(m_requestEntityBody.release()); - } - - if (m_requestHeaders.size() > 0) - request.addHTTPHeaderFields(m_requestHeaders); - - if (m_async) - loadRequestAsynchronously(request); - else - loadRequestSynchronously(request, ec); -} - -bool XMLHttpRequest::isSimpleCrossSiteAccessRequest() const -{ - if (m_method != "GET" && m_method != "POST") - return false; - - HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); - for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) { - if (!isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) - return false; - } - - return true; -} - -void XMLHttpRequest::makeCrossSiteAccessRequest(ExceptionCode& ec) -{ - ASSERT(!m_sameOriginRequest); - - if (isSimpleCrossSiteAccessRequest()) - makeSimpleCrossSiteAccessRequest(ec); - else - makeCrossSiteAccessRequestWithPreflight(ec); -} - -String XMLHttpRequest::accessControlOrigin() const -{ - String accessControlOrigin = m_doc->securityOrigin()->toString(); - if (accessControlOrigin.isEmpty()) - return "null"; - return accessControlOrigin; -} - -void XMLHttpRequest::makeSimpleCrossSiteAccessRequest(ExceptionCode& ec) -{ - ASSERT(isSimpleCrossSiteAccessRequest()); - - KURL url = m_url; - url.setUser(String()); - url.setPass(String()); - - ResourceRequest request(url); - request.setHTTPMethod(m_method); - request.setAllowHTTPCookies(m_includeCredentials); - request.setHTTPHeaderField("Origin", accessControlOrigin()); - - if (m_requestHeaders.size() > 0) - request.addHTTPHeaderFields(m_requestHeaders); - - if (m_async) - loadRequestAsynchronously(request); - else - loadRequestSynchronously(request, ec); -} - -static bool canSkipPrelight(PreflightResultCache::iterator cacheIt, bool includeCredentials, const String& method, const HTTPHeaderMap& requestHeaders) -{ - PreflightResultCacheItem* item = cacheIt->second; - if (item->m_absoluteExpiryTime < currentTime()) - return false; - if (includeCredentials && !item->m_credentials) - return false; - if (!item->m_methods->contains(method) && method != "GET" && method != "POST") - return false; - HTTPHeaderMap::const_iterator end = requestHeaders.end(); - for (HTTPHeaderMap::const_iterator it = requestHeaders.begin(); it != end; ++it) { - if (!item->m_headers->contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) - return false; - } - - return true; -} - -void XMLHttpRequest::makeCrossSiteAccessRequestWithPreflight(ExceptionCode& ec) -{ - String origin = accessControlOrigin(); - KURL url = m_url; - url.setUser(String()); - url.setPass(String()); - - bool skipPreflight = false; - - PreflightResultCache::iterator cacheIt = preflightResultCache().find(std::make_pair(origin, url)); - if (cacheIt != preflightResultCache().end()) { - skipPreflight = canSkipPrelight(cacheIt, m_includeCredentials, m_method, m_requestHeaders); - if (!skipPreflight) { - delete cacheIt->second; - preflightResultCache().remove(cacheIt); - } - } - - if (!skipPreflight) { - m_inPreflight = true; - ResourceRequest preflightRequest(url); - preflightRequest.setHTTPMethod("OPTIONS"); - preflightRequest.setHTTPHeaderField("Origin", origin); - preflightRequest.setHTTPHeaderField("Access-Control-Request-Method", m_method); - - if (m_requestHeaders.size() > 0) { - Vector<UChar> headerBuffer; - HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); - append(headerBuffer, it->first); - ++it; - - HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); - for (; it != end; ++it) { - headerBuffer.append(','); - headerBuffer.append(' '); - append(headerBuffer, it->first); - } - - preflightRequest.setHTTPHeaderField("Access-Control-Request-Headers", String::adopt(headerBuffer)); - preflightRequest.addHTTPHeaderFields(m_requestHeaders); - } - - if (m_async) { - loadRequestAsynchronously(preflightRequest); - return; - } - - loadRequestSynchronously(preflightRequest, ec); - m_inPreflight = false; - - if (ec) - return; - } - - // Send the actual request. - ResourceRequest request(url); - request.setHTTPMethod(m_method); - request.setAllowHTTPCookies(m_includeCredentials); - request.setHTTPHeaderField("Origin", origin); - - if (m_requestHeaders.size() > 0) - request.addHTTPHeaderFields(m_requestHeaders); - - if (m_requestEntityBody) { - ASSERT(m_method != "GET"); - request.setHTTPBody(m_requestEntityBody.release()); - } - - if (m_async) { - loadRequestAsynchronously(request); - return; - } - - loadRequestSynchronously(request, ec); -} - -void XMLHttpRequest::handleAsynchronousPreflightResult() -{ - ASSERT(m_inPreflight); - ASSERT(m_async); - - m_inPreflight = false; - - KURL url = m_url; - url.setUser(String()); - url.setPass(String()); - - ResourceRequest request(url); - request.setHTTPMethod(m_method); - request.setAllowHTTPCookies(m_includeCredentials); - request.setHTTPHeaderField("Origin", accessControlOrigin()); - - if (m_requestHeaders.size() > 0) - request.addHTTPHeaderFields(m_requestHeaders); - - if (m_requestEntityBody) { - ASSERT(m_method != "GET"); - request.setHTTPBody(m_requestEntityBody.release()); - } - - loadRequestAsynchronously(request); -} - -void XMLHttpRequest::loadRequestSynchronously(ResourceRequest& request, ExceptionCode& ec) -{ - ASSERT(!m_async); - Vector<char> data; - ResourceError error; - ResourceResponse response; - - if (m_doc->frame()) - m_identifier = m_doc->frame()->loader()->loadResourceSynchronously(request, error, response, data); - - m_loader = 0; - - // No exception for file:/// resources, see <rdar://problem/4962298>. - // Also, if we have an HTTP response, then it wasn't a network error in fact. - if (error.isNull() || request.url().isLocalFile() || response.httpStatusCode() > 0) { - processSyncLoadResults(data, response, ec); - return; - } - - if (error.isCancellation()) { - abortError(); - ec = XMLHttpRequestException::ABORT_ERR; - return; - } - - if (error.isCancellation()) { - abortError(); - ec = XMLHttpRequestException::ABORT_ERR; - return; - } - - networkError(); - ec = XMLHttpRequestException::NETWORK_ERR; -} - - -void XMLHttpRequest::loadRequestAsynchronously(ResourceRequest& request) -{ - ASSERT(m_async); - // SubresourceLoader::create can return null here, for example if we're no longer attached to a page. - // This is true while running onunload handlers. - // FIXME: We need to be able to send XMLHttpRequests from onunload, <http://bugs.webkit.org/show_bug.cgi?id=10904>. - // FIXME: Maybe create can return null for other reasons too? - // We need to keep content sniffing enabled for local files due to CFNetwork not providing a MIME type - // for local files otherwise, <rdar://problem/5671813>. - bool sendResourceLoadCallbacks = !m_inPreflight; - m_loader = SubresourceLoader::create(m_doc->frame(), this, request, false, sendResourceLoadCallbacks, request.url().isLocalFile()); - - if (m_loader) { - // Neither this object nor the JavaScript wrapper should be deleted while - // a request is in progress because we need to keep the listeners alive, - // and they are referenced by the JavaScript wrapper. - ref(); - -#if USE(JSC) - KJS::gcProtectNullTolerant(ScriptInterpreter::getDOMObject(this)); -#elif USE(V8) - ScriptController::gcProtectJSWrapper(this); -#endif - } -} - -void XMLHttpRequest::abort() -{ - bool sendFlag = m_loader; - - internalAbort(); - - // Clear headers as required by the spec - m_requestHeaders.clear(); - - if ((m_state <= OPENED && !sendFlag) || m_state == DONE) - m_state = UNSENT; - else { - ASSERT(!m_loader); - changeState(DONE); - m_state = UNSENT; - } - - dispatchAbortEvent(); - if (!m_uploadComplete) { - m_uploadComplete = true; - if (m_upload) - m_upload->dispatchAbortEvent(); - } -} - -void XMLHttpRequest::internalAbort() -{ - bool hadLoader = m_loader; - - m_error = true; - - // FIXME: when we add the support for multi-part XHR, we will have to think be careful with this initialization. - m_receivedLength = 0; - - if (hadLoader) { - m_loader->cancel(); - m_loader = 0; - } - - m_decoder = 0; - - if (hadLoader) - dropProtection(); -} - -void XMLHttpRequest::clearResponse() -{ - m_response = ResourceResponse(); - m_responseText = ""; - m_createdDocument = false; - m_responseXML = 0; -} - -void XMLHttpRequest::clearRequest() -{ - m_requestHeaders.clear(); - m_requestEntityBody = 0; -} - -void XMLHttpRequest::genericError() -{ - clearResponse(); - clearRequest(); - m_error = true; - - // The spec says we should "Synchronously switch the state to DONE." and then "Synchronously dispatch a readystatechange event on the object" - // but this does not match Firefox. -} - -void XMLHttpRequest::networkError() -{ - genericError(); - dispatchErrorEvent(); - if (!m_uploadComplete) { - m_uploadComplete = true; - if (m_upload) - m_upload->dispatchErrorEvent(); - } -} - -void XMLHttpRequest::abortError() -{ - genericError(); - dispatchAbortEvent(); - if (!m_uploadComplete) { - m_uploadComplete = true; - if (m_upload) - m_upload->dispatchAbortEvent(); - } -} - -void XMLHttpRequest::dropProtection() -{ -#if USE(JSC) - // The XHR object itself holds on to the responseText, and - // thus has extra cost even independent of any - // responseText or responseXML objects it has handed - // out. But it is protected from GC while loading, so this - // can't be recouped until the load is done, so only - // report the extra cost at that point. - - KJS::JSValue* wrapper = ScriptInterpreter::getDOMObject(this); - if (wrapper) { - KJS::gcUnprotect(wrapper); - KJS::Heap::heap(wrapper)->reportExtraMemoryCost(m_responseText.size() * 2); - KJS::JSValue* wrapper = ScriptInterpreter::getDOMObject(this); - } -#elif USE(V8) - ScriptController::gcUnprotectJSWrapper(this); -#endif - - deref(); -} - -void XMLHttpRequest::overrideMimeType(const String& override) -{ - m_mimeTypeOverride = override; -} - -void XMLHttpRequest::setRequestHeader(const String& name, const String& value, ExceptionCode& ec) -{ - if (m_state != OPENED || m_loader) { -#if ENABLE(DASHBOARD_SUPPORT) - Settings* settings = m_doc ? m_doc->settings() : 0; - if (settings && settings->usesDashboardBackwardCompatibilityMode()) - return; -#endif - - ec = INVALID_STATE_ERR; - return; - } - - if (!isValidToken(name) || !isValidHeaderValue(value)) { - ec = SYNTAX_ERR; - return; - } - - // A privileged script (e.g. a Dashboard widget) can set any headers. - if (!m_doc->securityOrigin()->canLoadLocalResources() && !isSafeRequestHeader(name)) { - if (m_doc && m_doc->frame()) - m_doc->frame()->domWindow()->console()->addMessage(JSMessageSource, ErrorMessageLevel, "Refused to set unsafe header \"" + name + "\"", 1, String()); - return; - } - - setRequestHeaderInternal(name, value); -} - -void XMLHttpRequest::setRequestHeaderInternal(const String& name, const String& value) -{ - pair<HTTPHeaderMap::iterator, bool> result = m_requestHeaders.add(name, value); - if (!result.second) - result.first->second += ", " + value; -} - -String XMLHttpRequest::getRequestHeader(const String& name) const -{ - return m_requestHeaders.get(name); -} - -String XMLHttpRequest::getAllResponseHeaders(ExceptionCode& ec) const -{ - if (m_state < LOADING) { - ec = INVALID_STATE_ERR; - return ""; - } - - Vector<UChar> stringBuilder; - String separator(": "); - - HTTPHeaderMap::const_iterator end = m_response.httpHeaderFields().end(); - for (HTTPHeaderMap::const_iterator it = m_response.httpHeaderFields().begin(); it!= end; ++it) { - if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(it->first)) - continue; - - stringBuilder.append(it->first.characters(), it->first.length()); - stringBuilder.append(separator.characters(), separator.length()); - stringBuilder.append(it->second.characters(), it->second.length()); - stringBuilder.append((UChar)'\r'); - stringBuilder.append((UChar)'\n'); - } - - return String::adopt(stringBuilder); -} - -String XMLHttpRequest::getResponseHeader(const String& name, ExceptionCode& ec) const -{ - if (m_state < LOADING) { - ec = INVALID_STATE_ERR; - return ""; - } - - if (!isValidToken(name)) - return ""; - - if (!m_sameOriginRequest && !isOnAccessControlResponseHeaderWhitelist(name)) - return ""; - - return m_response.httpHeaderField(name); -} - -String XMLHttpRequest::responseMIMEType() const -{ - String mimeType = extractMIMETypeFromMediaType(m_mimeTypeOverride); - if (mimeType.isEmpty()) { - if (m_response.isHTTP()) - mimeType = extractMIMETypeFromMediaType(m_response.httpHeaderField("Content-Type")); - else - mimeType = m_response.mimeType(); - } - if (mimeType.isEmpty()) - mimeType = "text/xml"; - - return mimeType; -} - -bool XMLHttpRequest::responseIsXML() const -{ - return DOMImplementation::isXMLMIMEType(responseMIMEType()); -} - -int XMLHttpRequest::status(ExceptionCode& ec) const -{ - if (m_response.httpStatusCode()) - return m_response.httpStatusCode(); - - if (m_state == OPENED) { - // Firefox only raises an exception in this state; we match it. - // Note the case of local file requests, where we have no HTTP response code! Firefox never raises an exception for those, but we match HTTP case for consistency. - ec = INVALID_STATE_ERR; - } - - return 0; -} - -String XMLHttpRequest::statusText(ExceptionCode& ec) const -{ - // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=3547> XMLHttpRequest.statusText returns always "OK". - if (m_response.httpStatusCode()) - return "OK"; - - if (m_state == OPENED) { - // See comments in getStatus() above. - ec = INVALID_STATE_ERR; - } - - return String(); -} - -void XMLHttpRequest::processSyncLoadResults(const Vector<char>& data, const ResourceResponse& response, ExceptionCode& ec) -{ - if (m_sameOriginRequest && !m_doc->securityOrigin()->canRequest(response.url())) { - abort(); - return; - } - - didReceiveResponse(0, response); - changeState(HEADERS_RECEIVED); - - const char* bytes = static_cast<const char*>(data.data()); - int len = static_cast<int>(data.size()); - didReceiveData(0, bytes, len); - - didFinishLoading(0); - if (m_error) - ec = XMLHttpRequestException::NETWORK_ERR; -} - -void XMLHttpRequest::didFail(SubresourceLoader* loader, const ResourceError& error) -{ - // If we are already in an error state, for instance we called abort(), bail out early. - if (m_error) - return; - - if (error.isCancellation()) { - abortError(); - return; - } - - networkError(); - return; -} - -void XMLHttpRequest::didFinishLoading(SubresourceLoader* loader) -{ - if (m_error) - return; - - if (m_inPreflight) { - didFinishLoadingPreflight(loader); - return; - } - - ASSERT(loader == m_loader); - - if (m_state < HEADERS_RECEIVED) - changeState(HEADERS_RECEIVED); - - if (m_decoder) - m_responseText += m_decoder->flush(); - - if (Frame* frame = m_doc->frame()) { - if (Page* page = frame->page()) { - page->inspectorController()->resourceRetrievedByXMLHttpRequest(m_loader ? m_loader->identifier() : m_identifier, m_responseText); - page->inspectorController()->addMessageToConsole(JSMessageSource, LogMessageLevel, "XHR finished loading \"" + m_url + "\".", 0, m_doc->url()); - } - } - - bool hadLoader = m_loader; - m_loader = 0; - - changeState(DONE); - m_decoder = 0; - - if (hadLoader) - dropProtection(); -} - -void XMLHttpRequest::didFinishLoadingPreflight(SubresourceLoader* loader) -{ - ASSERT(m_inPreflight); - ASSERT(!m_sameOriginRequest); - - // FIXME: this can probably be moved to didReceiveResponsePreflight. - if (m_async) - handleAsynchronousPreflightResult(); -} - -void XMLHttpRequest::willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse) -{ - // FIXME: This needs to be fixed to follow the redirect correctly even for cross-domain requests. - if (!m_doc->securityOrigin()->canRequest(request.url())) { - internalAbort(); - networkError(); - } -} - -void XMLHttpRequest::didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent) -{ - if (!m_upload) - return; - - m_upload->dispatchProgressEvent(bytesSent, totalBytesToBeSent); - - if (bytesSent == totalBytesToBeSent && !m_uploadComplete) { - m_uploadComplete = true; - m_upload->dispatchLoadEvent(); - } -} - -bool XMLHttpRequest::accessControlCheck(const ResourceResponse& response) -{ - const String& accessControlOriginString = response.httpHeaderField("Access-Control-Origin"); - if (accessControlOriginString == "*" && !m_includeCredentials) - return true; - - KURL accessControlOriginURL(accessControlOriginString); - if (!accessControlOriginURL.isValid()) - return false; - - RefPtr<SecurityOrigin> accessControlOrigin = SecurityOrigin::create(accessControlOriginURL); - if (!accessControlOrigin->isSameSchemeHostPort(m_doc->securityOrigin())) - return false; - - if (m_includeCredentials) { - const String& accessControlCredentialsString = response.httpHeaderField("Access-Control-Credentials"); - if (accessControlCredentialsString != "true") - return false; - } - - return true; -} - -void XMLHttpRequest::didReceiveResponse(SubresourceLoader* loader, const ResourceResponse& response) -{ - if (m_inPreflight) { - didReceiveResponsePreflight(loader, response); - return; - } - - if (!m_sameOriginRequest) { - if (!accessControlCheck(response)) { - networkError(); - return; - } - } - - m_response = response; - m_responseEncoding = extractCharsetFromMediaType(m_mimeTypeOverride); - if (m_responseEncoding.isEmpty()) - m_responseEncoding = response.textEncodingName(); -} - -template<class HashType> -static bool parseAccessControlAllowList(const String& string, HashSet<String, HashType>* set) -{ - int start = 0; - int end; - while ((end = string.find(',', start)) != -1) { - if (start == end) - return false; - - // FIXME: this could be made more efficient by not not allocating twice. - set->add(string.substring(start, end - start).stripWhiteSpace()); - start = end + 1; - } - if (start != static_cast<int>(string.length())) - set->add(string.substring(start).stripWhiteSpace()); - - return true; -} - -static bool parseAccessControlMaxAge(const String& string, unsigned& expiryDelta) -{ - // FIXME: this will not do the correct thing for a number starting with a '+' - bool ok = false; - expiryDelta = string.toUIntStrict(&ok); - return ok; -} - -void XMLHttpRequest::didReceiveResponsePreflight(SubresourceLoader*, const ResourceResponse& response) -{ - ASSERT(m_inPreflight); - ASSERT(!m_sameOriginRequest); - - if (!accessControlCheck(response)) { - networkError(); - return; - } - - OwnPtr<HashSet<String> > methods(new HashSet<String>); - if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Methods"), methods.get())) { - networkError(); - return; - } - - if (!methods->contains(m_method) && m_method != "GET" && m_method != "POST") { - networkError(); - return; - } - - OwnPtr<HashSet<String, CaseFoldingHash> > headers(new HashSet<String, CaseFoldingHash>); - if (!parseAccessControlAllowList(response.httpHeaderField("Access-Control-Allow-Headers"), headers.get())) { - networkError(); - return; - } - - HTTPHeaderMap::const_iterator end = m_requestHeaders.end(); - for (HTTPHeaderMap::const_iterator it = m_requestHeaders.begin(); it != end; ++it) { - if (!headers->contains(it->first) && !isOnAccessControlSimpleRequestHeaderWhitelist(it->first)) { - networkError(); - return; - } - } - - unsigned expiryDelta = 0; - if (!parseAccessControlMaxAge(response.httpHeaderField("Access-Control-Max-Age"), expiryDelta)) - expiryDelta = 5; - - appendPreflightResultCacheEntry(accessControlOrigin(), m_url, expiryDelta, m_includeCredentials, methods.release(), headers.release()); -} - -void XMLHttpRequest::receivedCancellation(SubresourceLoader*, const AuthenticationChallenge& challenge) -{ - m_response = challenge.failureResponse(); -} - -void XMLHttpRequest::didReceiveData(SubresourceLoader*, const char* data, int len) -{ - if (m_inPreflight) - return; - - if (m_state < HEADERS_RECEIVED) - changeState(HEADERS_RECEIVED); - - if (!m_decoder) { - if (!m_responseEncoding.isEmpty()) - m_decoder = TextResourceDecoder::create("text/plain", m_responseEncoding); - // allow TextResourceDecoder to look inside the m_response if it's XML or HTML - else if (responseIsXML()) - m_decoder = TextResourceDecoder::create("application/xml"); - else if (responseMIMEType() == "text/html") - m_decoder = TextResourceDecoder::create("text/html", "UTF-8"); - else - m_decoder = TextResourceDecoder::create("text/plain", "UTF-8"); - } - if (len == 0) - return; - - if (len == -1) - len = strlen(data); - - String decoded = m_decoder->decode(data, len); - - m_responseText += decoded; - - if (!m_error) { - updateAndDispatchOnProgress(len); - - if (m_state != LOADING) - changeState(LOADING); - else - // Firefox calls readyStateChanged every time it receives data, 4449442 - callReadyStateChangeListener(); - } -} - -void XMLHttpRequest::updateAndDispatchOnProgress(unsigned int len) -{ - long long expectedLength = m_response.expectedContentLength(); - m_receivedLength += len; - - // FIXME: the spec requires that we dispatch the event according to the least - // frequent method between every 350ms (+/-200ms) and for every byte received. - dispatchProgressEvent(expectedLength); -} - -void XMLHttpRequest::dispatchReadyStateChangeEvent() -{ - RefPtr<Event> evt = Event::create(readystatechangeEvent, false, false); - if (m_onReadyStateChangeListener) { - evt->setTarget(this); - evt->setCurrentTarget(this); - m_onReadyStateChangeListener->handleEvent(evt.get(), false); - } - - ExceptionCode ec = 0; - dispatchEvent(evt.release(), ec, false); - ASSERT(!ec); -} - -void XMLHttpRequest::dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total) -{ - RefPtr<XMLHttpRequestProgressEvent> evt = XMLHttpRequestProgressEvent::create(type, lengthComputable, loaded, total); - if (listener) { - evt->setTarget(this); - evt->setCurrentTarget(this); - listener->handleEvent(evt.get(), false); - } - - ExceptionCode ec = 0; - dispatchEvent(evt.release(), ec, false); - ASSERT(!ec); -} - -void XMLHttpRequest::dispatchAbortEvent() -{ - dispatchXMLHttpRequestProgressEvent(m_onAbortListener.get(), abortEvent, false, 0, 0); -} - -void XMLHttpRequest::dispatchErrorEvent() -{ - dispatchXMLHttpRequestProgressEvent(m_onErrorListener.get(), errorEvent, false, 0, 0); -} - -void XMLHttpRequest::dispatchLoadEvent() -{ - dispatchXMLHttpRequestProgressEvent(m_onLoadListener.get(), loadEvent, false, 0, 0); -} - -void XMLHttpRequest::dispatchLoadStartEvent() -{ - dispatchXMLHttpRequestProgressEvent(m_onLoadStartListener.get(), loadstartEvent, false, 0, 0); -} - -void XMLHttpRequest::dispatchProgressEvent(long long expectedLength) -{ - dispatchXMLHttpRequestProgressEvent(m_onProgressListener.get(), progressEvent, expectedLength && m_receivedLength <= expectedLength, - static_cast<unsigned>(m_receivedLength), static_cast<unsigned>(expectedLength)); -} - -void XMLHttpRequest::cancelRequests(Document* m_doc) -{ - RequestsSet* requests = requestsByDocument().get(m_doc); - if (!requests) - return; - RequestsSet copy = *requests; - RequestsSet::const_iterator end = copy.end(); - for (RequestsSet::const_iterator it = copy.begin(); it != end; ++it) - (*it)->internalAbort(); -} - -void XMLHttpRequest::detachRequests(Document* m_doc) -{ - RequestsSet* requests = requestsByDocument().get(m_doc); - if (!requests) - return; - requestsByDocument().remove(m_doc); - RequestsSet::const_iterator end = requests->end(); - for (RequestsSet::const_iterator it = requests->begin(); it != end; ++it) { - (*it)->m_doc = 0; - (*it)->internalAbort(); - } - delete requests; -} - -} // namespace WebCore diff --git a/webkit/pending/XMLHttpRequest.h b/webkit/pending/XMLHttpRequest.h deleted file mode 100644 index 77d32e6..0000000 --- a/webkit/pending/XMLHttpRequest.h +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. - * Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@nypop.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef XMLHttpRequest_h -#define XMLHttpRequest_h - -#include "EventListener.h" -#include "EventTarget.h" -#include "FormData.h" -#include "ResourceResponse.h" -#include "SubresourceLoaderClient.h" -#include "ScriptController.h" -#include <wtf/OwnPtr.h> - -namespace WebCore { - -class Document; -class File; -class TextResourceDecoder; - -class XMLHttpRequest : public RefCounted<XMLHttpRequest>, public EventTarget, private SubresourceLoaderClient { -public: - static PassRefPtr<XMLHttpRequest> create(Document* document) { return adoptRef(new XMLHttpRequest(document)); } - ~XMLHttpRequest(); - - // These exact numeric values are important because JS expects them. - enum State { - UNSENT = 0, - OPENED = 1, - HEADERS_RECEIVED = 2, - LOADING = 3, - DONE = 4 - }; - - virtual XMLHttpRequest* toXMLHttpRequest() { return this; } - - static void detachRequests(Document*); - static void cancelRequests(Document*); - - String statusText(ExceptionCode&) const; - int status(ExceptionCode&) const; - State readyState() const; - void open(const String& method, const KURL&, bool async, ExceptionCode&); - void open(const String& method, const KURL&, bool async, const String& user, ExceptionCode&); - void open(const String& method, const KURL&, bool async, const String& user, const String& password, ExceptionCode&); - void send(ExceptionCode&); - void send(Document*, ExceptionCode&); - void send(const String&, ExceptionCode&); - void send(File*, ExceptionCode&); - void abort(); - void setRequestHeader(const String& name, const String& value, ExceptionCode&); - void overrideMimeType(const String& override); - String getAllResponseHeaders(ExceptionCode&) const; - String getResponseHeader(const String& name, ExceptionCode&) const; - const JSString& responseText() const; - Document* responseXML() const; - - XMLHttpRequestUpload* upload(); - XMLHttpRequestUpload* optionalUpload() const { return m_upload.get(); } - - void setOnReadyStateChangeListener(PassRefPtr<EventListener> eventListener) { m_onReadyStateChangeListener = eventListener; } - EventListener* onReadyStateChangeListener() const { return m_onReadyStateChangeListener.get(); } - - void setOnAbortListener(PassRefPtr<EventListener> eventListener) { m_onAbortListener = eventListener; } - EventListener* onAbortListener() const { return m_onAbortListener.get(); } - - void setOnErrorListener(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } - EventListener* onErrorListener() const { return m_onErrorListener.get(); } - - void setOnLoadListener(PassRefPtr<EventListener> eventListener) { m_onLoadListener = eventListener; } - EventListener* onLoadListener() const { return m_onLoadListener.get(); } - - void setOnLoadStartListener(PassRefPtr<EventListener> eventListener) { m_onLoadStartListener = eventListener; } - EventListener* onLoadStartListener() const { return m_onLoadStartListener.get(); } - - void setOnProgressListener(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; } - EventListener* onProgressListener() const { return m_onProgressListener.get(); } - -#if USE(V8) - // Sam Weinig says that upstream WebKit plans to rename the above methods - // to have these same names for the bindings as well. - void setOnreadystatechange(EventListener* listener) { setOnReadyStateChangeListener(listener); } - EventListener* onreadystatechange() const { return onReadyStateChangeListener(); } - - void setOnabort(PassRefPtr<EventListener> eventListener) { m_onAbortListener = eventListener; } - EventListener* onabort() const { return m_onAbortListener.get(); } - - void setOnerror(PassRefPtr<EventListener> eventListener) { m_onErrorListener = eventListener; } - EventListener* onerror() const { return m_onErrorListener.get(); } - - void setOnload(EventListener* listener) { setOnLoadListener(listener); } - EventListener* onload() const { return onLoadListener(); } - - void setOnloadstart(PassRefPtr<EventListener> eventListener) { m_onLoadStartListener = eventListener; } - EventListener* onloadstart() const { return m_onLoadStartListener.get(); } - - void setOnprogress(PassRefPtr<EventListener> eventListener) { m_onProgressListener = eventListener; } - EventListener* onprogress() const { return m_onProgressListener.get(); } -#endif - - typedef Vector<RefPtr<EventListener> > ListenerVector; - typedef HashMap<AtomicStringImpl*, ListenerVector> EventListenersMap; - - // useCapture is not used, even for add/remove pairing (for Firefox compatibility). - virtual void addEventListener(const AtomicString& eventType, PassRefPtr<EventListener>, bool useCapture); - virtual void removeEventListener(const AtomicString& eventType, EventListener*, bool useCapture); - virtual bool dispatchEvent(PassRefPtr<Event>, ExceptionCode&, bool tempEvent = false); - EventListenersMap& eventListeners() { return m_eventListeners; } - - Document* document() const { return m_doc; } - - using RefCounted<XMLHttpRequest>::ref; - using RefCounted<XMLHttpRequest>::deref; - - Document* getOwnerDocument() { return m_doc; } - -private: - XMLHttpRequest(Document*); - - virtual void refEventTarget() { ref(); } - virtual void derefEventTarget() { deref(); } - - virtual void willSendRequest(SubresourceLoader*, ResourceRequest& request, const ResourceResponse& redirectResponse); - virtual void didSendData(SubresourceLoader*, unsigned long long bytesSent, unsigned long long totalBytesToBeSent); - virtual void didReceiveResponse(SubresourceLoader*, const ResourceResponse&); - virtual void didReceiveData(SubresourceLoader*, const char* data, int size); - virtual void didFail(SubresourceLoader*, const ResourceError&); - virtual void didFinishLoading(SubresourceLoader*); - virtual void receivedCancellation(SubresourceLoader*, const AuthenticationChallenge&); - - // Special versions for the preflight - void didReceiveResponsePreflight(SubresourceLoader*, const ResourceResponse&); - void didFinishLoadingPreflight(SubresourceLoader*); - - void processSyncLoadResults(const Vector<char>& data, const ResourceResponse&, ExceptionCode&); - void updateAndDispatchOnProgress(unsigned int len); - - String responseMIMEType() const; - bool responseIsXML() const; - - bool initSend(ExceptionCode&); - - String getRequestHeader(const String& name) const; - void setRequestHeaderInternal(const String& name, const String& value); - - void changeState(State newState); - void callReadyStateChangeListener(); - void dropProtection(); - void internalAbort(); - void clearResponse(); - void clearRequest(); - - void createRequest(ExceptionCode&); - - void makeSameOriginRequest(ExceptionCode&); - void makeCrossSiteAccessRequest(ExceptionCode&); - - void makeSimpleCrossSiteAccessRequest(ExceptionCode&); - void makeCrossSiteAccessRequestWithPreflight(ExceptionCode&); - void handleAsynchronousPreflightResult(); - - void loadRequestSynchronously(ResourceRequest&, ExceptionCode&); - void loadRequestAsynchronously(ResourceRequest&); - - bool isSimpleCrossSiteAccessRequest() const; - String accessControlOrigin() const; - bool accessControlCheck(const ResourceResponse&); - - void genericError(); - void networkError(); - void abortError(); - - void dispatchReadyStateChangeEvent(); - void dispatchXMLHttpRequestProgressEvent(EventListener* listener, const AtomicString& type, bool lengthComputable, unsigned loaded, unsigned total); - void dispatchAbortEvent(); - void dispatchErrorEvent(); - void dispatchLoadEvent(); - void dispatchLoadStartEvent(); - void dispatchProgressEvent(long long expectedLength); - - Document* m_doc; - - RefPtr<EventListener> m_onReadyStateChangeListener; - RefPtr<EventListener> m_onAbortListener; - RefPtr<EventListener> m_onErrorListener; - RefPtr<EventListener> m_onLoadListener; - RefPtr<EventListener> m_onLoadStartListener; - RefPtr<EventListener> m_onProgressListener; - EventListenersMap m_eventListeners; - - RefPtr<XMLHttpRequestUpload> m_upload; - - KURL m_url; - String m_method; - HTTPHeaderMap m_requestHeaders; - RefPtr<FormData> m_requestEntityBody; - String m_mimeTypeOverride; - bool m_async; - bool m_includeCredentials; - - RefPtr<SubresourceLoader> m_loader; - State m_state; - - ResourceResponse m_response; - String m_responseEncoding; - - RefPtr<TextResourceDecoder> m_decoder; - unsigned long m_identifier; - - // Unlike most strings in the DOM, we keep this as a KJS::UString, not a WebCore::String. - // That's because these strings can easily get huge (they are filled from the network with - // no parsing) and because JS can easily observe many intermediate states, so it's very useful - // to be able to share the buffer with JavaScript versions of the whole or partial string. - // In contrast, this string doesn't interact much with the rest of the engine so it's not that - // big a cost that it isn't a String. - JSString m_responseText; - mutable bool m_createdDocument; - mutable RefPtr<Document> m_responseXML; - - bool m_error; - - bool m_uploadComplete; - - bool m_sameOriginRequest; - bool m_allowAccess; - bool m_inPreflight; - - // Used for onprogress tracking - long long m_receivedLength; -}; - -} // namespace WebCore - -#endif // XMLHttpRequest_h diff --git a/webkit/pending/XSLImportRule.cpp b/webkit/pending/XSLImportRule.cpp deleted file mode 100644 index 6ceb108..0000000 --- a/webkit/pending/XSLImportRule.cpp +++ /dev/null @@ -1,117 +0,0 @@ -/* - * This file is part of the XSL implementation. - * - * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "XSLImportRule.h" - -#if ENABLE(XSLT) - -#include "CachedXSLStyleSheet.h" -#include "DocLoader.h" -#include "XSLStyleSheet.h" - -namespace WebCore { - -XSLImportRule::XSLImportRule(XSLStyleSheet* parent, const String& href) - : StyleBase(parent) - , m_strHref(href) - , m_cachedSheet(0) - , m_loading(false) -{ -} - -XSLImportRule::~XSLImportRule() -{ - if (m_styleSheet) - m_styleSheet->setParent(0); - - if (m_cachedSheet) - m_cachedSheet->removeClient(this); -} - -XSLStyleSheet* XSLImportRule::parentStyleSheet() const -{ - return (parent() && parent()->isXSLStyleSheet()) ? static_cast<XSLStyleSheet*>(parent()) : 0; -} - -void XSLImportRule::setXSLStyleSheet(const String& url, const String& sheet) -{ - if (m_styleSheet) - m_styleSheet->setParent(0); - - m_styleSheet = XSLStyleSheet::create(this, url); - - XSLStyleSheet* parent = parentStyleSheet(); - if (parent) - m_styleSheet->setParentStyleSheet(parent); - - m_styleSheet->parseString(sheet); - m_loading = false; - - if (parent) - parent->checkLoaded(); -} - -bool XSLImportRule::isLoading() -{ - return (m_loading || (m_styleSheet && m_styleSheet->isLoading())); -} - -void XSLImportRule::loadSheet() -{ - DocLoader* docLoader = 0; - StyleBase* root = this; - StyleBase* parent; - while ((parent = root->parent())) - root = parent; - if (root->isXSLStyleSheet()) - docLoader = static_cast<XSLStyleSheet*>(root)->docLoader(); - - String absHref = m_strHref; - XSLStyleSheet* parentSheet = parentStyleSheet(); - if (!parentSheet->href().isNull()) - // use parent styleheet's URL as the base URL - absHref = KURL(KURL(parentSheet->href()), m_strHref).string(); - - // Check for a cycle in our import chain. If we encounter a stylesheet - // in our parent chain with the same URL, then just bail. - for (parent = this->parent(); parent; parent = parent->parent()) { - if (parent->isXSLStyleSheet() && absHref == static_cast<XSLStyleSheet*>(parent)->href()) - return; - } - - m_cachedSheet = docLoader->requestXSLStyleSheet(absHref); - - if (m_cachedSheet) { - m_cachedSheet->addClient(this); - - // If the imported sheet is in the cache, then setXSLStyleSheet gets called, - // and the sheet even gets parsed (via parseString). In this case we have - // loaded (even if our subresources haven't), so if we have a stylesheet after - // checking the cache, then we've clearly loaded. - if (!m_styleSheet) - m_loading = true; - } -} - -} // namespace WebCore - -#endif // ENABLE(XSLT) diff --git a/webkit/pending/XSLStyleSheet.cpp b/webkit/pending/XSLStyleSheet.cpp deleted file mode 100644 index 316db15..0000000 --- a/webkit/pending/XSLStyleSheet.cpp +++ /dev/null @@ -1,316 +0,0 @@ -/* - * This file is part of the XSL implementation. - * - * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - */ - -#include "config.h" -#include "XSLStyleSheet.h" - -#if ENABLE(XSLT) - -#include "CString.h" -#include "Console.h" -#include "DOMWindow.h" -#include "DocLoader.h" -#include "Document.h" -#include "Frame.h" -#include "loader.h" -#include "Node.h" -#include "XMLTokenizer.h" -#include "XSLImportRule.h" -#include "XSLTProcessor.h" - -#include <libxml/uri.h> -#include <libxslt/xsltutils.h> - -#if PLATFORM(MAC) -#include "SoftLinking.h" -#endif - -#if PLATFORM(MAC) -SOFT_LINK_LIBRARY(libxslt) -SOFT_LINK(libxslt, xsltIsBlank, int, (xmlChar *str), (str)) -SOFT_LINK(libxslt, xsltGetNsProp, xmlChar *, (xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace), (node, name, nameSpace)) -SOFT_LINK(libxslt, xsltParseStylesheetDoc, xsltStylesheetPtr, (xmlDocPtr doc), (doc)) -SOFT_LINK(libxslt, xsltLoadStylesheetPI, xsltStylesheetPtr, (xmlDocPtr doc), (doc)) -#endif - -namespace WebCore { - -XSLStyleSheet::XSLStyleSheet(XSLImportRule* parentRule, const String& href) - : StyleSheet(parentRule, href) - , m_ownerDocument(0) - , m_stylesheetDoc(0) - , m_embedded(false) - , m_processed(false) // Child sheets get marked as processed when the libxslt engine has finally seen them. - , m_stylesheetDocTaken(false) - , m_parentStyleSheet(0) -{ -} - -XSLStyleSheet::XSLStyleSheet(Node* parentNode, const String& href, bool embedded) - : StyleSheet(parentNode, href) - , m_ownerDocument(parentNode->document()) - , m_stylesheetDoc(0) - , m_embedded(embedded) - , m_processed(true) // The root sheet starts off processed. - , m_stylesheetDocTaken(false) - , m_parentStyleSheet(0) -{ -} - -XSLStyleSheet::~XSLStyleSheet() -{ - if (!m_stylesheetDocTaken) - xmlFreeDoc(m_stylesheetDoc); -} - -bool XSLStyleSheet::isLoading() -{ - unsigned len = length(); - for (unsigned i = 0; i < len; ++i) { - StyleBase* rule = item(i); - if (rule->isImportRule()) { - XSLImportRule* import = static_cast<XSLImportRule*>(rule); - if (import->isLoading()) - return true; - } - } - return false; -} - -void XSLStyleSheet::checkLoaded() -{ - if (isLoading()) - return; - if (parent()) - parent()->checkLoaded(); - if (ownerNode()) - ownerNode()->sheetLoaded(); -} - -xmlDocPtr XSLStyleSheet::document() -{ - if (m_embedded && ownerDocument()) - return (xmlDocPtr)ownerDocument()->transformSource(); - return m_stylesheetDoc; -} - -void XSLStyleSheet::clearDocuments() -{ - m_stylesheetDoc = 0; - unsigned len = length(); - for (unsigned i = 0; i < len; ++i) { - StyleBase* rule = item(i); - if (rule->isImportRule()) { - XSLImportRule* import = static_cast<XSLImportRule*>(rule); - if (import->styleSheet()) - import->styleSheet()->clearDocuments(); - } - } -} - -DocLoader* XSLStyleSheet::docLoader() -{ - if (!m_ownerDocument) - return 0; - return m_ownerDocument->docLoader(); -} - -bool XSLStyleSheet::parseString(const String& string, bool strict) -{ - // Parse in a single chunk into an xmlDocPtr - const UChar BOM = 0xFEFF; - const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM); - setLoaderForLibXMLCallbacks(docLoader()); - if (!m_stylesheetDocTaken) - xmlFreeDoc(m_stylesheetDoc); - m_stylesheetDocTaken = false; - - Console* console = 0; - if (Frame* frame = ownerDocument()->frame()) - console = frame->domWindow()->console(); - xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc); - xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc); - - xmlParserCtxtPtr ctxt = - xmlCreateMemoryParserCtxt(reinterpret_cast<const char*>(string.characters()), string.length()); - - if (parentStyleSheet()) - { - // Child stylesheets should use the same libxml dictionary as - // their parents. Otherwise both dictionaries end up trying to - // free the same memory. - xmlDictFree(ctxt->dict); - ctxt->dict = parentStyleSheet()->m_stylesheetDoc->dict; - } - - m_stylesheetDoc = xmlCtxtReadMemory(ctxt, reinterpret_cast<const char*>(string.characters()), string.length() * sizeof(UChar), - href().utf8().data(), - BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE", - XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA); - // If we had already freed the dict memory, do not try to clean it up - // again. - if (parentStyleSheet()) - ctxt->dict = NULL; - xmlFreeParserCtxt(ctxt); - - loadChildSheets(); - - xmlSetStructuredErrorFunc(0, 0); - xmlSetGenericErrorFunc(0, 0); - - setLoaderForLibXMLCallbacks(0); - return m_stylesheetDoc; -} - -void XSLStyleSheet::loadChildSheets() -{ - if (!document()) - return; - - xmlNodePtr stylesheetRoot = document()->children; - - // Top level children may include other things such as DTD nodes, we ignore those. - while (stylesheetRoot && stylesheetRoot->type != XML_ELEMENT_NODE) - stylesheetRoot = stylesheetRoot->next; - - if (m_embedded) { - // We have to locate (by ID) the appropriate embedded stylesheet element, so that we can walk the - // import/include list. - xmlAttrPtr idNode = xmlGetID(document(), (const xmlChar*)(href().utf8().data())); - if (!idNode) - return; - stylesheetRoot = idNode->parent; - } else { - // FIXME: Need to handle an external URI with a # in it. This is a pretty minor edge case, so we'll deal - // with it later. - } - - if (stylesheetRoot) { - // Walk the children of the root element and look for import/include elements. - // Imports must occur first. - xmlNodePtr curr = stylesheetRoot->children; - while (curr) { - if (curr->type != XML_ELEMENT_NODE) { - curr = curr->next; - continue; - } - if (IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "import")) { - xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE); - loadChildSheet(String::fromUTF8((const char*)uriRef)); - xmlFree(uriRef); - } else - break; - curr = curr->next; - } - - // Now handle includes. - while (curr) { - if (curr->type == XML_ELEMENT_NODE && IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "include")) { - xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE); - loadChildSheet(String::fromUTF8((const char*)uriRef)); - xmlFree(uriRef); - } - curr = curr->next; - } - } -} - -void XSLStyleSheet::loadChildSheet(const String& href) -{ - RefPtr<XSLImportRule> childRule = XSLImportRule::create(this, href); - append(childRule); - childRule->loadSheet(); -} - -xsltStylesheetPtr XSLStyleSheet::compileStyleSheet() -{ - // FIXME: Hook up error reporting for the stylesheet compilation process. - if (m_embedded) - return xsltLoadStylesheetPI(document()); - - // xsltParseStylesheetDoc makes the document part of the stylesheet - // so we have to release our pointer to it. - ASSERT(!m_stylesheetDocTaken); - xsltStylesheetPtr result = xsltParseStylesheetDoc(m_stylesheetDoc); - if (result) - m_stylesheetDocTaken = true; - return result; -} - -xmlDocPtr XSLStyleSheet::locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri) -{ - bool matchedParent = (parentDoc == document()); - unsigned len = length(); - for (unsigned i = 0; i < len; ++i) { - StyleBase* rule = item(i); - if (rule->isImportRule()) { - XSLImportRule* import = static_cast<XSLImportRule*>(rule); - XSLStyleSheet* child = import->styleSheet(); - if (!child) - continue; - if (matchedParent) { - if (child->processed()) - continue; // libxslt has been given this sheet already. - - // Check the URI of the child stylesheet against the doc URI. - // In order to ensure that libxml canonicalized both URLs, we get the original href - // string from the import rule and canonicalize it using libxml before comparing it - // with the URI argument. - CString importHref = import->href().utf8(); - xmlChar* base = xmlNodeGetBase(parentDoc, (xmlNodePtr)parentDoc); - xmlChar* childURI = xmlBuildURI((const xmlChar*)importHref.data(), base); - bool equalURIs = xmlStrEqual(uri, childURI); - xmlFree(base); - xmlFree(childURI); - if (equalURIs) { - child->markAsProcessed(); - return child->document(); - } - } else { - xmlDocPtr result = import->styleSheet()->locateStylesheetSubResource(parentDoc, uri); - if (result) - return result; - } - } - } - - return 0; -} - -void XSLStyleSheet::markAsProcessed() -{ - ASSERT(!m_processed); - ASSERT(!m_stylesheetDocTaken); - m_processed = true; - m_stylesheetDocTaken = true; -} - -void XSLStyleSheet::setParentStyleSheet(XSLStyleSheet* parent) -{ - m_parentStyleSheet = parent; - if (parent) { - setOwnerDocument(parent->ownerDocument()); - } -} - -} // namespace WebCore - -#endif // ENABLE(XSLT) diff --git a/webkit/pending/XSLStyleSheet.h b/webkit/pending/XSLStyleSheet.h deleted file mode 100644 index c6ed097..0000000 --- a/webkit/pending/XSLStyleSheet.h +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the XSL implementation. - * - * Copyright (C) 2004, 2006, 2008 Apple Inc. All rights reserved. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public License - * along with this library; see the file COPYING.LIB. If not, write to - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, - * Boston, MA 02110-1301, USA. - * - */ - -#ifndef XSLStyleSheet_h -#define XSLStyleSheet_h - -#if ENABLE(XSLT) - -#include "StyleSheet.h" -#include <libxml/parser.h> -#include <libxslt/transform.h> -#include <wtf/PassRefPtr.h> - -namespace WebCore { - -class DocLoader; -class Document; -class XSLImportRule; - -class XSLStyleSheet : public StyleSheet { -public: - static PassRefPtr<XSLStyleSheet> create(XSLImportRule* parentImport, const String& href) - { - return adoptRef(new XSLStyleSheet(parentImport, href)); - } - static PassRefPtr<XSLStyleSheet> create(Node* parentNode, const String& href) - { - return adoptRef(new XSLStyleSheet(parentNode, href, false)); - } - static PassRefPtr<XSLStyleSheet> createEmbedded(Node* parentNode, const String& href) - { - return adoptRef(new XSLStyleSheet(parentNode, href, true)); - } - - virtual ~XSLStyleSheet(); - - virtual bool isXSLStyleSheet() const { return true; } - - virtual String type() const { return "text/xml"; } - - virtual bool parseString(const String &string, bool strict = true); - - virtual bool isLoading(); - virtual void checkLoaded(); - - void loadChildSheets(); - void loadChildSheet(const String& href); - - xsltStylesheetPtr compileStyleSheet(); - - DocLoader* docLoader(); - - Document* ownerDocument() { return m_ownerDocument; } - void setOwnerDocument(Document* doc) { m_ownerDocument = doc; } - - xmlDocPtr document(); - - void clearDocuments(); - - xmlDocPtr locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri); - - void markAsProcessed(); - bool processed() const { return m_processed; } - - XSLStyleSheet* parentStyleSheet() { return m_parentStyleSheet; } - void setParentStyleSheet(XSLStyleSheet* parent); - -private: - XSLStyleSheet(Node* parentNode, const String& href, bool embedded); - XSLStyleSheet(XSLImportRule* parentImport, const String& href); - - Document* m_ownerDocument; - xmlDocPtr m_stylesheetDoc; - bool m_embedded; - bool m_processed; - bool m_stylesheetDocTaken; - - XSLStyleSheet* m_parentStyleSheet; -}; - -} // namespace WebCore - -#endif // ENABLE(XSLT) - -#endif // XSLStyleSheet_h diff --git a/webkit/pending/pcre_exec.cpp b/webkit/pending/pcre_exec.cpp deleted file mode 100644 index c1f6fb8..0000000 --- a/webkit/pending/pcre_exec.cpp +++ /dev/null @@ -1,2176 +0,0 @@ -/* This is JavaScriptCore's variant of the PCRE library. While this library -started out as a copy of PCRE, many of the features of PCRE have been -removed. This library now supports only the regular expression features -required by the JavaScript language specification, and has only the functions -needed by JavaScriptCore and the rest of WebKit. - - Originally written by Philip Hazel - Copyright (c) 1997-2006 University of Cambridge - Copyright (C) 2002, 2004, 2006, 2007 Apple Inc. All rights reserved. - Copyright (C) 2007 Eric Seidel <eric@webkit.org> - ------------------------------------------------------------------------------ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, - this list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in the - documentation and/or other materials provided with the distribution. - - * Neither the name of the University of Cambridge nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. ------------------------------------------------------------------------------ -*/ - -/* This module contains jsRegExpExecute(), the externally visible function -that does pattern matching using an NFA algorithm, following the rules from -the JavaScript specification. There are also some supporting functions. */ - -#include "config.h" -#include "pcre_internal.h" - -#include <limits.h> -#include <wtf/ASCIICType.h> -#include <wtf/Vector.h> - -#if REGEXP_HISTOGRAM -#include <kjs/DateMath.h> -#include <kjs/ustring.h> -#endif - -using namespace WTF; - -#ifdef __GNUC__ -#define USE_COMPUTED_GOTO_FOR_MATCH_RECURSION -//#define USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP -#endif - -/* Avoid warnings on Windows. */ -#undef min -#undef max - -#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION -typedef int ReturnLocation; -#else -typedef void* ReturnLocation; -#endif - -#if !REGEXP_HISTOGRAM - -class HistogramTimeLogger { -public: - HistogramTimeLogger(const JSRegExp*) { } -}; - -#else - -using namespace KJS; - -class Histogram { -public: - ~Histogram(); - void add(const JSRegExp*, double); - -private: - typedef HashMap<RefPtr<UString::Rep>, double> Map; - Map times; -}; - -class HistogramTimeLogger { -public: - HistogramTimeLogger(const JSRegExp*); - ~HistogramTimeLogger(); - -private: - const JSRegExp* m_re; - double m_startTime; -}; - -#endif - -/* Structure for building a chain of data for holding the values of -the subject pointer at the start of each bracket, used to detect when -an empty string has been matched by a bracket to break infinite loops. */ -struct BracketChainNode { - BracketChainNode* previousBracket; - const UChar* bracketStart; -}; - -struct MatchFrame { - ReturnLocation returnLocation; - struct MatchFrame* previousFrame; - - /* Function arguments that may change */ - struct { - const UChar* subjectPtr; - const unsigned char* instructionPtr; - int offsetTop; - BracketChainNode* bracketChain; - } args; - - - /* PCRE uses "fake" recursion built off of gotos, thus - stack-based local variables are not safe to use. Instead we have to - store local variables on the current MatchFrame. */ - struct { - const unsigned char* data; - const unsigned char* startOfRepeatingBracket; - const UChar* subjectPtrAtStartOfInstruction; // Several instrutions stash away a subjectPtr here for later compare - const unsigned char* instructionPtrAtStartOfOnce; - - int repeatOthercase; - - int ctype; - int fc; - int fi; - int length; - int max; - int number; - int offset; - int saveOffset1; - int saveOffset2; - int saveOffset3; - - BracketChainNode bracketChainNode; - } locals; -}; - -/* Structure for passing "static" information around between the functions -doing traditional NFA matching, so that they are thread-safe. */ - -struct MatchData { - int* offsetVector; /* Offset vector */ - int offsetEnd; /* One past the end */ - int offsetMax; /* The maximum usable for return data */ - bool offsetOverflow; /* Set if too many extractions */ - const UChar* startSubject; /* Start of the subject string */ - const UChar* endSubject; /* End of the subject string */ - const UChar* endMatchPtr; /* Subject position at end match */ - int endOffsetTop; /* Highwater mark at end of match */ - bool multiline; - bool ignoreCase; -}; - -/* The maximum remaining length of subject we are prepared to search for a -reqByte match. */ - -#define REQ_BYTE_MAX 1000 - -/* The below limit restricts the number of "recursive" match calls in order to -avoid spending exponential time on complex regular expressions. */ - -static const unsigned matchLimit = 100000; - -#ifdef DEBUG -/************************************************* -* Debugging function to print chars * -*************************************************/ - -/* Print a sequence of chars in printable format, stopping at the end of the -subject if the requested. - -Arguments: - p points to characters - length number to print - isSubject true if printing from within md.startSubject - md pointer to matching data block, if isSubject is true -*/ - -static void pchars(const UChar* p, int length, bool isSubject, const MatchData& md) -{ - if (isSubject && length > md.endSubject - p) - length = md.endSubject - p; - while (length-- > 0) { - int c; - if (isprint(c = *(p++))) - printf("%c", c); - else if (c < 256) - printf("\\x%02x", c); - else - printf("\\x{%x}", c); - } -} -#endif - -/************************************************* -* Match a back-reference * -*************************************************/ - -/* If a back reference hasn't been set, the length that is passed is greater -than the number of characters left in the string, so the match fails. - -Arguments: - offset index into the offset vector - subjectPtr points into the subject - length length to be matched - md points to match data block - -Returns: true if matched -*/ - -static bool matchRef(int offset, const UChar* subjectPtr, int length, const MatchData& md) -{ - const UChar* p = md.startSubject + md.offsetVector[offset]; - -#ifdef DEBUG - if (subjectPtr >= md.endSubject) - printf("matching subject <null>"); - else { - printf("matching subject "); - pchars(subjectPtr, length, true, md); - } - printf(" against backref "); - pchars(p, length, false, md); - printf("\n"); -#endif - - /* Always fail if not enough characters left */ - - if (length > md.endSubject - subjectPtr) - return false; - - /* Separate the caselesss case for speed */ - - if (md.ignoreCase) { - while (length-- > 0) { - UChar c = *p++; - int othercase = kjs_pcre_ucp_othercase(c); - UChar d = *subjectPtr++; - if (c != d && othercase != d) - return false; - } - } - else { - while (length-- > 0) - if (*p++ != *subjectPtr++) - return false; - } - - return true; -} - -#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION - -/* Use numbered labels and switch statement at the bottom of the match function. */ - -#define RMATCH_WHERE(num) num -#define RRETURN_LABEL RRETURN_SWITCH - -#else - -/* Use GCC's computed goto extension. */ - -/* For one test case this is more than 40% faster than the switch statement. -We could avoid the use of the num argument entirely by using local labels, -but using it for the GCC case as well as the non-GCC case allows us to share -a bit more code and notice if we use conflicting numbers.*/ - -#define RMATCH_WHERE(num) &&RRETURN_##num -#define RRETURN_LABEL *stack.currentFrame->returnLocation - -#endif - -#define RECURSIVE_MATCH_COMMON(num) \ - goto RECURSE;\ - RRETURN_##num: \ - stack.popCurrentFrame(); - -#define RECURSIVE_MATCH(num, ra, rb) \ - do { \ - stack.pushNewFrame((ra), (rb), RMATCH_WHERE(num)); \ - RECURSIVE_MATCH_COMMON(num) \ - } while (0) - -#define RECURSIVE_MATCH_NEW_GROUP(num, ra, rb) \ - do { \ - stack.pushNewFrame((ra), (rb), RMATCH_WHERE(num)); \ - startNewGroup(stack.currentFrame); \ - RECURSIVE_MATCH_COMMON(num) \ - } while (0) - -#define RRETURN goto RRETURN_LABEL - -#define RRETURN_NO_MATCH do { isMatch = false; RRETURN; } while (0) - -/************************************************* -* Match from current position * -*************************************************/ - -/* On entry instructionPtr points to the first opcode, and subjectPtr to the first character -in the subject string, while substringStart holds the value of subjectPtr at the start of the -last bracketed group - used for breaking infinite loops matching zero-length -strings. This function is called recursively in many circumstances. Whenever it -returns a negative (error) response, the outer match() call must also return the -same response. - -Arguments: - subjectPtr pointer in subject - instructionPtr position in code - offsetTop current top pointer - md pointer to "static" info for the match - -Returns: 1 if matched ) these values are >= 0 - 0 if failed to match ) - a negative error value if aborted by an error condition - (e.g. stopped by repeated call or recursion limit) -*/ - -static const unsigned numFramesOnStack = 16; - -struct MatchStack { - MatchStack() - : framesEnd(frames + numFramesOnStack) - , currentFrame(frames) - , size(1) // match() creates accesses the first frame w/o calling pushNewFrame - { - ASSERT((sizeof(frames) / sizeof(frames[0])) == numFramesOnStack); - } - - MatchFrame frames[numFramesOnStack]; - MatchFrame* framesEnd; - MatchFrame* currentFrame; - unsigned size; - - inline bool canUseStackBufferForNextFrame() - { - return size < numFramesOnStack; - } - - inline MatchFrame* allocateNextFrame() - { - if (canUseStackBufferForNextFrame()) - return currentFrame + 1; - return new MatchFrame; - } - - inline void pushNewFrame(const unsigned char* instructionPtr, BracketChainNode* bracketChain, ReturnLocation returnLocation) - { - MatchFrame* newframe = allocateNextFrame(); - newframe->previousFrame = currentFrame; - - newframe->args.subjectPtr = currentFrame->args.subjectPtr; - newframe->args.offsetTop = currentFrame->args.offsetTop; - newframe->args.instructionPtr = instructionPtr; - newframe->args.bracketChain = bracketChain; - newframe->returnLocation = returnLocation; - size++; - - currentFrame = newframe; - } - - inline void popCurrentFrame() - { - MatchFrame* oldFrame = currentFrame; - currentFrame = currentFrame->previousFrame; - if (size > numFramesOnStack) - delete oldFrame; - size--; - } - - void popAllFrames() - { - while (size) - popCurrentFrame(); - } -}; - -static int matchError(int errorCode, MatchStack& stack) -{ - stack.popAllFrames(); - return errorCode; -} - -/* Get the next UTF-8 character, not advancing the pointer, incrementing length - if there are extra bytes. This is called when we know we are in UTF-8 mode. */ - -static inline void getUTF8CharAndIncrementLength(int& c, const unsigned char* subjectPtr, int& len) -{ - c = *subjectPtr; - if ((c & 0xc0) == 0xc0) { - int gcaa = kjs_pcre_utf8_table4[c & 0x3f]; /* Number of additional bytes */ - int gcss = 6 * gcaa; - c = (c & kjs_pcre_utf8_table3[gcaa]) << gcss; - for (int gcii = 1; gcii <= gcaa; gcii++) { - gcss -= 6; - c |= (subjectPtr[gcii] & 0x3f) << gcss; - } - len += gcaa; - } -} - -static inline void startNewGroup(MatchFrame* currentFrame) -{ - /* At the start of a bracketed group, add the current subject pointer to the - stack of such pointers, to be re-instated at the end of the group when we hit - the closing ket. When match() is called in other circumstances, we don't add to - this stack. */ - - currentFrame->locals.bracketChainNode.previousBracket = currentFrame->args.bracketChain; - currentFrame->locals.bracketChainNode.bracketStart = currentFrame->args.subjectPtr; - currentFrame->args.bracketChain = ¤tFrame->locals.bracketChainNode; -} - -// FIXME: "minimize" means "not greedy", we should invert the callers to ask for "greedy" to be less confusing -static inline void repeatInformationFromInstructionOffset(short instructionOffset, bool& minimize, int& minimumRepeats, int& maximumRepeats) -{ - // Instruction offsets are based off of OP_CRSTAR, OP_STAR, OP_TYPESTAR, OP_NOTSTAR - static const char minimumRepeatsFromInstructionOffset[] = { 0, 0, 1, 1, 0, 0 }; - static const int maximumRepeatsFromInstructionOffset[] = { INT_MAX, INT_MAX, INT_MAX, INT_MAX, 1, 1 }; - - ASSERT(instructionOffset >= 0); - ASSERT(instructionOffset <= (OP_CRMINQUERY - OP_CRSTAR)); - - minimize = (instructionOffset & 1); // this assumes ordering: Instruction, MinimizeInstruction, Instruction2, MinimizeInstruction2 - minimumRepeats = minimumRepeatsFromInstructionOffset[instructionOffset]; - maximumRepeats = maximumRepeatsFromInstructionOffset[instructionOffset]; -} - -static int match(const UChar* subjectPtr, const unsigned char* instructionPtr, int offsetTop, MatchData& md) -{ - bool isMatch = false; - int min; - bool minimize = false; /* Initialization not really needed, but some compilers think so. */ - unsigned remainingMatchCount = matchLimit; - - MatchStack stack; - - /* The opcode jump table. */ -#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP -#define EMIT_JUMP_TABLE_ENTRY(opcode) &&LABEL_OP_##opcode, - static void* opcodeJumpTable[256] = { FOR_EACH_OPCODE(EMIT_JUMP_TABLE_ENTRY) }; -#undef EMIT_JUMP_TABLE_ENTRY -#endif - - /* One-time setup of the opcode jump table. */ -#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP - for (int i = 255; !opcodeJumpTable[i]; i--) - opcodeJumpTable[i] = &&CAPTURING_BRACKET; -#endif - -#ifdef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION - // Shark shows this as a hot line - // Using a static const here makes this line disappear, but makes later access hotter (not sure why) - stack.currentFrame->returnLocation = &&RETURN; -#else - stack.currentFrame->returnLocation = 0; -#endif - stack.currentFrame->args.subjectPtr = subjectPtr; - stack.currentFrame->args.instructionPtr = instructionPtr; - stack.currentFrame->args.offsetTop = offsetTop; - stack.currentFrame->args.bracketChain = 0; - startNewGroup(stack.currentFrame); - - /* This is where control jumps back to to effect "recursion" */ - -RECURSE: - if (!--remainingMatchCount) - return matchError(JSRegExpErrorHitLimit, stack); - - /* Now start processing the operations. */ - -#ifndef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP - while (true) -#endif - { - -#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP -#define BEGIN_OPCODE(opcode) LABEL_OP_##opcode -#define NEXT_OPCODE goto *opcodeJumpTable[*stack.currentFrame->args.instructionPtr] -#else -#define BEGIN_OPCODE(opcode) case OP_##opcode -#define NEXT_OPCODE continue -#endif - -#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP - NEXT_OPCODE; -#else - switch (*stack.currentFrame->args.instructionPtr) -#endif - { - /* Non-capturing bracket: optimized */ - - BEGIN_OPCODE(BRA): - NON_CAPTURING_BRACKET: - DPRINTF(("start bracket 0\n")); - do { - RECURSIVE_MATCH_NEW_GROUP(2, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); - } while (*stack.currentFrame->args.instructionPtr == OP_ALT); - DPRINTF(("bracket 0 failed\n")); - RRETURN; - - /* Skip over large extraction number data if encountered. */ - - BEGIN_OPCODE(BRANUMBER): - stack.currentFrame->args.instructionPtr += 3; - NEXT_OPCODE; - - /* End of the pattern. */ - - BEGIN_OPCODE(END): - md.endMatchPtr = stack.currentFrame->args.subjectPtr; /* Record where we ended */ - md.endOffsetTop = stack.currentFrame->args.offsetTop; /* and how many extracts were taken */ - isMatch = true; - RRETURN; - - /* Assertion brackets. Check the alternative branches in turn - the - matching won't pass the KET for an assertion. If any one branch matches, - the assertion is true. Lookbehind assertions have an OP_REVERSE item at the - start of each branch to move the current point backwards, so the code at - this level is identical to the lookahead case. */ - - BEGIN_OPCODE(ASSERT): - do { - RECURSIVE_MATCH_NEW_GROUP(6, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, NULL); - if (isMatch) - break; - stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); - } while (*stack.currentFrame->args.instructionPtr == OP_ALT); - if (*stack.currentFrame->args.instructionPtr == OP_KET) - RRETURN_NO_MATCH; - - /* Continue from after the assertion, updating the offsets high water - mark, since extracts may have been taken during the assertion. */ - - advanceToEndOfBracket(stack.currentFrame->args.instructionPtr); - stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; - stack.currentFrame->args.offsetTop = md.endOffsetTop; - NEXT_OPCODE; - - /* Negative assertion: all branches must fail to match */ - - BEGIN_OPCODE(ASSERT_NOT): - do { - RECURSIVE_MATCH_NEW_GROUP(7, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, NULL); - if (isMatch) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); - } while (*stack.currentFrame->args.instructionPtr == OP_ALT); - - stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; - NEXT_OPCODE; - - /* An alternation is the end of a branch; scan along to find the end of the - bracketed group and go to there. */ - - BEGIN_OPCODE(ALT): - advanceToEndOfBracket(stack.currentFrame->args.instructionPtr); - NEXT_OPCODE; - - /* BRAZERO and BRAMINZERO occur just before a bracket group, indicating - that it may occur zero times. It may repeat infinitely, or not at all - - i.e. it could be ()* or ()? in the pattern. Brackets with fixed upper - repeat limits are compiled as a number of copies, with the optional ones - preceded by BRAZERO or BRAMINZERO. */ - - BEGIN_OPCODE(BRAZERO): { - stack.currentFrame->locals.startOfRepeatingBracket = stack.currentFrame->args.instructionPtr + 1; - RECURSIVE_MATCH_NEW_GROUP(14, stack.currentFrame->locals.startOfRepeatingBracket, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - advanceToEndOfBracket(stack.currentFrame->locals.startOfRepeatingBracket); - stack.currentFrame->args.instructionPtr = stack.currentFrame->locals.startOfRepeatingBracket + 1 + LINK_SIZE; - NEXT_OPCODE; - } - - BEGIN_OPCODE(BRAMINZERO): { - stack.currentFrame->locals.startOfRepeatingBracket = stack.currentFrame->args.instructionPtr + 1; - advanceToEndOfBracket(stack.currentFrame->locals.startOfRepeatingBracket); - RECURSIVE_MATCH_NEW_GROUP(15, stack.currentFrame->locals.startOfRepeatingBracket + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - } - - /* End of a group, repeated or non-repeating. If we are at the end of - an assertion "group", stop matching and return 1, but record the - current high water mark for use by positive assertions. Do this also - for the "once" (not-backup up) groups. */ - - BEGIN_OPCODE(KET): - BEGIN_OPCODE(KETRMIN): - BEGIN_OPCODE(KETRMAX): - stack.currentFrame->locals.instructionPtrAtStartOfOnce = stack.currentFrame->args.instructionPtr - getLinkValue(stack.currentFrame->args.instructionPtr + 1); - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.bracketChain->bracketStart; - - /* Back up the stack of bracket start pointers. */ - - stack.currentFrame->args.bracketChain = stack.currentFrame->args.bracketChain->previousBracket; - - if (*stack.currentFrame->locals.instructionPtrAtStartOfOnce == OP_ASSERT || *stack.currentFrame->locals.instructionPtrAtStartOfOnce == OP_ASSERT_NOT) { - md.endOffsetTop = stack.currentFrame->args.offsetTop; - isMatch = true; - RRETURN; - } - - /* In all other cases except a conditional group we have to check the - group number back at the start and if necessary complete handling an - extraction by setting the offsets and bumping the high water mark. */ - - stack.currentFrame->locals.number = *stack.currentFrame->locals.instructionPtrAtStartOfOnce - OP_BRA; - - /* For extended extraction brackets (large number), we have to fish out - the number from a dummy opcode at the start. */ - - if (stack.currentFrame->locals.number > EXTRACT_BASIC_MAX) - stack.currentFrame->locals.number = get2ByteValue(stack.currentFrame->locals.instructionPtrAtStartOfOnce + 2 + LINK_SIZE); - stack.currentFrame->locals.offset = stack.currentFrame->locals.number << 1; - -#ifdef DEBUG - printf("end bracket %d", stack.currentFrame->locals.number); - printf("\n"); -#endif - - /* Test for a numbered group. This includes groups called as a result - of recursion. Note that whole-pattern recursion is coded as a recurse - into group 0, so it won't be picked up here. Instead, we catch it when - the OP_END is reached. */ - - if (stack.currentFrame->locals.number > 0) { - if (stack.currentFrame->locals.offset >= md.offsetMax) - md.offsetOverflow = true; - else { - md.offsetVector[stack.currentFrame->locals.offset] = - md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number]; - md.offsetVector[stack.currentFrame->locals.offset+1] = stack.currentFrame->args.subjectPtr - md.startSubject; - if (stack.currentFrame->args.offsetTop <= stack.currentFrame->locals.offset) - stack.currentFrame->args.offsetTop = stack.currentFrame->locals.offset + 2; - } - } - - /* For a non-repeating ket, just continue at this level. This also - happens for a repeating ket if no characters were matched in the group. - This is the forcible breaking of infinite loops as implemented in Perl - 5.005. If there is an options reset, it will get obeyed in the normal - course of events. */ - - if (*stack.currentFrame->args.instructionPtr == OP_KET || stack.currentFrame->args.subjectPtr == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { - stack.currentFrame->args.instructionPtr += 1 + LINK_SIZE; - NEXT_OPCODE; - } - - /* The repeating kets try the rest of the pattern or restart from the - preceding bracket, in the appropriate order. */ - - if (*stack.currentFrame->args.instructionPtr == OP_KETRMIN) { - RECURSIVE_MATCH(16, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - RECURSIVE_MATCH_NEW_GROUP(17, stack.currentFrame->locals.instructionPtrAtStartOfOnce, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - } else { /* OP_KETRMAX */ - RECURSIVE_MATCH_NEW_GROUP(18, stack.currentFrame->locals.instructionPtrAtStartOfOnce, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - RECURSIVE_MATCH(19, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - } - RRETURN; - - /* Start of subject. */ - - BEGIN_OPCODE(CIRC): - if (stack.currentFrame->args.subjectPtr != md.startSubject) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - /* After internal newline if multiline. */ - - BEGIN_OPCODE(BOL): - if (stack.currentFrame->args.subjectPtr != md.startSubject && !isNewline(stack.currentFrame->args.subjectPtr[-1])) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - /* End of subject. */ - - BEGIN_OPCODE(DOLL): - if (stack.currentFrame->args.subjectPtr < md.endSubject) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - /* Before internal newline if multiline. */ - - BEGIN_OPCODE(EOL): - if (stack.currentFrame->args.subjectPtr < md.endSubject && !isNewline(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - /* Word boundary assertions */ - - BEGIN_OPCODE(NOT_WORD_BOUNDARY): - BEGIN_OPCODE(WORD_BOUNDARY): { - bool currentCharIsWordChar = false; - bool previousCharIsWordChar = false; - - if (stack.currentFrame->args.subjectPtr > md.startSubject) - previousCharIsWordChar = isWordChar(stack.currentFrame->args.subjectPtr[-1]); - if (stack.currentFrame->args.subjectPtr < md.endSubject) - currentCharIsWordChar = isWordChar(*stack.currentFrame->args.subjectPtr); - - /* Now see if the situation is what we want */ - bool wordBoundaryDesired = (*stack.currentFrame->args.instructionPtr++ == OP_WORD_BOUNDARY); - if (wordBoundaryDesired ? currentCharIsWordChar == previousCharIsWordChar : currentCharIsWordChar != previousCharIsWordChar) - RRETURN_NO_MATCH; - NEXT_OPCODE; - } - - /* Match a single character type; inline for speed */ - - BEGIN_OPCODE(NOT_NEWLINE): - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (isNewline(*stack.currentFrame->args.subjectPtr++)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - BEGIN_OPCODE(NOT_DIGIT): - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (isASCIIDigit(*stack.currentFrame->args.subjectPtr++)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - BEGIN_OPCODE(DIGIT): - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (!isASCIIDigit(*stack.currentFrame->args.subjectPtr++)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - BEGIN_OPCODE(NOT_WHITESPACE): - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (isSpaceChar(*stack.currentFrame->args.subjectPtr++)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - BEGIN_OPCODE(WHITESPACE): - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (!isSpaceChar(*stack.currentFrame->args.subjectPtr++)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - BEGIN_OPCODE(NOT_WORDCHAR): - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (isWordChar(*stack.currentFrame->args.subjectPtr++)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - BEGIN_OPCODE(WORDCHAR): - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (!isWordChar(*stack.currentFrame->args.subjectPtr++)) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr++; - NEXT_OPCODE; - - /* Match a back reference, possibly repeatedly. Look past the end of the - item to see if there is repeat information following. The code is similar - to that for character classes, but repeated for efficiency. Then obey - similar code to character type repeats - written out again for speed. - However, if the referenced string is the empty string, always treat - it as matched, any number of times (otherwise there could be infinite - loops). */ - - BEGIN_OPCODE(REF): - stack.currentFrame->locals.offset = get2ByteValue(stack.currentFrame->args.instructionPtr + 1) << 1; /* Doubled ref number */ - stack.currentFrame->args.instructionPtr += 3; /* Advance past item */ - - /* If the reference is unset, set the length to be longer than the amount - of subject left; this ensures that every attempt at a match fails. We - can't just fail here, because of the possibility of quantifiers with zero - minima. */ - - if (stack.currentFrame->locals.offset >= stack.currentFrame->args.offsetTop || md.offsetVector[stack.currentFrame->locals.offset] < 0) - stack.currentFrame->locals.length = 0; - else - stack.currentFrame->locals.length = md.offsetVector[stack.currentFrame->locals.offset+1] - md.offsetVector[stack.currentFrame->locals.offset]; - - /* Set up for repetition, or handle the non-repeated case */ - - switch (*stack.currentFrame->args.instructionPtr) { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); - min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); - if (stack.currentFrame->locals.max == 0) - stack.currentFrame->locals.max = INT_MAX; - stack.currentFrame->args.instructionPtr += 5; - break; - - default: /* No repeat follows */ - if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) - RRETURN_NO_MATCH; - stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; - NEXT_OPCODE; - } - - /* If the length of the reference is zero, just continue with the - main loop. */ - - if (stack.currentFrame->locals.length == 0) - NEXT_OPCODE; - - /* First, ensure the minimum number of matches are present. */ - - for (int i = 1; i <= min; i++) { - if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) - RRETURN_NO_MATCH; - stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; - } - - /* If min = max, continue at the same level without recursion. - They are not both allowed to be zero. */ - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - /* If minimizing, keep trying and advancing the pointer */ - - if (minimize) { - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(20, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || !matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) - RRETURN; - stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; - } - /* Control never reaches here */ - } - - /* If maximizing, find the longest string and work backwards */ - - else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (!matchRef(stack.currentFrame->locals.offset, stack.currentFrame->args.subjectPtr, stack.currentFrame->locals.length, md)) - break; - stack.currentFrame->args.subjectPtr += stack.currentFrame->locals.length; - } - while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { - RECURSIVE_MATCH(21, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - stack.currentFrame->args.subjectPtr -= stack.currentFrame->locals.length; - } - RRETURN_NO_MATCH; - } - /* Control never reaches here */ - - /* Match a bit-mapped character class, possibly repeatedly. This op code is - used when all the characters in the class have values in the range 0-255, - and either the matching is caseful, or the characters are in the range - 0-127 when UTF-8 processing is enabled. The only difference between - OP_CLASS and OP_NCLASS occurs when a data character outside the range is - encountered. - - First, look past the end of the item to see if there is repeat information - following. Then obey similar code to character type repeats - written out - again for speed. */ - - BEGIN_OPCODE(NCLASS): - BEGIN_OPCODE(CLASS): - stack.currentFrame->locals.data = stack.currentFrame->args.instructionPtr + 1; /* Save for matching */ - stack.currentFrame->args.instructionPtr += 33; /* Advance past the item */ - - switch (*stack.currentFrame->args.instructionPtr) { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); - min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); - if (stack.currentFrame->locals.max == 0) - stack.currentFrame->locals.max = INT_MAX; - stack.currentFrame->args.instructionPtr += 5; - break; - - default: /* No repeat follows */ - min = stack.currentFrame->locals.max = 1; - break; - } - - /* First, ensure the minimum number of matches are present. */ - - for (int i = 1; i <= min; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - int c = *stack.currentFrame->args.subjectPtr++; - if (c > 255) { - if (stack.currentFrame->locals.data[-1] == OP_CLASS) - RRETURN_NO_MATCH; - } else { - if (!(stack.currentFrame->locals.data[c / 8] & (1 << (c & 7)))) - RRETURN_NO_MATCH; - } - } - - /* If max == min we can continue with the main loop without the - need to recurse. */ - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ - if (minimize) { - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(22, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN; - int c = *stack.currentFrame->args.subjectPtr++; - if (c > 255) { - if (stack.currentFrame->locals.data[-1] == OP_CLASS) - RRETURN; - } else { - if ((stack.currentFrame->locals.data[c/8] & (1 << (c&7))) == 0) - RRETURN; - } - } - /* Control never reaches here */ - } - /* If maximizing, find the longest possible run, then work backwards. */ - else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; - - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (c > 255) { - if (stack.currentFrame->locals.data[-1] == OP_CLASS) - break; - } else { - if (!(stack.currentFrame->locals.data[c / 8] & (1 << (c & 7)))) - break; - } - ++stack.currentFrame->args.subjectPtr; - } - for (;;) { - RECURSIVE_MATCH(24, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) - break; /* Stop if tried at original pos */ - } - - RRETURN; - } - /* Control never reaches here */ - - /* Match an extended character class. */ - - BEGIN_OPCODE(XCLASS): - stack.currentFrame->locals.data = stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE; /* Save for matching */ - stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); /* Advance past the item */ - - switch (*stack.currentFrame->args.instructionPtr) { - case OP_CRSTAR: - case OP_CRMINSTAR: - case OP_CRPLUS: - case OP_CRMINPLUS: - case OP_CRQUERY: - case OP_CRMINQUERY: - repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_CRSTAR, minimize, min, stack.currentFrame->locals.max); - break; - - case OP_CRRANGE: - case OP_CRMINRANGE: - minimize = (*stack.currentFrame->args.instructionPtr == OP_CRMINRANGE); - min = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 3); - if (stack.currentFrame->locals.max == 0) - stack.currentFrame->locals.max = INT_MAX; - stack.currentFrame->args.instructionPtr += 5; - break; - - default: /* No repeat follows */ - min = stack.currentFrame->locals.max = 1; - } - - /* First, ensure the minimum number of matches are present. */ - - for (int i = 1; i <= min; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - int c = *stack.currentFrame->args.subjectPtr++; - if (!kjs_pcre_xclass(c, stack.currentFrame->locals.data)) - RRETURN_NO_MATCH; - } - - /* If max == min we can continue with the main loop without the - need to recurse. */ - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - /* If minimizing, keep testing the rest of the expression and advancing - the pointer while it matches the class. */ - - if (minimize) { - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(26, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN; - int c = *stack.currentFrame->args.subjectPtr++; - if (!kjs_pcre_xclass(c, stack.currentFrame->locals.data)) - RRETURN; - } - /* Control never reaches here */ - } - - /* If maximizing, find the longest possible run, then work backwards. */ - - else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (!kjs_pcre_xclass(c, stack.currentFrame->locals.data)) - break; - ++stack.currentFrame->args.subjectPtr; - } - for(;;) { - RECURSIVE_MATCH(27, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) - break; /* Stop if tried at original pos */ - } - RRETURN; - } - - /* Control never reaches here */ - - /* Match a single character, casefully */ - - BEGIN_OPCODE(CHAR): - stack.currentFrame->locals.length = 1; - stack.currentFrame->args.instructionPtr++; - getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); - stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - if (stack.currentFrame->locals.fc != *stack.currentFrame->args.subjectPtr++) - RRETURN_NO_MATCH; - NEXT_OPCODE; - - /* Match a single character, caselessly */ - - BEGIN_OPCODE(CHAR_IGNORING_CASE): { - stack.currentFrame->locals.length = 1; - stack.currentFrame->args.instructionPtr++; - getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); - stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - int dc = *stack.currentFrame->args.subjectPtr++; - if (stack.currentFrame->locals.fc != dc && kjs_pcre_ucp_othercase(stack.currentFrame->locals.fc) != dc) - RRETURN_NO_MATCH; - NEXT_OPCODE; - } - - /* Match a single ASCII character. */ - - BEGIN_OPCODE(ASCII_CHAR): - if (md.endSubject == stack.currentFrame->args.subjectPtr) - RRETURN_NO_MATCH; - if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->args.instructionPtr[1]) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - stack.currentFrame->args.instructionPtr += 2; - NEXT_OPCODE; - - /* Match one of two cases of an ASCII letter. */ - - BEGIN_OPCODE(ASCII_LETTER_IGNORING_CASE): - if (md.endSubject == stack.currentFrame->args.subjectPtr) - RRETURN_NO_MATCH; - if ((*stack.currentFrame->args.subjectPtr | 0x20) != stack.currentFrame->args.instructionPtr[1]) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - stack.currentFrame->args.instructionPtr += 2; - NEXT_OPCODE; - - /* Match a single character repeatedly; different opcodes share code. */ - - BEGIN_OPCODE(EXACT): - min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - minimize = false; - stack.currentFrame->args.instructionPtr += 3; - goto REPEATCHAR; - - BEGIN_OPCODE(UPTO): - BEGIN_OPCODE(MINUPTO): - min = 0; - stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - minimize = *stack.currentFrame->args.instructionPtr == OP_MINUPTO; - stack.currentFrame->args.instructionPtr += 3; - goto REPEATCHAR; - - BEGIN_OPCODE(STAR): - BEGIN_OPCODE(MINSTAR): - BEGIN_OPCODE(PLUS): - BEGIN_OPCODE(MINPLUS): - BEGIN_OPCODE(QUERY): - BEGIN_OPCODE(MINQUERY): - repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_STAR, minimize, min, stack.currentFrame->locals.max); - - /* Common code for all repeated single-character matches. We can give - up quickly if there are fewer than the minimum number of characters left in - the subject. */ - - REPEATCHAR: - - stack.currentFrame->locals.length = 1; - getUTF8CharAndIncrementLength(stack.currentFrame->locals.fc, stack.currentFrame->args.instructionPtr, stack.currentFrame->locals.length); - if (min * (stack.currentFrame->locals.fc > 0xFFFF ? 2 : 1) > md.endSubject - stack.currentFrame->args.subjectPtr) - RRETURN_NO_MATCH; - stack.currentFrame->args.instructionPtr += stack.currentFrame->locals.length; - - if (stack.currentFrame->locals.fc <= 0xFFFF) { - int othercase = md.ignoreCase ? kjs_pcre_ucp_othercase(stack.currentFrame->locals.fc) : -1; - - for (int i = 1; i <= min; i++) { - if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != othercase) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - if (minimize) { - stack.currentFrame->locals.repeatOthercase = othercase; - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(28, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN; - if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.repeatOthercase) - RRETURN; - ++stack.currentFrame->args.subjectPtr; - } - /* Control never reaches here */ - } else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc && *stack.currentFrame->args.subjectPtr != othercase) - break; - ++stack.currentFrame->args.subjectPtr; - } - while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { - RECURSIVE_MATCH(29, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - --stack.currentFrame->args.subjectPtr; - } - RRETURN_NO_MATCH; - } - /* Control never reaches here */ - } else { - /* No case on surrogate pairs, so no need to bother with "othercase". */ - - for (int i = 1; i <= min; i++) { - if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) - RRETURN_NO_MATCH; - stack.currentFrame->args.subjectPtr += 2; - } - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - if (minimize) { - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(30, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN; - if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) - RRETURN; - stack.currentFrame->args.subjectPtr += 2; - } - /* Control never reaches here */ - } else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr > md.endSubject - 2) - break; - if (*stack.currentFrame->args.subjectPtr != stack.currentFrame->locals.fc) - break; - stack.currentFrame->args.subjectPtr += 2; - } - while (stack.currentFrame->args.subjectPtr >= stack.currentFrame->locals.subjectPtrAtStartOfInstruction) { - RECURSIVE_MATCH(31, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - stack.currentFrame->args.subjectPtr -= 2; - } - RRETURN_NO_MATCH; - } - /* Control never reaches here */ - } - /* Control never reaches here */ - - /* Match a negated single one-byte character. */ - - BEGIN_OPCODE(NOT): { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN_NO_MATCH; - int b = stack.currentFrame->args.instructionPtr[1]; - int c = *stack.currentFrame->args.subjectPtr++; - stack.currentFrame->args.instructionPtr += 2; - if (md.ignoreCase) { - if (c < 128) - c = toLowerCase(c); - if (toLowerCase(b) == c) - RRETURN_NO_MATCH; - } else { - if (b == c) - RRETURN_NO_MATCH; - } - NEXT_OPCODE; - } - - /* Match a negated single one-byte character repeatedly. This is almost a - repeat of the code for a repeated single character, but I haven't found a - nice way of commoning these up that doesn't require a test of the - positive/negative option for each character match. Maybe that wouldn't add - very much to the time taken, but character matching *is* what this is all - about... */ - - BEGIN_OPCODE(NOTEXACT): - min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - minimize = false; - stack.currentFrame->args.instructionPtr += 3; - goto REPEATNOTCHAR; - - BEGIN_OPCODE(NOTUPTO): - BEGIN_OPCODE(NOTMINUPTO): - min = 0; - stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - minimize = *stack.currentFrame->args.instructionPtr == OP_NOTMINUPTO; - stack.currentFrame->args.instructionPtr += 3; - goto REPEATNOTCHAR; - - BEGIN_OPCODE(NOTSTAR): - BEGIN_OPCODE(NOTMINSTAR): - BEGIN_OPCODE(NOTPLUS): - BEGIN_OPCODE(NOTMINPLUS): - BEGIN_OPCODE(NOTQUERY): - BEGIN_OPCODE(NOTMINQUERY): - repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_NOTSTAR, minimize, min, stack.currentFrame->locals.max); - - /* Common code for all repeated single-byte matches. We can give up quickly - if there are fewer than the minimum number of bytes left in the - subject. */ - - REPEATNOTCHAR: - if (min > md.endSubject - stack.currentFrame->args.subjectPtr) - RRETURN_NO_MATCH; - stack.currentFrame->locals.fc = *stack.currentFrame->args.instructionPtr++; - - /* The code is duplicated for the caseless and caseful cases, for speed, - since matching characters is likely to be quite common. First, ensure the - minimum number of matches are present. If min = max, continue at the same - level without recursing. Otherwise, if minimizing, keep trying the rest of - the expression and advancing one matching character if failing, up to the - maximum. Alternatively, if maximizing, find the maximum number of - characters and work backwards. */ - - DPRINTF(("negative matching %c{%d,%d}\n", stack.currentFrame->locals.fc, min, stack.currentFrame->locals.max)); - - if (md.ignoreCase) { - if (stack.currentFrame->locals.fc < 128) - stack.currentFrame->locals.fc = toLowerCase(stack.currentFrame->locals.fc); - - for (int i = 1; i <= min; i++) { - int d = *stack.currentFrame->args.subjectPtr++; - if (d < 128) - d = toLowerCase(d); - if (stack.currentFrame->locals.fc == d) - RRETURN_NO_MATCH; - } - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - if (minimize) { - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(38, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - int d = *stack.currentFrame->args.subjectPtr++; - if (d < 128) - d = toLowerCase(d); - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject || stack.currentFrame->locals.fc == d) - RRETURN; - } - /* Control never reaches here */ - } - - /* Maximize case */ - - else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; - - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int d = *stack.currentFrame->args.subjectPtr; - if (d < 128) - d = toLowerCase(d); - if (stack.currentFrame->locals.fc == d) - break; - ++stack.currentFrame->args.subjectPtr; - } - for (;;) { - RECURSIVE_MATCH(40, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) - break; /* Stop if tried at original pos */ - } - - RRETURN; - } - /* Control never reaches here */ - } - - /* Caseful comparisons */ - - else { - for (int i = 1; i <= min; i++) { - int d = *stack.currentFrame->args.subjectPtr++; - if (stack.currentFrame->locals.fc == d) - RRETURN_NO_MATCH; - } - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - if (minimize) { - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(42, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - int d = *stack.currentFrame->args.subjectPtr++; - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject || stack.currentFrame->locals.fc == d) - RRETURN; - } - /* Control never reaches here */ - } - - /* Maximize case */ - - else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; - - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int d = *stack.currentFrame->args.subjectPtr; - if (stack.currentFrame->locals.fc == d) - break; - ++stack.currentFrame->args.subjectPtr; - } - for (;;) { - RECURSIVE_MATCH(44, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) - break; /* Stop if tried at original pos */ - } - - RRETURN; - } - } - /* Control never reaches here */ - - /* Match a single character type repeatedly; several different opcodes - share code. This is very similar to the code for single characters, but we - repeat it in the interests of efficiency. */ - - BEGIN_OPCODE(TYPEEXACT): - min = stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - minimize = true; - stack.currentFrame->args.instructionPtr += 3; - goto REPEATTYPE; - - BEGIN_OPCODE(TYPEUPTO): - BEGIN_OPCODE(TYPEMINUPTO): - min = 0; - stack.currentFrame->locals.max = get2ByteValue(stack.currentFrame->args.instructionPtr + 1); - minimize = *stack.currentFrame->args.instructionPtr == OP_TYPEMINUPTO; - stack.currentFrame->args.instructionPtr += 3; - goto REPEATTYPE; - - BEGIN_OPCODE(TYPESTAR): - BEGIN_OPCODE(TYPEMINSTAR): - BEGIN_OPCODE(TYPEPLUS): - BEGIN_OPCODE(TYPEMINPLUS): - BEGIN_OPCODE(TYPEQUERY): - BEGIN_OPCODE(TYPEMINQUERY): - repeatInformationFromInstructionOffset(*stack.currentFrame->args.instructionPtr++ - OP_TYPESTAR, minimize, min, stack.currentFrame->locals.max); - - /* Common code for all repeated single character type matches. Note that - in UTF-8 mode, '.' matches a character of any length, but for the other - character types, the valid characters are all one-byte long. */ - - REPEATTYPE: - stack.currentFrame->locals.ctype = *stack.currentFrame->args.instructionPtr++; /* Code for the character type */ - - /* First, ensure the minimum number of matches are present. Use inline - code for maximizing the speed, and do the type test once at the start - (i.e. keep it out of the loop). Also we can test that there are at least - the minimum number of characters before we start. */ - - if (min > md.endSubject - stack.currentFrame->args.subjectPtr) - RRETURN_NO_MATCH; - if (min > 0) { - switch (stack.currentFrame->locals.ctype) { - case OP_NOT_NEWLINE: - for (int i = 1; i <= min; i++) { - if (isNewline(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_NOT_DIGIT: - for (int i = 1; i <= min; i++) { - if (isASCIIDigit(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_DIGIT: - for (int i = 1; i <= min; i++) { - if (!isASCIIDigit(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_NOT_WHITESPACE: - for (int i = 1; i <= min; i++) { - if (isSpaceChar(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_WHITESPACE: - for (int i = 1; i <= min; i++) { - if (!isSpaceChar(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_NOT_WORDCHAR: - for (int i = 1; i <= min; i++) { - if (isWordChar(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_WORDCHAR: - for (int i = 1; i <= min; i++) { - if (!isWordChar(*stack.currentFrame->args.subjectPtr)) - RRETURN_NO_MATCH; - ++stack.currentFrame->args.subjectPtr; - } - break; - - default: - ASSERT_NOT_REACHED(); - return matchError(JSRegExpErrorInternal, stack); - } /* End switch(stack.currentFrame->locals.ctype) */ - } - - /* If min = max, continue at the same level without recursing */ - - if (min == stack.currentFrame->locals.max) - NEXT_OPCODE; - - /* If minimizing, we have to test the rest of the pattern before each - subsequent match. */ - - if (minimize) { - for (stack.currentFrame->locals.fi = min;; stack.currentFrame->locals.fi++) { - RECURSIVE_MATCH(48, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->locals.fi >= stack.currentFrame->locals.max || stack.currentFrame->args.subjectPtr >= md.endSubject) - RRETURN; - - int c = *stack.currentFrame->args.subjectPtr++; - switch (stack.currentFrame->locals.ctype) { - case OP_NOT_NEWLINE: - if (isNewline(c)) - RRETURN; - break; - - case OP_NOT_DIGIT: - if (isASCIIDigit(c)) - RRETURN; - break; - - case OP_DIGIT: - if (!isASCIIDigit(c)) - RRETURN; - break; - - case OP_NOT_WHITESPACE: - if (isSpaceChar(c)) - RRETURN; - break; - - case OP_WHITESPACE: - if (!isSpaceChar(c)) - RRETURN; - break; - - case OP_NOT_WORDCHAR: - if (isWordChar(c)) - RRETURN; - break; - - case OP_WORDCHAR: - if (!isWordChar(c)) - RRETURN; - break; - - default: - ASSERT_NOT_REACHED(); - return matchError(JSRegExpErrorInternal, stack); - } - } - /* Control never reaches here */ - } - - /* If maximizing it is worth using inline code for speed, doing the type - test once at the start (i.e. keep it out of the loop). */ - - else { - stack.currentFrame->locals.subjectPtrAtStartOfInstruction = stack.currentFrame->args.subjectPtr; /* Remember where we started */ - - switch (stack.currentFrame->locals.ctype) { - case OP_NOT_NEWLINE: - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject || isNewline(*stack.currentFrame->args.subjectPtr)) - break; - stack.currentFrame->args.subjectPtr++; - } - break; - - case OP_NOT_DIGIT: - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (isASCIIDigit(c)) - break; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_DIGIT: - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (!isASCIIDigit(c)) - break; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_NOT_WHITESPACE: - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (isSpaceChar(c)) - break; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_WHITESPACE: - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (!isSpaceChar(c)) - break; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_NOT_WORDCHAR: - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (isWordChar(c)) - break; - ++stack.currentFrame->args.subjectPtr; - } - break; - - case OP_WORDCHAR: - for (int i = min; i < stack.currentFrame->locals.max; i++) { - if (stack.currentFrame->args.subjectPtr >= md.endSubject) - break; - int c = *stack.currentFrame->args.subjectPtr; - if (!isWordChar(c)) - break; - ++stack.currentFrame->args.subjectPtr; - } - break; - - default: - ASSERT_NOT_REACHED(); - return matchError(JSRegExpErrorInternal, stack); - } - - /* stack.currentFrame->args.subjectPtr is now past the end of the maximum run */ - - for (;;) { - RECURSIVE_MATCH(52, stack.currentFrame->args.instructionPtr, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - if (stack.currentFrame->args.subjectPtr-- == stack.currentFrame->locals.subjectPtrAtStartOfInstruction) - break; /* Stop if tried at original pos */ - } - - /* Get here if we can't make it match with any permitted repetitions */ - - RRETURN; - } - /* Control never reaches here */ - - BEGIN_OPCODE(CRMINPLUS): - BEGIN_OPCODE(CRMINQUERY): - BEGIN_OPCODE(CRMINRANGE): - BEGIN_OPCODE(CRMINSTAR): - BEGIN_OPCODE(CRPLUS): - BEGIN_OPCODE(CRQUERY): - BEGIN_OPCODE(CRRANGE): - BEGIN_OPCODE(CRSTAR): - ASSERT_NOT_REACHED(); - return matchError(JSRegExpErrorInternal, stack); - -#ifdef USE_COMPUTED_GOTO_FOR_MATCH_OPCODE_LOOP - CAPTURING_BRACKET: -#else - default: -#endif - /* Opening capturing bracket. If there is space in the offset vector, save - the current subject position in the working slot at the top of the vector. We - mustn't change the current values of the data slot, because they may be set - from a previous iteration of this group, and be referred to by a reference - inside the group. - - If the bracket fails to match, we need to restore this value and also the - values of the final offsets, in case they were set by a previous iteration of - the same bracket. - - If there isn't enough space in the offset vector, treat this as if it were a - non-capturing bracket. Don't worry about setting the flag for the error case - here; that is handled in the code for KET. */ - - ASSERT(*stack.currentFrame->args.instructionPtr > OP_BRA); - - stack.currentFrame->locals.number = *stack.currentFrame->args.instructionPtr - OP_BRA; - - /* For extended extraction brackets (large number), we have to fish out the - number from a dummy opcode at the start. */ - - if (stack.currentFrame->locals.number > EXTRACT_BASIC_MAX) - stack.currentFrame->locals.number = get2ByteValue(stack.currentFrame->args.instructionPtr + 2 + LINK_SIZE); - stack.currentFrame->locals.offset = stack.currentFrame->locals.number << 1; - -#ifdef DEBUG - printf("start bracket %d subject=", stack.currentFrame->locals.number); - pchars(stack.currentFrame->args.subjectPtr, 16, true, md); - printf("\n"); -#endif - - if (stack.currentFrame->locals.offset < md.offsetMax) { - stack.currentFrame->locals.saveOffset1 = md.offsetVector[stack.currentFrame->locals.offset]; - stack.currentFrame->locals.saveOffset2 = md.offsetVector[stack.currentFrame->locals.offset + 1]; - stack.currentFrame->locals.saveOffset3 = md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number]; - - DPRINTF(("saving %d %d %d\n", stack.currentFrame->locals.saveOffset1, stack.currentFrame->locals.saveOffset2, stack.currentFrame->locals.saveOffset3)); - md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number] = stack.currentFrame->args.subjectPtr - md.startSubject; - - do { - RECURSIVE_MATCH_NEW_GROUP(1, stack.currentFrame->args.instructionPtr + 1 + LINK_SIZE, stack.currentFrame->args.bracketChain); - if (isMatch) - RRETURN; - stack.currentFrame->args.instructionPtr += getLinkValue(stack.currentFrame->args.instructionPtr + 1); - } while (*stack.currentFrame->args.instructionPtr == OP_ALT); - - DPRINTF(("bracket %d failed\n", stack.currentFrame->locals.number)); - - md.offsetVector[stack.currentFrame->locals.offset] = stack.currentFrame->locals.saveOffset1; - md.offsetVector[stack.currentFrame->locals.offset + 1] = stack.currentFrame->locals.saveOffset2; - md.offsetVector[md.offsetEnd - stack.currentFrame->locals.number] = stack.currentFrame->locals.saveOffset3; - - RRETURN; - } - - /* Insufficient room for saving captured contents */ - - goto NON_CAPTURING_BRACKET; - } - - /* Do not stick any code in here without much thought; it is assumed - that "continue" in the code above comes out to here to repeat the main - loop. */ - - } /* End of main loop */ - - ASSERT_NOT_REACHED(); - -#ifndef USE_COMPUTED_GOTO_FOR_MATCH_RECURSION - -RRETURN_SWITCH: - switch (stack.currentFrame->returnLocation) { - case 0: goto RETURN; - case 1: goto RRETURN_1; - case 2: goto RRETURN_2; - case 6: goto RRETURN_6; - case 7: goto RRETURN_7; - case 14: goto RRETURN_14; - case 15: goto RRETURN_15; - case 16: goto RRETURN_16; - case 17: goto RRETURN_17; - case 18: goto RRETURN_18; - case 19: goto RRETURN_19; - case 20: goto RRETURN_20; - case 21: goto RRETURN_21; - case 22: goto RRETURN_22; - case 24: goto RRETURN_24; - case 26: goto RRETURN_26; - case 27: goto RRETURN_27; - case 28: goto RRETURN_28; - case 29: goto RRETURN_29; - case 30: goto RRETURN_30; - case 31: goto RRETURN_31; - case 38: goto RRETURN_38; - case 40: goto RRETURN_40; - case 42: goto RRETURN_42; - case 44: goto RRETURN_44; - case 48: goto RRETURN_48; - case 52: goto RRETURN_52; - } - - ASSERT_NOT_REACHED(); - return matchError(JSRegExpErrorInternal, stack); - -#endif - -RETURN: - return isMatch; -} - - -/************************************************* -* Execute a Regular Expression * -*************************************************/ - -/* This function applies a compiled re to a subject string and picks out -portions of the string if it matches. Two elements in the vector are set for -each substring: the offsets to the start and end of the substring. - -Arguments: - re points to the compiled expression - extra_data points to extra data or is NULL - subject points to the subject string - length length of subject string (may contain binary zeros) - start_offset where to start in the subject string - options option bits - offsets points to a vector of ints to be filled in with offsets - offsetCount the number of elements in the vector - -Returns: > 0 => success; value is the number of elements filled in - = 0 => success, but offsets is not big enough - -1 => failed to match - < -1 => some kind of unexpected problem -*/ - -static void tryFirstByteOptimization(const UChar*& subjectPtr, const UChar* endSubject, int firstByte, bool firstByteIsCaseless, bool useMultiLineFirstCharOptimization, const UChar* originalSubjectStart) -{ - // If firstByte is set, try scanning to the first instance of that byte - // no need to try and match against any earlier part of the subject string. - if (firstByte >= 0) { - UChar firstChar = firstByte; - if (firstByteIsCaseless) - while (subjectPtr < endSubject) { - int c = *subjectPtr; - if (c > 127) - break; - if (toLowerCase(c) == firstChar) - break; - subjectPtr++; - } - else { - while (subjectPtr < endSubject && *subjectPtr != firstChar) - subjectPtr++; - } - } else if (useMultiLineFirstCharOptimization) { - /* Or to just after \n for a multiline match if possible */ - // I'm not sure why this != originalSubjectStart check is necessary -- ecs 11/18/07 - if (subjectPtr > originalSubjectStart) { - while (subjectPtr < endSubject && !isNewline(subjectPtr[-1])) - subjectPtr++; - } - } -} - -static bool tryRequiredByteOptimization(const UChar*& subjectPtr, const UChar* endSubject, int reqByte, int reqByte2, bool reqByteIsCaseless, bool hasFirstByte, const UChar*& reqBytePtr) -{ - /* If reqByte is set, we know that that character must appear in the subject - for the match to succeed. If the first character is set, reqByte must be - later in the subject; otherwise the test starts at the match point. This - optimization can save a huge amount of backtracking in patterns with nested - unlimited repeats that aren't going to match. Writing separate code for - cased/caseless versions makes it go faster, as does using an autoincrement - and backing off on a match. - - HOWEVER: when the subject string is very, very long, searching to its end can - take a long time, and give bad performance on quite ordinary patterns. This - showed up when somebody was matching /^C/ on a 32-megabyte string... so we - don't do this when the string is sufficiently long. - */ - - if (reqByte >= 0 && endSubject - subjectPtr < REQ_BYTE_MAX) { - const UChar* p = subjectPtr + (hasFirstByte ? 1 : 0); - - /* We don't need to repeat the search if we haven't yet reached the - place we found it at last time. */ - - if (p > reqBytePtr) { - if (reqByteIsCaseless) { - while (p < endSubject) { - int pp = *p++; - if (pp == reqByte || pp == reqByte2) { - p--; - break; - } - } - } else { - while (p < endSubject) { - if (*p++ == reqByte) { - p--; - break; - } - } - } - - /* If we can't find the required character, break the matching loop */ - - if (p >= endSubject) - return true; - - /* If we have found the required character, save the point where we - found it, so that we don't search again next time round the loop if - the start hasn't passed this character yet. */ - - reqBytePtr = p; - } - } - return false; -} - -int jsRegExpExecute(const JSRegExp* re, - const UChar* subject, int length, int start_offset, int* offsets, - int offsetCount) -{ - ASSERT(re); - ASSERT(subject || !length); - ASSERT(offsetCount >= 0); - ASSERT(offsets || offsetCount == 0); - - HistogramTimeLogger logger(re); - - MatchData matchBlock; - matchBlock.startSubject = subject; - matchBlock.endSubject = matchBlock.startSubject + length; - const UChar* endSubject = matchBlock.endSubject; - - matchBlock.multiline = (re->options & MatchAcrossMultipleLinesOption); - matchBlock.ignoreCase = (re->options & IgnoreCaseOption); - - /* If the expression has got more back references than the offsets supplied can - hold, we get a temporary chunk of working store to use during the matching. - Otherwise, we can use the vector supplied, rounding down its size to a multiple - of 3. */ - - int ocount = offsetCount - (offsetCount % 3); - - // FIXME: This is lame that we have to second-guess our caller here. - // The API should change to either fail-hard when we don't have enough offset space - // or that we shouldn't ask our callers to pre-allocate in the first place. - bool usingTemporaryOffsets = false; - if (re->topBackref > 0 && re->topBackref >= ocount/3) { - ocount = re->topBackref * 3 + 3; - matchBlock.offsetVector = new int[ocount]; - if (!matchBlock.offsetVector) - return JSRegExpErrorNoMemory; - usingTemporaryOffsets = true; - } else - matchBlock.offsetVector = offsets; - - matchBlock.offsetEnd = ocount; - matchBlock.offsetMax = (2*ocount)/3; - matchBlock.offsetOverflow = false; - - /* Compute the minimum number of offsets that we need to reset each time. Doing - this makes a huge difference to execution time when there aren't many brackets - in the pattern. */ - - int resetCount = 2 + re->topBracket * 2; - if (resetCount > offsetCount) - resetCount = ocount; - - /* Reset the working variable associated with each extraction. These should - never be used unless previously set, but they get saved and restored, and so we - initialize them to avoid reading uninitialized locations. */ - - if (matchBlock.offsetVector) { - int* iptr = matchBlock.offsetVector + ocount; - int* iend = iptr - resetCount/2 + 1; - while (--iptr >= iend) - *iptr = -1; - } - - /* Set up the first character to match, if available. The firstByte value is - never set for an anchored regular expression, but the anchoring may be forced - at run time, so we have to test for anchoring. The first char may be unset for - an unanchored pattern, of course. If there's no first char and the pattern was - studied, there may be a bitmap of possible first characters. */ - - bool firstByteIsCaseless = false; - int firstByte = -1; - if (re->options & UseFirstByteOptimizationOption) { - firstByte = re->firstByte & 255; - if ((firstByteIsCaseless = (re->firstByte & REQ_IGNORE_CASE))) - firstByte = toLowerCase(firstByte); - } - - /* For anchored or unanchored matches, there may be a "last known required - character" set. */ - - bool reqByteIsCaseless = false; - int reqByte = -1; - int reqByte2 = -1; - if (re->options & UseRequiredByteOptimizationOption) { - reqByte = re->reqByte & 255; // FIXME: This optimization could be made to work for UTF16 chars as well... - reqByteIsCaseless = (re->reqByte & REQ_IGNORE_CASE); - reqByte2 = flipCase(reqByte); - } - - /* Loop for handling unanchored repeated matching attempts; for anchored regexs - the loop runs just once. */ - - const UChar* startMatch = subject + start_offset; - const UChar* reqBytePtr = startMatch - 1; - bool useMultiLineFirstCharOptimization = re->options & UseMultiLineFirstByteOptimizationOption; - - do { - /* Reset the maximum number of extractions we might see. */ - if (matchBlock.offsetVector) { - int* iptr = matchBlock.offsetVector; - int* iend = iptr + resetCount; - while (iptr < iend) - *iptr++ = -1; - } - - tryFirstByteOptimization(startMatch, endSubject, firstByte, firstByteIsCaseless, useMultiLineFirstCharOptimization, matchBlock.startSubject + start_offset); - if (tryRequiredByteOptimization(startMatch, endSubject, reqByte, reqByte2, reqByteIsCaseless, firstByte >= 0, reqBytePtr)) - break; - - /* When a match occurs, substrings will be set for all internal extractions; - we just need to set up the whole thing as substring 0 before returning. If - there were too many extractions, set the return code to zero. In the case - where we had to get some local store to hold offsets for backreferences, copy - those back references that we can. In this case there need not be overflow - if certain parts of the pattern were not used. */ - - /* The code starts after the JSRegExp block and the capture name table. */ - const unsigned char* start_code = (const unsigned char*)(re + 1); - - int returnCode = match(startMatch, start_code, 2, matchBlock); - - /* When the result is no match, advance the pointer to the next character - and continue. */ - if (returnCode == 0) { - startMatch++; - continue; - } - - if (returnCode != 1) { - ASSERT(returnCode == JSRegExpErrorHitLimit || returnCode == JSRegExpErrorNoMemory); - DPRINTF((">>>> error: returning %d\n", returnCode)); - return returnCode; - } - - /* We have a match! Copy the offset information from temporary store if - necessary */ - - if (usingTemporaryOffsets) { - if (offsetCount >= 4) { - memcpy(offsets + 2, matchBlock.offsetVector + 2, (offsetCount - 2) * sizeof(int)); - DPRINTF(("Copied offsets from temporary memory\n")); - } - if (matchBlock.endOffsetTop > offsetCount) - matchBlock.offsetOverflow = true; - - DPRINTF(("Freeing temporary memory\n")); - delete [] matchBlock.offsetVector; - } - - returnCode = matchBlock.offsetOverflow ? 0 : matchBlock.endOffsetTop / 2; - - if (offsetCount < 2) - returnCode = 0; - else { - offsets[0] = startMatch - matchBlock.startSubject; - offsets[1] = matchBlock.endMatchPtr - matchBlock.startSubject; - } - - DPRINTF((">>>> returning %d\n", returnCode)); - return returnCode; - } while (!(re->options & IsAnchoredOption) && startMatch <= endSubject); - - if (usingTemporaryOffsets) { - DPRINTF(("Freeing temporary memory\n")); - delete [] matchBlock.offsetVector; - } - - DPRINTF((">>>> returning PCRE_ERROR_NOMATCH\n")); - return JSRegExpErrorNoMatch; -} - -#if REGEXP_HISTOGRAM - -class CompareHistogramEntries { -public: - bool operator()(const pair<UString, double>& a, const pair<UString, double>& b) - { - if (a.second == b.second) - return a.first < b.first; - return a.second < b.second; - } -}; - -Histogram::~Histogram() -{ - Vector<pair<UString, double> > values; - Map::iterator end = times.end(); - for (Map::iterator it = times.begin(); it != end; ++it) - values.append(*it); - sort(values.begin(), values.end(), CompareHistogramEntries()); - size_t size = values.size(); - printf("Regular Expressions, sorted by time spent evaluating them:\n"); - for (size_t i = 0; i < size; ++i) - printf(" %f - %s\n", values[size - i - 1].second, values[size - i - 1].first.UTF8String().c_str()); -} - -void Histogram::add(const JSRegExp* re, double elapsedTime) -{ - UString string(reinterpret_cast<const UChar*>(reinterpret_cast<const char*>(re) + re->stringOffset), re->stringLength); - if (re->options & IgnoreCaseOption && re->options & MatchAcrossMultipleLinesOption) - string += " (multi-line, ignore case)"; - else { - if (re->options & IgnoreCaseOption) - string += " (ignore case)"; - if (re->options & MatchAcrossMultipleLinesOption) - string += " (multi-line)"; - } - pair<Map::iterator, bool> result = times.add(string.rep(), elapsedTime); - if (!result.second) - result.first->second += elapsedTime; -} - -HistogramTimeLogger::HistogramTimeLogger(const JSRegExp* re) - : m_re(re) - , m_startTime(getCurrentUTCTimeWithMicroseconds()) -{ -} - -HistogramTimeLogger::~HistogramTimeLogger() -{ - static Histogram histogram; - histogram.add(m_re, getCurrentUTCTimeWithMicroseconds() - m_startTime); -} - -#endif |