diff options
Diffstat (limited to 'Source/WebCore/platform')
16 files changed, 92 insertions, 117 deletions
diff --git a/Source/WebCore/platform/KURL.cpp b/Source/WebCore/platform/KURL.cpp index 88ad3d9..234d749 100644 --- a/Source/WebCore/platform/KURL.cpp +++ b/Source/WebCore/platform/KURL.cpp @@ -708,7 +708,7 @@ String KURL::query() const String KURL::path() const { - return decodeURLEscapeSequences(m_string.substring(m_portEnd, m_pathEnd - m_portEnd)); + return m_string.substring(m_portEnd, m_pathEnd - m_portEnd); } bool KURL::setProtocol(const String& s) @@ -866,7 +866,7 @@ void KURL::setPath(const String& s) parse(m_string.left(m_portEnd) + encodeWithURLEscapeSequences(path) + m_string.substring(m_pathEnd)); } -String KURL::prettyURL() const +String KURL::deprecatedString() const { if (!m_isValid) return m_string; @@ -996,7 +996,7 @@ static void appendEscapingBadChars(char*& buffer, const char* strStart, size_t l buffer = p; } -static void escapeAndAppendFragment(char*& buffer, const char* strStart, size_t length) +static void escapeAndAppendNonHierarchicalPart(char*& buffer, const char* strStart, size_t length) { char* p = buffer; @@ -1137,6 +1137,23 @@ static inline bool hostPortIsEmptyButCredentialsArePresent(int hostStart, int po return userEndChar == '@' && hostStart == portEnd; } +static bool isNonFileHierarchicalScheme(const char* scheme, size_t schemeLength) +{ + switch (schemeLength) { + case 2: + return equal("ws", 2, scheme, schemeLength); + case 3: + return equal("ftp", 3, scheme, schemeLength) || equal("wss", 3, scheme, schemeLength); + case 4: + return equal("http", 4, scheme, schemeLength); + case 5: + return equal("https", 5, scheme, schemeLength); + case 6: + return equal("gopher", 6, scheme, schemeLength); + } + return false; +} + void KURL::parse(const char* url, const String* originalString) { if (!url || url[0] == '\0') { @@ -1173,6 +1190,7 @@ void KURL::parse(const char* url, const String* originalString) int portEnd; bool hierarchical = url[schemeEnd + 1] == '/'; + bool hasSecondSlash = hierarchical && url[schemeEnd + 2] == '/'; bool isFile = schemeEnd == 4 && matchLetter(url[0], 'f') @@ -1186,12 +1204,15 @@ void KURL::parse(const char* url, const String* originalString) && matchLetter(url[3], 'p') && (url[4] == ':' || (matchLetter(url[4], 's') && url[5] == ':')); - if (hierarchical && url[schemeEnd + 2] == '/') { + if ((hierarchical && hasSecondSlash) || isNonFileHierarchicalScheme(url, schemeEnd)) { // 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; + + if (hierarchical) + userStart++; + if (hasSecondSlash) + userStart++; userEnd = userStart; int colonPos = 0; @@ -1394,12 +1415,14 @@ void KURL::parse(const char* url, const String* originalString) m_userStart = m_userEnd = m_passwordEnd = m_hostEnd = m_portEnd = p - buffer.data(); // For canonicalization, ensure we have a '/' for no path. - // Do this only for hierarchical URL with protocol http or https. - if (m_protocolInHTTPFamily && hierarchical && pathEnd == pathStart) + // Do this only for URL with protocol http or https. + if (m_protocolInHTTPFamily && pathEnd == pathStart) *p++ = '/'; // add path, escaping bad characters - if (!hierarchical || !hasSlashDotOrDotDot(url)) + if (!hierarchical) + escapeAndAppendNonHierarchicalPart(p, url + pathStart, pathEnd - pathStart); + else if (!hasSlashDotOrDotDot(url)) appendEscapingBadChars(p, url + pathStart, pathEnd - pathStart); else { CharBuffer pathBuffer(pathEnd - pathStart + 1); @@ -1425,7 +1448,7 @@ void KURL::parse(const char* url, const String* originalString) // add fragment, escaping bad characters if (fragmentEnd != queryEnd) { *p++ = '#'; - escapeAndAppendFragment(p, url + fragmentStart, fragmentEnd - fragmentStart); + escapeAndAppendNonHierarchicalPart(p, url + fragmentStart, fragmentEnd - fragmentStart); } m_fragmentEnd = p - buffer.data(); diff --git a/Source/WebCore/platform/KURL.h b/Source/WebCore/platform/KURL.h index db2dd42..192c10b 100644 --- a/Source/WebCore/platform/KURL.h +++ b/Source/WebCore/platform/KURL.h @@ -152,7 +152,9 @@ public: String baseAsString() const; - String prettyURL() const; + // This function is only used by location.href. It's likely we shouldn't + // use it for that purpose, but more study is necessary before we remove it. + String deprecatedString() const; String fileSystemPath() const; // Returns true if the current URL's protocol is the same as the null- diff --git a/Source/WebCore/platform/KURLGoogle.cpp b/Source/WebCore/platform/KURLGoogle.cpp index 0d11b99..54bcf16 100644 --- a/Source/WebCore/platform/KURLGoogle.cpp +++ b/Source/WebCore/platform/KURLGoogle.cpp @@ -593,7 +593,6 @@ String KURL::query() const String KURL::path() const { - // Note: KURL.cpp unescapes here. return m_url.componentString(m_url.m_parsed.path); } @@ -670,16 +669,12 @@ void KURL::setPort(unsigned short i) { KURLGooglePrivate::Replacements replacements; String portStr; - if (i) { - portStr = String::number(i); - replacements.SetPort( - reinterpret_cast<const url_parse::UTF16Char*>(portStr.characters()), - url_parse::Component(0, portStr.length())); - } else { - // Clear any existing port when it is set to 0. - replacements.ClearPort(); - } + portStr = String::number(i); + replacements.SetPort( + reinterpret_cast<const url_parse::UTF16Char*>(portStr.characters()), + url_parse::Component(0, portStr.length())); + m_url.replaceComponents(replacements); } @@ -772,7 +767,7 @@ void KURL::setPath(const String& path) // On Mac, this just seems to return the same URL, but with "/foo/bar" for // file: URLs instead of file:///foo/bar. We don't bother with any of this, // at least for now. -String KURL::prettyURL() const +String KURL::deprecatedString() const { if (!m_url.m_isValid) return String(); diff --git a/Source/WebCore/platform/PlatformTouchEvent.h b/Source/WebCore/platform/PlatformTouchEvent.h index 2ca7c99..f7524b4 100644 --- a/Source/WebCore/platform/PlatformTouchEvent.h +++ b/Source/WebCore/platform/PlatformTouchEvent.h @@ -52,10 +52,6 @@ enum TouchEventType { , TouchMove , TouchEnd , TouchCancel -#if PLATFORM(ANDROID) - , TouchLongPress - , TouchDoubleTap -#endif }; class PlatformTouchEvent { diff --git a/Source/WebCore/platform/graphics/android/fonts/GlyphMapAndroid.cpp b/Source/WebCore/platform/graphics/android/fonts/GlyphMapAndroid.cpp index a327b79..b9798e6 100644 --- a/Source/WebCore/platform/graphics/android/fonts/GlyphMapAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/fonts/GlyphMapAndroid.cpp @@ -44,12 +44,12 @@ namespace WebCore { #define NO_BREAK_SPACE_UNICHAR 0xA0 -static int substituteWithVerticalGlyphs(const FontPlatformData& platformData, uint16_t* glyphs, unsigned bufferLength) +static HB_Error substituteWithVerticalGlyphs(const FontPlatformData& platformData, uint16_t* glyphs, unsigned bufferLength) { HB_FaceRec_* hbFace = platformData.harfbuzzFace(); if (!hbFace->gsub) { // if there is no GSUB table, treat it as not covered - return 0Xffff; + return static_cast<HB_Error>(0Xffff); } HB_Buffer buffer; @@ -60,13 +60,19 @@ static int substituteWithVerticalGlyphs(const FontPlatformData& platformData, ui HB_UShort scriptIndex; HB_UShort featureIndex; - HB_GSUB_Select_Script(hbFace->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &scriptIndex); + HB_Error error = HB_GSUB_Select_Script(hbFace->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &scriptIndex); + if (error) { + if (error != HB_Err_Not_Covered) + return error; + scriptIndex = HB_Script_Common; // Set script to common script. + } + HB_GSUB_Select_Feature(hbFace->gsub, HB_MAKE_TAG('v', 'e', 'r', 't'), scriptIndex, 0xffff, &featureIndex); HB_GSUB_Add_Feature(hbFace->gsub, featureIndex, 1); HB_GSUB_Select_Feature(hbFace->gsub, HB_MAKE_TAG('v', 'r', 't', '2'), scriptIndex, 0xffff, &featureIndex); HB_GSUB_Add_Feature(hbFace->gsub, featureIndex, 1); - int error = HB_GSUB_Apply_String(hbFace->gsub, buffer); + error = HB_GSUB_Apply_String(hbFace->gsub, buffer); if (!error) { for (unsigned i = 0; i < bufferLength; ++i) glyphs[i] = static_cast<Glyph>(buffer->out_string[i].gindex); @@ -74,6 +80,14 @@ static int substituteWithVerticalGlyphs(const FontPlatformData& platformData, ui return error; } +static void convertToVerticalForms(UChar* src, UChar* dest, unsigned bufferLength) { + for (unsigned i = 0; i < bufferLength; ++i) { + dest[i] = VerticalTextMap::getVerticalForm(src[i]); + if (!dest[i]) + dest[i] = src[i]; + } +} + bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned bufferLength, const SimpleFontData* fontData) { if (SkUTF16_IsHighSurrogate(buffer[bufferLength-1])) { @@ -92,11 +106,7 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b if (fontData->platformData().orientation() == Vertical && !fontData->hasVerticalGlyphs()) { // Convert to vertical form if there is no vertical glyphs. - for (unsigned i = 0; i < bufferLength; ++i) { - vTextBuffer[i] = VerticalTextMap::getVerticalForm(buffer[i]); - if (!vTextBuffer[i]) - vTextBuffer[i] = buffer[i]; - } + convertToVerticalForms(buffer, vTextBuffer, bufferLength); textBuffer = vTextBuffer; } @@ -111,11 +121,22 @@ bool GlyphPage::fill(unsigned offset, unsigned length, UChar* buffer, unsigned b for (unsigned i = 0; i < bufferLength; ++i) { if (!Font::isCJKIdeograph(textBuffer[i])) { lookVariants = true; - continue; + break; + } + } + if (lookVariants) { + if (substituteWithVerticalGlyphs(fontData->platformData(), glyphs, bufferLength)) { + // Convert text to vertical forms if substituteWithVerticalGlyphs() fails to access vert tables. + convertToVerticalForms(buffer, vTextBuffer, bufferLength); + textBuffer = vTextBuffer; + + unsigned count = paint.textToGlyphs(textBuffer, bufferLength << 1, glyphs); + if (count != length) { + SkDebugf("%s count != length\n", __FUNCTION__); + return false; + } } } - if (lookVariants) - substituteWithVerticalGlyphs(fontData->platformData(), glyphs, bufferLength); } unsigned allGlyphs = 0; // track if any of the glyphIDs are non-zero diff --git a/Source/WebCore/platform/graphics/android/layers/Layer.h b/Source/WebCore/platform/graphics/android/layers/Layer.h index 996547b..d87c699 100644 --- a/Source/WebCore/platform/graphics/android/layers/Layer.h +++ b/Source/WebCore/platform/graphics/android/layers/Layer.h @@ -157,6 +157,9 @@ protected: bool m_hasOverflowChildren; + // invalidation region + SkRegion m_dirtyRegion; +private: bool isAncestor(const Layer*) const; Layer* fParent; @@ -173,9 +176,6 @@ protected: SkTDArray<Layer*> m_children; - // invalidation region - SkRegion m_dirtyRegion; - WebCore::GLWebViewState* m_state; typedef SkRefCnt INHERITED; diff --git a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp index df3fa42..a02759d 100644 --- a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp +++ b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.cpp @@ -254,7 +254,7 @@ void LayerAndroid::addDirtyArea() area.intersect(clip); IntRect dirtyArea(area.x(), area.y(), area.width(), area.height()); - m_state->addDirtyArea(dirtyArea); + state()->addDirtyArea(dirtyArea); } void LayerAndroid::addAnimation(PassRefPtr<AndroidAnimation> prpAnim) @@ -411,7 +411,6 @@ void LayerAndroid::updatePositions() void LayerAndroid::updateGLPositionsAndScale(const TransformationMatrix& parentMatrix, const FloatRect& clipping, float opacity, float scale) { - m_atomicSync.lock(); IntSize layerSize(getSize().width(), getSize().height()); FloatPoint anchorPoint(getAnchorPoint().fX, getAnchorPoint().fY); FloatPoint position(getPosition().fX - m_offset.x(), getPosition().fY - m_offset.y()); @@ -428,7 +427,6 @@ void LayerAndroid::updateGLPositionsAndScale(const TransformationMatrix& parentM -originY, -anchorPointZ()); - m_atomicSync.unlock(); setDrawTransform(localMatrix); if (m_drawTransform.isIdentityOrTranslation()) { // adjust the translation coordinates of the draw transform matrix so @@ -610,50 +608,6 @@ void LayerAndroid::mergeInvalsInto(LayerAndroid* replacementTree) replacementLayer->markAsDirty(m_dirtyRegion); } -bool LayerAndroid::updateWithTree(LayerAndroid* newTree) -{ -// Disable fast update for now -#if (0) - bool needsRepaint = false; - int count = this->countChildren(); - for (int i = 0; i < count; i++) - needsRepaint |= this->getChild(i)->updateWithTree(newTree); - - if (newTree) { - LayerAndroid* newLayer = newTree->findById(uniqueId()); - needsRepaint |= updateWithLayer(newLayer); - } - return needsRepaint; -#else - return true; -#endif -} - -// Return true to indicate to WebViewCore that the updates -// are too complicated to be fully handled and we need a full -// call to webkit (e.g. handle repaints) -bool LayerAndroid::updateWithLayer(LayerAndroid* layer) -{ - if (!layer) - return true; - - android::AutoMutex lock(m_atomicSync); - m_position = layer->m_position; - m_anchorPoint = layer->m_anchorPoint; - m_size = layer->m_size; - m_opacity = layer->m_opacity; - m_transform = layer->m_transform; - - if (m_imageCRC != layer->m_imageCRC) - m_visible = false; - - if ((m_content != layer->m_content) - || (m_imageCRC != layer->m_imageCRC)) - return true; - - return false; -} - static inline bool compareLayerZ(const LayerAndroid* a, const LayerAndroid* b) { return a->zValue() > b->zValue(); @@ -857,7 +811,7 @@ bool LayerAndroid::drawGL(bool layerTilesDisabled) ImagesManager::instance()->releaseImage(m_imageCRC); } - m_state->glExtras()->drawGL(this); + state()->glExtras()->drawGL(this); bool askScreenUpdate = false; m_atomicSync.lock(); @@ -930,7 +884,8 @@ void LayerAndroid::onDraw(SkCanvas* canvas, SkScalar opacity, return; } - if (masksToBounds() || !m_content) + // only continue drawing if layer is drawable + if (!m_content && !m_imageCRC) return; // we just have this save/restore for opacity... diff --git a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h index 6239418..9a803a9 100644 --- a/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/layers/LayerAndroid.h @@ -257,12 +257,6 @@ public: friend LayerAndroid* android::deserializeLayer(int version, SkStream* stream); friend void android::cleanupImageRefs(LayerAndroid* layer); - // Update layers using another tree. Only works for basic properties - // such as the position, the transform. Return true if anything more - // complex is needed. - bool updateWithTree(LayerAndroid*); - virtual bool updateWithLayer(LayerAndroid*); - LayerType type() { return m_type; } virtual SubclassType subclassType() { return LayerAndroid::StandardLayer; } diff --git a/Source/WebCore/platform/graphics/android/layers/ScrollableLayerAndroid.h b/Source/WebCore/platform/graphics/android/layers/ScrollableLayerAndroid.h index 1f289e6..52f5e7e 100644 --- a/Source/WebCore/platform/graphics/android/layers/ScrollableLayerAndroid.h +++ b/Source/WebCore/platform/graphics/android/layers/ScrollableLayerAndroid.h @@ -43,8 +43,6 @@ public: virtual LayerAndroid* copy() const { return new ScrollableLayerAndroid(*this); } virtual SubclassType subclassType() { return LayerAndroid::ScrollableLayer; } - virtual bool updateWithLayer(LayerAndroid*) { return true; } - // Scrolls to the given position in the layer. // Returns whether or not any scrolling was required. virtual bool scrollTo(int x, int y); diff --git a/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp index 26bd55d..9435065 100644 --- a/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/GLUtils.cpp @@ -578,16 +578,6 @@ void GLUtils::createTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, GL } glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); - - // The following is a workaround -- remove when EGLImage texture upload is fixed. - GLuint fboID; - glGenFramebuffers(1, &fboID); - glBindFramebuffer(GL_FRAMEBUFFER, fboID); - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); - glCheckFramebufferStatus(GL_FRAMEBUFFER); // should return GL_FRAMEBUFFER_COMPLETE - - glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO - glDeleteFramebuffers(1, &fboID); } void GLUtils::updateTextureWithBitmap(GLuint texture, const SkBitmap& bitmap, diff --git a/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp b/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp index 2510d52..a58a1d2 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/TileGrid.cpp @@ -274,8 +274,8 @@ int TileGrid::nbTextures(IntRect& area, float scale) } void TileGrid::drawGL(const IntRect& visibleArea, float opacity, - const TransformationMatrix* transform, - const Color* background) + const TransformationMatrix* transform, + const Color* background) { m_area = computeTilesArea(visibleArea, m_scale); if (m_area.width() == 0 || m_area.height() == 0) @@ -322,7 +322,8 @@ void TileGrid::drawGL(const IntRect& visibleArea, float opacity, drawn++; } - if (semiOpaqueBaseSurface) + // log tile information for base, high res tiles + if (m_isBaseSurface && background) TilesManager::instance()->getProfiler()->nextTile(tile, invScale, tileInView); } diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp index af19f30..a330b41 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.cpp @@ -203,7 +203,6 @@ void TransferQueue::blitTileFromQueue(GLuint fboID, TileTexture* destTex, GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { ALOGV("Error: glCheckFramebufferStatus failed"); - glBindFramebuffer(GL_FRAMEBUFFER, 0); return; } @@ -426,7 +425,6 @@ void TransferQueue::updateDirtyTiles() // dynamic switch possible. Moving this out from the loop can save some // milli-seconds. if (usedFboForUpload) { - glBindFramebuffer(GL_FRAMEBUFFER, 0); // rebind the standard FBO restoreGLState(); GLUtils::checkGlError("updateDirtyTiles"); } @@ -593,6 +591,7 @@ void TransferQueue::cleanupPendingDiscard() void TransferQueue::saveGLState() { + glGetIntegerv(GL_FRAMEBUFFER_BINDING, m_GLStateBeforeBlit.bufferId); glGetIntegerv(GL_VIEWPORT, m_GLStateBeforeBlit.viewport); glGetBooleanv(GL_SCISSOR_TEST, m_GLStateBeforeBlit.scissor); glGetBooleanv(GL_DEPTH_TEST, m_GLStateBeforeBlit.depth); @@ -616,6 +615,7 @@ void TransferQueue::setGLStateForCopy(int width, int height) void TransferQueue::restoreGLState() { + glBindFramebuffer(GL_FRAMEBUFFER, m_GLStateBeforeBlit.bufferId[0]); glViewport(m_GLStateBeforeBlit.viewport[0], m_GLStateBeforeBlit.viewport[1], m_GLStateBeforeBlit.viewport[2], diff --git a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h index 65ff116..d1024a4 100644 --- a/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h +++ b/Source/WebCore/platform/graphics/android/rendering/TransferQueue.h @@ -40,6 +40,7 @@ class Tile; class TileTexture; struct GLState { + GLint bufferId[1]; GLint viewport[4]; GLboolean scissor[1]; GLboolean depth[1]; diff --git a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp index dc22fca..ec04035 100644 --- a/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp +++ b/Source/WebCore/platform/network/soup/ResourceHandleSoup.cpp @@ -180,9 +180,9 @@ static void ensureSessionIsInitialized(SoupSession* session) g_object_set_data(G_OBJECT(session), "webkit-init", reinterpret_cast<void*>(0xdeadbeef)); } -void ResourceHandle::prepareForURL(const KURL &url) +void ResourceHandle::prepareForURL(const KURL& url) { - GOwnPtr<SoupURI> soupURI(soup_uri_new(url.prettyURL().utf8().data())); + GOwnPtr<SoupURI> soupURI(soup_uri_new(url.string().utf8().data())); if (!soupURI) return; soup_session_prepare_for_uri(ResourceHandle::defaultSession(), soupURI.get()); diff --git a/Source/WebCore/platform/qt/KURLQt.cpp b/Source/WebCore/platform/qt/KURLQt.cpp index f6d2a86..674a933 100644 --- a/Source/WebCore/platform/qt/KURLQt.cpp +++ b/Source/WebCore/platform/qt/KURLQt.cpp @@ -50,4 +50,3 @@ String KURL::fileSystemPath() const } } - diff --git a/Source/WebCore/platform/win/ClipboardWin.cpp b/Source/WebCore/platform/win/ClipboardWin.cpp index 0b5a3d3..2e56cbc 100644 --- a/Source/WebCore/platform/win/ClipboardWin.cpp +++ b/Source/WebCore/platform/win/ClipboardWin.cpp @@ -191,7 +191,7 @@ static HGLOBAL createGlobalHDropContent(const KURL& url, String& fileName, Share WCHAR filePath[MAX_PATH]; if (url.isLocalFile()) { - String localPath = url.path(); + String localPath = decodeURLEscapeSequences(url.path()); // windows does not enjoy a leading slash on paths if (localPath[0] == '/') localPath = localPath.substring(1); |