Internal change PiperOrigin-RevId: 96791655 Change-Id: I1232926a48b9fa8ba7ca739ba16294d17da1dd6a
diff --git a/LICENSE b/LICENSE index ac40837..4917789 100644 --- a/LICENSE +++ b/LICENSE
@@ -63,3 +63,40 @@ and other provisions required by the GPL or the LGPL. If you do not delete the provisions above, a recipient may use your version of this file under the terms of any one of the MPL, the GPL or the LGPL. + +------------------------------------------------------------------------------- + +The file icu_utf.cc is from IBM. This file is licensed separately as follows: + +ICU License - ICU 1.8.1 and later + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2009 International Business Machines Corporation and others + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder.
diff --git a/README.google b/README.google index b7346d5..eab9cf3 100644 --- a/README.google +++ b/README.google
@@ -1,32 +1,51 @@ -URL: http://google-url.googlecode.com/svn/trunk/ -Version: Snapshot of Subversion trunk, revision [139] -License: BSD and MPL (one source file under MPL) -License File: googleurl/LICENSE.txt +URL: https://chromium.googlesource.com/chromium/src/+archive/6e0744b15b09421eac6634fb3fb7fe0a03427d56/url.tar.gz +Version: 6e0744b15b09421eac6634fb3fb7fe0a03427d56 (matching Chromium 41.0.2272.118) +License: BSD, MPL, ICU (one source file under MPL, one source file under ICU) +License File: LICENSE Description: A small library for parsing and canonicalizing URLs Local Modifications: -We use our google3 versions of //base (which is a superset of the base/ -functionality in googleurl), and ICU. These are both injected through BUILD -and don't require source modifications. +1. src/base directory: +- Remove BASE_EXPORT macros. +- Wrap namespace base with namespace url to distinguish from google3 base. +- src/base/strings/string16.* + * Include src/build/build_config.h to detect wchar_t size. + * PrintTo function and operator << are removed to eliminate dependency on + src/base/strings/utf_string_conversion. +- src/base/strings/string_util.* + * Only one MatchPattern function is kept for src/url/origin.cc. + * Change the argument type from StringPiece to std::string to remove + dependency on google3 StringPiece. +- src/base/third_party/icu/icu_utf.cc + * Add FALLTHROUGH_INTENDED for fall-through switch cases. -We use //depot/google3/third_party/breakpad/import_to_p4_from_svn.py -to ease synchronization of this Perforce mirror with the authoritative -Subversion repository. When using this script, don't forget to update this -file, README.google, to reflect the Subversion revision being imported. +2. src/url directory: +- Use google3 version of //base, //util/gtl/lazy_static_ptr.h + //third_party/icu and //testing/base/public:gunit_main. Some users don't want + googleurl to be dependent on google3 (e.g. geo/render/mirth/net:googleurl), + so we try our best to do it. +- src/url/gurl.cc + * Replace scoped_ptr with std::unique_ptr to eliminate dependency on google3 + scoped_ptr. +- src/url/url_canon_icu.cc + * Replace LazyInstance with google3 LazyStaticPtr, modify intialization + and access methods accordingly. +- src/url/url_util.cc + * Replace ANNOTATE_LEAKING_OBJECT_PTR() with google3 + HeapLeakChecker::IgnoreObject(), and only use it when GOOGLEURL_IN_GOOGLE3 + is defined. +- src/url/url_canon_internal.h + * Expand NOT_REACHED() as DCHECK(false). +- src/url/url_canon_icu.h and src/url/url_canon_stdstring.h + * Remove the include of src/base/compiler_specific.h. +- src/url/third_party/mozilla/url_parse.cc + * Compile filesystemurl related function only when NO_FILESYSTEMURL_SUPPORT + is not defined, so that + wireless/android/icing/lib/core:liburl_parse_icing_static doesn't need to + depend on other googleurl srcs as well as third_party/icu. -Because googleurl uses include paths like "googleurl/src/header.h", the source -is located in a googleurl subdirectory under this directory. This allows -the paths to work correctly without adding //third_party to the include path. - -2010-01-22: the upstream code uses an open-source version of gunit and the -google3 code uses a google3 version of gunit. When importing, be careful -to use the current google3 names: testing/base/public/googletest.h and -testing/base/public/gunit.h . -- mec - +3. google3_addidions directory: 2014-07-30: added google3_additions/googleurl_init.cc, which properly initializes googleurl during InitGoogle(). - -2014-09-29: Adjusted googleurl/src/url_canon_unittest.cc for C++11 -compatibility.
diff --git a/google3_additions/googleurl_init.cc b/google3_additions/googleurl_init.cc index e0af8e6..03470f0 100644 --- a/google3_additions/googleurl_init.cc +++ b/google3_additions/googleurl_init.cc
@@ -4,12 +4,12 @@ // InitGoogle() at startup. #include "base/googleinit.h" -#include "third_party/googleurl/googleurl/src/url_util.h" +#include "third_party/googleurl/src/url/url_util.h" namespace { void InitGoogleUrl() { - url_util::Initialize(); + url::Initialize(); } } // namespace
diff --git a/googleurl/LICENSE.txt b/googleurl/LICENSE.txt deleted file mode 100644 index ac40837..0000000 --- a/googleurl/LICENSE.txt +++ /dev/null
@@ -1,65 +0,0 @@ -Copyright 2007, Google 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: - - * 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 Google Inc. 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. - -------------------------------------------------------------------------------- - -The file url_parse.cc is based on nsURLParsers.cc from Mozilla. This file is -licensed separately as follows: - -The contents of this file are subject to the Mozilla Public License Version -1.1 (the "License"); you may not use this file except in compliance with -the License. You may obtain a copy of the License at -http://www.mozilla.org/MPL/ - -Software distributed under the License is distributed on an "AS IS" basis, -WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License -for the specific language governing rights and limitations under the -License. - -The Original Code is mozilla.org code. - -The Initial Developer of the Original Code is -Netscape Communications Corporation. -Portions created by the Initial Developer are Copyright (C) 1998 -the Initial Developer. All Rights Reserved. - -Contributor(s): - Darin Fisher (original author) - -Alternatively, the contents of this file may be used under the terms of -either the GNU General Public License Version 2 or later (the "GPL"), or -the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), -in which case the provisions of the GPL or the LGPL are applicable instead -of those above. If you wish to allow use of your version of this file only -under the terms of either the GPL or the LGPL, and not to allow others to -use your version of this file under the terms of the MPL, indicate your -decision by deleting the provisions above and replace them with the notice -and other provisions required by the GPL or the LGPL. If you do not delete -the provisions above, a recipient may use your version of this file under -the terms of any one of the MPL, the GPL or the LGPL.
diff --git a/googleurl/README.txt b/googleurl/README.txt deleted file mode 100644 index b28fd04..0000000 --- a/googleurl/README.txt +++ /dev/null
@@ -1,180 +0,0 @@ - ============================== - The Google URL Parsing Library - ============================== - -This is the Google URL Parsing Library which parses and canonicalizes URLs. -Please see the LICENSE.txt file for licensing information. - -Features -======== - - * Easily embeddable: This library was written for a variety of client and - server programs in mind, so unlike most implementations of URL parsing - and canonicalization, it can be easily emdedded. - - * Fast: hundreds of thousands of typical URLs can be parsed and - canonicalized per second on a modern CPU. It is much faster than, for - example, calling WinInet's corresponding functions. - - * Compatible: When possible, this library has strived for IE7 compatability - for both general web compatability, and so IE addons or other applications - that communicate with or embed IE will work properly. - - It supports Unix-style file URLs, as well as the more complex rules for - Window file URLs. Note that total compatability is not possible (for - example, IE6 and IE7 disagree about how to parse certain IP addresses), - and that this is more strict about certain illegal, rarely used, and - potentially dangerous constructs such as escaped control characters in - host names that IE will allow. It is typically a little less strict than - Firefox. - - -Example -======= - -An example implementation of a URL object that uses this library is provided -in src/gurl.*. This implementation uses the "application integration" layer -discussed below to interface with the low-level parsing and canonicalization -functions. - - -Building -======== - -The canonicalization files require ICU for some UTF-8 and UTF-16 conversion -macros. If your project does not use ICU, it should be straightforward to -factor out the macros and functions used in ICU, there are only a few well- -isolated things that are used. - -TODO(brettw) ADD INSTRUCTIONS FOR GETTING ICU HERE! - -logging.h and logging.cc are Windows-only because the corresponding Unix -logging system has many dependencies. This library uses few of the logging -macros, and a dummy header can easily be written that defines the -appropriate things for Unix. - - -Definitions -=========== - -"Standard URL": A URL with an "authority", which is a hostname and optionally - a port, username, and password. Most URLs are standard such as HTTP and FTP. - -"File URL": A URL that references a file on disk. There are special rules for - this type of URL. Note that it may have a hostname! "localhost" is allowed, - for example "file://localhost/foo" is the same as "file:///foo". - -"Path URL": This is everything else. There is no standard on how to treat these - URLs, or even what they are called. This library decomposes them into a - scheme and a path. The path is everything following the scheme. This type of - URL includes "javascript", "data", and even "mailto" (although "mailto" - might look like a standard scheme in some respects, it is not). - - -Design -====== - -The library is divided into four layers. They are listed here from the lowest -to the highest; you can use any portion of the library as long as you embed the -layers below it. - -1. Parsing ----------- -At the lowest level is the parsing code. The files encompasing this are -url_parse.* and the main include file is src/url_parse.h. This code will, given -an input string, parse it into the most likely form of a URL. - -Parsing can not fail and does no validation. The exception is the port number, -which it currently validates, but this is a bug. Given crazy input, the parser -will do its best to find the various URL components according to its rules (see -url_parse_unittest.cc for some examples). - -To use this, an application will typically use ExtractScheme to determine the -type of a given input URL, and then call one of the initialization functions: -"ParseStandardURL", "ParsePathURL", or "ParseFileURL". This will result in -a "Parsed" structure which identifies the substrings of each identified -component. - -2. Canonicalization -------------------- -At the next highest level is canonicalization. The files encompasing this are -url_canon.* and the main include file is src/url_canon.h. This code will -validate an already-parsed URL, and will convert it to a canonical form. For -example, this will convert host names to lowercase, convert IP addresses -into dotted-decimal notation, handle encoding issues, etc. - -This layer will always do its best to produce a reasonable output string, but -it may return that the string is invalid. For example, if there are invalid -characters in the host name, it will escape them or replace them with the -Unicode "invalid character" character, but will fail. This way, the program can -display error messages to the user with the output, log it, etc. and the -string will have some meaning. - -Canonicalized output is written to a CanonOutput object which is a simple -wrapper around an expanding buffer. An implementation called RawCanonOutput is -proivided that writes to a raw buffer with a fixed amount statically allocated -(for performance). Applications using STL can use StdStringCanonOutput defined -in url_canon_stdstring.h which writes into a std::string. - -A normal application would call one of the three high-level functions -"CanonicalizeStandardURL", "CanonicalizeFileURL", and CanonicalizePathURL" -depending on the type of URL in question. Lower-level functions are also -provided which will canonicalize individual parts of a URL (for example, -"CanonicalizeHost"). - -Part of this layer is the integration with the host system for IDN and encoding -conversion. An implementation that provides integration with the ICU -(http://www-306.ibm.com/software/globalization/icu/index.jsp) is provided in -src/url_canon_icu.cc. The embedder may wish to replace this file with -implementations of the functions for their own IDN library if they do not use -ICU. - -3. Application integration --------------------------- -The canonicalization and parsing layers do not know anything about the URI -schemes supported by your application. The parsing and canonicalization -functions are very low-level, and you must call the correct function to do the -work (for example, "CanonicalizeFileURL"). - -The application integration in url_util.* provides wrappers around the -low-level parsing and canonicalization to call the correct versions for -different identified schemes. Embedders will want to modify this file if -necessary to suit the needs of their application. - -4. URL object -------------- -The highest level is the "URL" object that a C++ application would use to -to encapsulate a URL. Embedders will typically want to provide their own URL -object that meets the requirements of their system. A reasonably complete -example implemnetation is provided in src/gurl.*. You may wish to use this -object, extend or modify it, or write your own. - -Whitespace ----------- -Sometimes, you may want to remove linefeeds and tabs from the content of a URL. -Some web pages, for example, expect that a URL spanning two lines should be -treated as one with the newline removed. Depending on the source of the URLs -you are canonicalizing, these newlines may or may not be trimmed off. - -If you want this behavior, call RemoveURLWhitespace before parsing. This will -remove CR, LF and TAB from the input. Note that it preserves spaces. On typical -URLs, this function produces a 10-15% speed reduction, so it is optional and -not done automatically. The example GURL object and the url_util wrapper does -this for you. - -Tests -===== - -There are a number of *_unittest.cc and *_perftest.cc files. These files are -not currently compilable as they rely on a not-included unit testing framework -Tests are declared like this: - TEST(TestCaseName, TestName) { - ASSERT_TRUE(a); - EXPECT_EQ(a, b); - } -If you would like to compile them, it should be straightforward to define -the TEST macro (which would declare a function by combining the two arguments) -and the other macros whose behavior should be self-explanatory (EXPECT is like -an ASSERT, but does not stop the test, if you are doing this, you probably -don't care about this difference). Then you would define a .cc file that -calls all of these functions.
diff --git a/googleurl/base/README.txt b/googleurl/base/README.txt deleted file mode 100644 index 311faa0..0000000 --- a/googleurl/base/README.txt +++ /dev/null
@@ -1,2 +0,0 @@ -These files contain some shared code. You can define your own assertion macros -to eliminate the dependency on logging.h.
diff --git a/googleurl/base/string16.cc b/googleurl/base/string16.cc deleted file mode 100644 index fc25809..0000000 --- a/googleurl/base/string16.cc +++ /dev/null
@@ -1,94 +0,0 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// -// 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 Google Inc. 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. - -#include "base/string16.h" - -#ifdef WIN32 - -#error This file should not be used on 2-byte wchar_t systems -// If this winds up being needed on 2-byte wchar_t systems, either the -// definitions below can be used, or the host system's wide character -// functions like wmemcmp can be wrapped. - -#else // !WIN32 - -namespace base { - -int c16memcmp(const char16* s1, const char16* s2, size_t n) { - // We cannot call memcmp because that changes the semantics. - while (n-- > 0) { - if (*s1 != *s2) { - // We cannot use (*s1 - *s2) because char16 is unsigned. - return ((*s1 < *s2) ? -1 : 1); - } - ++s1; - ++s2; - } - return 0; -} - -size_t c16len(const char16* s) { - const char16 *s_orig = s; - while (*s) { - ++s; - } - return s - s_orig; -} - -const char16* c16memchr(const char16* s, char16 c, size_t n) { - while (n-- > 0) { - if (*s == c) { - return s; - } - ++s; - } - return 0; -} - -char16* c16memmove(char16* s1, const char16* s2, size_t n) { - return reinterpret_cast<char16*>(memmove(s1, s2, n * sizeof(char16))); -} - -char16* c16memcpy(char16* s1, const char16* s2, size_t n) { - return reinterpret_cast<char16*>(memcpy(s1, s2, n * sizeof(char16))); -} - -char16* c16memset(char16* s, char16 c, size_t n) { - char16 *s_orig = s; - while (n-- > 0) { - *s = c; - ++s; - } - return s_orig; -} - -} // namespace base - -template class std::basic_string<char16, base::string16_char_traits>; - -#endif // WIN32
diff --git a/googleurl/build/README.txt b/googleurl/build/README.txt deleted file mode 100644 index eab011a..0000000 --- a/googleurl/build/README.txt +++ /dev/null
@@ -1,4 +0,0 @@ -This directory includes solution and project files for compiling with -Visual Studio 2005 on Windows. - -The base checkout directory must be named 'googleurl'.
diff --git a/googleurl/build/base.vcproj b/googleurl/build/base.vcproj deleted file mode 100644 index 0e923cf..0000000 --- a/googleurl/build/base.vcproj +++ /dev/null
@@ -1,151 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioProject - ProjectType="Visual C++" - Version="8.00" - Name="base" - ProjectGUID="{ACAC8E18-F003-4881-9BA0-C7718AC5FFD5}" - RootNamespace="base" - Keyword="Win32Proj" - > - <Platforms> - <Platform - Name="Win32" - /> - </Platforms> - <ToolFiles> - </ToolFiles> - <Configurations> - <Configuration - Name="Debug|Win32" - ConfigurationType="4" - InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)../third_party/icu/build/using_icu.vsprops" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - <Configuration - Name="Release|Win32" - ConfigurationType="4" - InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)../third_party/icu/build/using_icu.vsprops" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - </Configurations> - <References> - </References> - <Files> - <File - RelativePath="..\base\basictypes.h" - > - </File> - <File - RelativePath="..\base\logging.cc" - > - </File> - <File - RelativePath="..\base\logging.h" - > - </File> - <File - RelativePath="..\base\README.txt" - > - </File> - <File - RelativePath="..\base\scoped_ptr.h" - > - </File> - <File - RelativePath="..\base\string16.h" - > - </File> - </Files> - <Globals> - </Globals> -</VisualStudioProject>
diff --git a/googleurl/build/common.vsprops b/googleurl/build/common.vsprops deleted file mode 100644 index ede28e9..0000000 --- a/googleurl/build/common.vsprops +++ /dev/null
@@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioPropertySheet - ProjectType="Visual C++" - Version="8.00" - Name="common" - OutputDirectory="$(SolutionDir)$(ConfigurationName)" - IntermediateDirectory="$(SolutionDir)$(ConfigurationName)\obj\$(ProjectName)" - CharacterSet="1" - > - <Tool - Name="VCCLCompilerTool" - AdditionalIncludeDirectories="$(SolutionDir)..\..;$(SolutionDir).." - PreprocessorDefinitions="_WIN32_WINNT=0x0501;WINVER=0x0501;WIN32;_WINDOWS" - MinimalRebuild="false" - BufferSecurityCheck="true" - EnableFunctionLevelLinking="true" - WarningLevel="3" - WarnAsError="true" - Detect64BitPortabilityProblems="true" - DebugInformationFormat="3" - /> -</VisualStudioPropertySheet>
diff --git a/googleurl/build/debug.vsprops b/googleurl/build/debug.vsprops deleted file mode 100644 index d2aa43f..0000000 --- a/googleurl/build/debug.vsprops +++ /dev/null
@@ -1,18 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioPropertySheet - ProjectType="Visual C++" - Version="8.00" - Name="debug" - > - <Tool - Name="VCCLCompilerTool" - Optimization="0" - PreprocessorDefinitions="_DEBUG" - BasicRuntimeChecks="3" - RuntimeLibrary="1" - /> - <Tool - Name="VCLinkerTool" - LinkIncremental="2" - /> -</VisualStudioPropertySheet>
diff --git a/googleurl/build/googleurl.sln b/googleurl/build/googleurl.sln deleted file mode 100644 index 347810d..0000000 --- a/googleurl/build/googleurl.sln +++ /dev/null
@@ -1,32 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 9.00 -# Visual Studio 2005 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "googleurl", "googleurl.vcproj", "{EF5E94AB-B646-4E5B-A058-52EF07B8351C}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "base", "base.vcproj", "{ACAC8E18-F003-4881-9BA0-C7718AC5FFD5}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D8E84C85-89D3-4B8D-9A3A-C44B63C3383A}" - ProjectSection(SolutionItems) = preProject - ..\LICENSE.txt = ..\LICENSE.txt - ..\README.txt = ..\README.txt - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {EF5E94AB-B646-4E5B-A058-52EF07B8351C}.Debug|Win32.ActiveCfg = Debug|Win32 - {EF5E94AB-B646-4E5B-A058-52EF07B8351C}.Debug|Win32.Build.0 = Debug|Win32 - {EF5E94AB-B646-4E5B-A058-52EF07B8351C}.Release|Win32.ActiveCfg = Release|Win32 - {EF5E94AB-B646-4E5B-A058-52EF07B8351C}.Release|Win32.Build.0 = Release|Win32 - {ACAC8E18-F003-4881-9BA0-C7718AC5FFD5}.Debug|Win32.ActiveCfg = Debug|Win32 - {ACAC8E18-F003-4881-9BA0-C7718AC5FFD5}.Debug|Win32.Build.0 = Debug|Win32 - {ACAC8E18-F003-4881-9BA0-C7718AC5FFD5}.Release|Win32.ActiveCfg = Release|Win32 - {ACAC8E18-F003-4881-9BA0-C7718AC5FFD5}.Release|Win32.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection -EndGlobal
diff --git a/googleurl/build/googleurl.vcproj b/googleurl/build/googleurl.vcproj deleted file mode 100644 index 71b3123..0000000 --- a/googleurl/build/googleurl.vcproj +++ /dev/null
@@ -1,239 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioProject - ProjectType="Visual C++" - Version="8.00" - Name="googleurl" - ProjectGUID="{EF5E94AB-B646-4E5B-A058-52EF07B8351C}" - RootNamespace="googleurl" - Keyword="Win32Proj" - > - <Platforms> - <Platform - Name="Win32" - /> - </Platforms> - <ToolFiles> - </ToolFiles> - <Configurations> - <Configuration - Name="Debug|Win32" - ConfigurationType="4" - InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)../third_party/icu/build/using_icu.vsprops" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - <Configuration - Name="Release|Win32" - ConfigurationType="4" - InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\release.vsprops;$(SolutionDir)../third_party/icu/build/using_icu.vsprops" - > - <Tool - Name="VCPreBuildEventTool" - /> - <Tool - Name="VCCustomBuildTool" - /> - <Tool - Name="VCXMLDataGeneratorTool" - /> - <Tool - Name="VCWebServiceProxyGeneratorTool" - /> - <Tool - Name="VCMIDLTool" - /> - <Tool - Name="VCCLCompilerTool" - /> - <Tool - Name="VCManagedResourceCompilerTool" - /> - <Tool - Name="VCResourceCompilerTool" - /> - <Tool - Name="VCPreLinkEventTool" - /> - <Tool - Name="VCLibrarianTool" - /> - <Tool - Name="VCALinkTool" - /> - <Tool - Name="VCXDCMakeTool" - /> - <Tool - Name="VCBscMakeTool" - /> - <Tool - Name="VCFxCopTool" - /> - <Tool - Name="VCPostBuildEventTool" - /> - </Configuration> - </Configurations> - <References> - </References> - <Files> - <File - RelativePath="..\src\gurl.cc" - > - </File> - <File - RelativePath="..\src\gurl.h" - > - </File> - <File - RelativePath=".\README.txt" - > - </File> - <File - RelativePath="..\src\url_canon.h" - > - </File> - <File - RelativePath="..\src\url_canon_etc.cc" - > - </File> - <File - RelativePath="..\src\url_canon_fileurl.cc" - > - </File> - <File - RelativePath="..\src\url_canon_host.cc" - > - </File> - <File - RelativePath="..\src\url_canon_icu.cc" - > - </File> - <File - RelativePath="..\src\url_canon_icu.h" - > - </File> - <File - RelativePath="..\src\url_canon_internal.cc" - > - </File> - <File - RelativePath="..\src\url_canon_internal.h" - > - </File> - <File - RelativePath="..\src\url_canon_internal_file.h" - > - </File> - <File - RelativePath="..\src\url_canon_ip.cc" - > - </File> - <File - RelativePath="..\src\url_canon_ip.h" - > - </File> - <File - RelativePath="..\src\url_canon_mailtourl.cc" - > - </File> - <File - RelativePath="..\src\url_canon_path.cc" - > - </File> - <File - RelativePath="..\src\url_canon_pathurl.cc" - > - </File> - <File - RelativePath="..\src\url_canon_query.cc" - > - </File> - <File - RelativePath="..\src\url_canon_relative.cc" - > - </File> - <File - RelativePath="..\src\url_canon_stdstring.h" - > - </File> - <File - RelativePath="..\src\url_canon_stdurl.cc" - > - </File> - <File - RelativePath="..\src\url_file.h" - > - </File> - <File - RelativePath="..\src\url_parse.cc" - > - </File> - <File - RelativePath="..\src\url_parse.h" - > - </File> - <File - RelativePath="..\src\url_parse_file.cc" - > - </File> - <File - RelativePath="..\src\url_parse_internal.h" - > - </File> - <File - RelativePath="..\src\url_util.cc" - > - </File> - <File - RelativePath="..\src\url_util.h" - > - </File> - </Files> - <Globals> - </Globals> -</VisualStudioProject>
diff --git a/googleurl/build/release.vsprops b/googleurl/build/release.vsprops deleted file mode 100644 index 2e59356..0000000 --- a/googleurl/build/release.vsprops +++ /dev/null
@@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioPropertySheet - ProjectType="Visual C++" - Version="8.00" - Name="release" - > - <Tool - Name="VCCLCompilerTool" - WholeProgramOptimization="true" - PreprocessorDefinitions="NDEBUG" - /> - <Tool - Name="VCLibrarianTool" - AdditionalOptions="/ltcg" - /> - <Tool - Name="VCLinkerTool" - LinkIncremental="1" - OptimizeReferences="2" - EnableCOMDATFolding="2" - LinkTimeCodeGeneration="1" - /> -</VisualStudioPropertySheet>
diff --git a/googleurl/src/gurl.cc b/googleurl/src/gurl.cc deleted file mode 100644 index a0bfd26..0000000 --- a/googleurl/src/gurl.cc +++ /dev/null
@@ -1,449 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -#ifdef WIN32 -#include <windows.h> -#else -#include <pthread.h> -#endif - -#include <algorithm> - -#include "googleurl/src/gurl.h" - -#include "base/logging.h" -#include "googleurl/src/url_canon_stdstring.h" -#include "googleurl/src/url_util.h" - -namespace { - -// External template that can handle initialization of either character type. -// The input spec is given, and the canonical version will be placed in -// |*canonical|, along with the parsing of the canonical spec in |*parsed|. -template<typename STR> -bool InitCanonical(const STR& input_spec, - std::string* canonical, - url_parse::Parsed* parsed) { - // Reserve enough room in the output for the input, plus some extra so that - // we have room if we have to escape a few things without reallocating. - canonical->reserve(input_spec.size() + 32); - url_canon::StdStringCanonOutput output(canonical); - bool success = url_util::Canonicalize( - input_spec.data(), static_cast<int>(input_spec.length()), - NULL, &output, parsed); - - output.Complete(); // Must be done before using string. - return success; -} - -static std::string* empty_string = NULL; -static GURL* empty_gurl = NULL; - -#ifdef WIN32 - -// Returns a static reference to an empty string for returning a reference -// when there is no underlying string. -const std::string& EmptyStringForGURL() { - // Avoid static object construction/destruction on startup/shutdown. - if (!empty_string) { - // Create the string. Be careful that we don't break in the case that this - // is being called from multiple threads. Statics are not threadsafe. - std::string* new_empty_string = new std::string; - if (InterlockedCompareExchangePointer( - reinterpret_cast<PVOID*>(&empty_string), new_empty_string, NULL)) { - // The old value was non-NULL, so no replacement was done. Another - // thread did the initialization out from under us. - delete new_empty_string; - } - } - return *empty_string; -} - -#else - -static pthread_once_t empty_string_once = PTHREAD_ONCE_INIT; -static pthread_once_t empty_gurl_once = PTHREAD_ONCE_INIT; - -void EmptyStringForGURLOnce(void) { - empty_string = new std::string; -} - -const std::string& EmptyStringForGURL() { - // Avoid static object construction/destruction on startup/shutdown. - pthread_once(&empty_string_once, EmptyStringForGURLOnce); - return *empty_string; -} - -#endif // WIN32 - -} // namespace - -GURL::GURL() : is_valid_(false) { -} - -GURL::GURL(const GURL& other) - : spec_(other.spec_), - is_valid_(other.is_valid_), - parsed_(other.parsed_) { -} - -GURL::GURL(const std::string& url_string) { - is_valid_ = InitCanonical(url_string, &spec_, &parsed_); -} - -GURL::GURL(const string16& url_string) { - is_valid_ = InitCanonical(url_string, &spec_, &parsed_); -} - -GURL::GURL(const char* canonical_spec, size_t canonical_spec_len, - const url_parse::Parsed& parsed, bool is_valid) - : spec_(canonical_spec, canonical_spec_len), - is_valid_(is_valid), - parsed_(parsed) { -#ifndef NDEBUG - // For testing purposes, check that the parsed canonical URL is identical to - // what we would have produced. Skip checking for invalid URLs have no meaning - // and we can't always canonicalize then reproducabely. - if (is_valid_) { - GURL test_url(spec_); - - DCHECK(test_url.is_valid_ == is_valid_); - DCHECK(test_url.spec_ == spec_); - - DCHECK(test_url.parsed_.scheme == parsed_.scheme); - DCHECK(test_url.parsed_.username == parsed_.username); - DCHECK(test_url.parsed_.password == parsed_.password); - DCHECK(test_url.parsed_.host == parsed_.host); - DCHECK(test_url.parsed_.port == parsed_.port); - DCHECK(test_url.parsed_.path == parsed_.path); - DCHECK(test_url.parsed_.query == parsed_.query); - DCHECK(test_url.parsed_.ref == parsed_.ref); - } -#endif -} - -const std::string& GURL::spec() const { - if (is_valid_ || spec_.empty()) - return spec_; - - DCHECK(false) << "Trying to get the spec of an invalid URL!"; - return EmptyStringForGURL(); -} - -GURL GURL::Resolve(const std::string& relative) const { - return ResolveWithCharsetConverter(relative, NULL); -} -GURL GURL::Resolve(const string16& relative) const { - return ResolveWithCharsetConverter(relative, NULL); -} - -// Note: code duplicated below (it's inconvenient to use a template here). -GURL GURL::ResolveWithCharsetConverter( - const std::string& relative, - url_canon::CharsetConverter* charset_converter) const { - // Not allowed for invalid URLs. - if (!is_valid_) - return GURL(); - - GURL result; - - // Reserve enough room in the output for the input, plus some extra so that - // we have room if we have to escape a few things without reallocating. - result.spec_.reserve(spec_.size() + 32); - url_canon::StdStringCanonOutput output(&result.spec_); - - if (!url_util::ResolveRelative( - spec_.data(), static_cast<int>(spec_.length()), parsed_, - relative.data(), static_cast<int>(relative.length()), - charset_converter, &output, &result.parsed_)) { - // Error resolving, return an empty URL. - return GURL(); - } - - output.Complete(); - result.is_valid_ = true; - return result; -} - -// Note: code duplicated above (it's inconvenient to use a template here). -GURL GURL::ResolveWithCharsetConverter( - const string16& relative, - url_canon::CharsetConverter* charset_converter) const { - // Not allowed for invalid URLs. - if (!is_valid_) - return GURL(); - - GURL result; - - // Reserve enough room in the output for the input, plus some extra so that - // we have room if we have to escape a few things without reallocating. - result.spec_.reserve(spec_.size() + 32); - url_canon::StdStringCanonOutput output(&result.spec_); - - if (!url_util::ResolveRelative( - spec_.data(), static_cast<int>(spec_.length()), parsed_, - relative.data(), static_cast<int>(relative.length()), - charset_converter, &output, &result.parsed_)) { - // Error resolving, return an empty URL. - return GURL(); - } - - output.Complete(); - result.is_valid_ = true; - return result; -} - -// Note: code duplicated below (it's inconvenient to use a template here). -GURL GURL::ReplaceComponents( - const url_canon::Replacements<char>& replacements) const { - GURL result; - - // Not allowed for invalid URLs. - if (!is_valid_) - return GURL(); - - // Reserve enough room in the output for the input, plus some extra so that - // we have room if we have to escape a few things without reallocating. - result.spec_.reserve(spec_.size() + 32); - url_canon::StdStringCanonOutput output(&result.spec_); - - result.is_valid_ = url_util::ReplaceComponents( - spec_.data(), static_cast<int>(spec_.length()), parsed_, replacements, - NULL, &output, &result.parsed_); - - output.Complete(); - return result; -} - -// Note: code duplicated above (it's inconvenient to use a template here). -GURL GURL::ReplaceComponents( - const url_canon::Replacements<char16>& replacements) const { - GURL result; - - // Not allowed for invalid URLs. - if (!is_valid_) - return GURL(); - - // Reserve enough room in the output for the input, plus some extra so that - // we have room if we have to escape a few things without reallocating. - result.spec_.reserve(spec_.size() + 32); - url_canon::StdStringCanonOutput output(&result.spec_); - - result.is_valid_ = url_util::ReplaceComponents( - spec_.data(), static_cast<int>(spec_.length()), parsed_, replacements, - NULL, &output, &result.parsed_); - - output.Complete(); - return result; -} - -GURL GURL::GetOrigin() const { - // This doesn't make sense for invalid or nonstandard URLs, so return - // the empty URL - if (!is_valid_ || !IsStandard()) - return GURL(); - - url_canon::Replacements<char> replacements; - replacements.ClearUsername(); - replacements.ClearPassword(); - replacements.ClearPath(); - replacements.ClearQuery(); - replacements.ClearRef(); - - return ReplaceComponents(replacements); -} - -GURL GURL::GetWithEmptyPath() const { - // This doesn't make sense for invalid or nonstandard URLs, so return - // the empty URL. - if (!is_valid_ || !IsStandard()) - return GURL(); - - // We could optimize this since we know that the URL is canonical, and we are - // appending a canonical path, so avoiding re-parsing. - GURL other(*this); - if (parsed_.path.len == 0) - return other; - - // Clear everything after the path. - other.parsed_.query.reset(); - other.parsed_.ref.reset(); - - // Set the path, since the path is longer than one, we can just set the - // first character and resize. - other.spec_[other.parsed_.path.begin] = '/'; - other.parsed_.path.len = 1; - other.spec_.resize(other.parsed_.path.begin + 1); - return other; -} - -bool GURL::IsStandard() const { - return url_util::IsStandard(spec_.data(), parsed_.scheme); -} - -bool GURL::SchemeIs(const char* lower_ascii_scheme) const { - if (parsed_.scheme.len <= 0) - return lower_ascii_scheme == NULL; - return url_util::LowerCaseEqualsASCII(spec_.data() + parsed_.scheme.begin, - spec_.data() + parsed_.scheme.end(), - lower_ascii_scheme); -} - -int GURL::IntPort() const { - if (parsed_.port.is_nonempty()) - return url_parse::ParsePort(spec_.data(), parsed_.port); - return url_parse::PORT_UNSPECIFIED; -} - -int GURL::EffectiveIntPort() const { - int int_port = IntPort(); - if (int_port == url_parse::PORT_UNSPECIFIED && IsStandard()) - return url_canon::DefaultPortForScheme(spec_.data() + parsed_.scheme.begin, - parsed_.scheme.len); - return int_port; -} - -std::string GURL::ExtractFileName() const { - url_parse::Component file_component; - url_parse::ExtractFileName(spec_.data(), parsed_.path, &file_component); - return ComponentString(file_component); -} - -std::string GURL::PathForRequest() const { - DCHECK(parsed_.path.len > 0) << "Canonical path for requests should be non-empty"; - if (parsed_.ref.len >= 0) { - // Clip off the reference when it exists. The reference starts after the # - // sign, so we have to subtract one to also remove it. - return std::string(spec_, parsed_.path.begin, - parsed_.ref.begin - parsed_.path.begin - 1); - } - - // Use everything form the path to the end. - return std::string(spec_, parsed_.path.begin); -} - -std::string GURL::HostNoBrackets() const { - // If host looks like an IPv6 literal, strip the square brackets. - url_parse::Component h(parsed_.host); - if (h.len >= 2 && spec_[h.begin] == '[' && spec_[h.end() - 1] == ']') { - h.begin++; - h.len -= 2; - } - return ComponentString(h); -} - -bool GURL::HostIsIPAddress() const { - if (!is_valid_ || spec_.empty()) - return false; - - url_canon::RawCanonOutputT<char, 128> ignored_output; - url_canon::CanonHostInfo host_info; - url_canon::CanonicalizeIPAddress(spec_.c_str(), parsed_.host, - &ignored_output, &host_info); - return host_info.IsIPAddress(); -} - -#ifdef WIN32 - -const GURL& GURL::EmptyGURL() { - // Avoid static object construction/destruction on startup/shutdown. - if (!empty_gurl) { - // Create the string. Be careful that we don't break in the case that this - // is being called from multiple threads. - GURL* new_empty_gurl = new GURL; - if (InterlockedCompareExchangePointer( - reinterpret_cast<PVOID*>(&empty_gurl), new_empty_gurl, NULL)) { - // The old value was non-NULL, so no replacement was done. Another - // thread did the initialization out from under us. - delete new_empty_gurl; - } - } - return *empty_gurl; -} - -#else - -void EmptyGURLOnce(void) { - empty_gurl = new GURL; -} - -const GURL& GURL::EmptyGURL() { - // Avoid static object construction/destruction on startup/shutdown. - pthread_once(&empty_gurl_once, EmptyGURLOnce); - return *empty_gurl; -} - -#endif // WIN32 - -bool GURL::DomainIs(const char* lower_ascii_domain, - int domain_len) const { - // Return false if this URL is not valid or domain is empty. - if (!is_valid_ || !parsed_.host.is_nonempty() || !domain_len) - return false; - - // Check whether the host name is end with a dot. If yes, treat it - // the same as no-dot unless the input comparison domain is end - // with dot. - const char* last_pos = spec_.data() + parsed_.host.end() - 1; - int host_len = parsed_.host.len; - if ('.' == *last_pos && '.' != lower_ascii_domain[domain_len - 1]) { - last_pos--; - host_len--; - } - - // Return false if host's length is less than domain's length. - if (host_len < domain_len) - return false; - - // Compare this url whether belong specific domain. - const char* start_pos = spec_.data() + parsed_.host.begin + - host_len - domain_len; - - if (!url_util::LowerCaseEqualsASCII(start_pos, - last_pos + 1, - lower_ascii_domain, - lower_ascii_domain + domain_len)) - return false; - - // Check whether host has right domain start with dot, make sure we got - // right domain range. For example www.google.com has domain - // "google.com" but www.iamnotgoogle.com does not. - if ('.' != lower_ascii_domain[0] && host_len > domain_len && - '.' != *(start_pos - 1)) - return false; - - return true; -} - -void GURL::Swap(GURL* other) { - spec_.swap(other->spec_); - std::swap(is_valid_, other->is_valid_); - std::swap(parsed_, other->parsed_); -} -
diff --git a/googleurl/src/gurl_test_main.cc b/googleurl/src/gurl_test_main.cc deleted file mode 100644 index 9a7c9f4..0000000 --- a/googleurl/src/gurl_test_main.cc +++ /dev/null
@@ -1,97 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -#include "build/build_config.h" - -#if defined(OS_WIN) -#include <windows.h> -#endif - -#include <string> - -#include "testing/gtest/include/gtest/gtest.h" -#include "unicode/putil.h" -#include "unicode/udata.h" - -#define ICU_UTIL_DATA_SHARED 1 -#define ICU_UTIL_DATA_STATIC 2 - -#ifndef ICU_UTIL_DATA_IMPL - -#if defined(OS_WIN) -#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_SHARED -#elif defined(OS_MACOSX) -#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_STATIC -#elif defined(OS_LINUX) -#define ICU_UTIL_DATA_IMPL ICU_UTIL_DATA_FILE -#endif - -#endif // ICU_UTIL_DATA_IMPL - -#if defined(OS_WIN) -#define ICU_UTIL_DATA_SYMBOL "icudt" U_ICU_VERSION_SHORT "_dat" -#define ICU_UTIL_DATA_SHARED_MODULE_NAME "icudt" U_ICU_VERSION_SHORT ".dll" -#endif - -bool InitializeICU() { -#if (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_SHARED) - // We expect to find the ICU data module alongside the current module. - // Because the module name is ASCII-only, "A" API should be safe. - HMODULE module = LoadLibraryA(ICU_UTIL_DATA_SHARED_MODULE_NAME); - if (!module) - return false; - - FARPROC addr = GetProcAddress(module, ICU_UTIL_DATA_SYMBOL); - if (!addr) - return false; - - UErrorCode err = U_ZERO_ERROR; - udata_setCommonData(reinterpret_cast<void*>(addr), &err); - return err == U_ZERO_ERROR; -#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_STATIC) - // Mac bundles the ICU data in. - return true; -#elif (ICU_UTIL_DATA_IMPL == ICU_UTIL_DATA_FILE) - // We expect to find the ICU data module alongside the current module. - u_setDataDirectory("."); - // Only look for the packaged data file; - // the default behavior is to look for individual files. - UErrorCode err = U_ZERO_ERROR; - udata_setFileAccess(UDATA_ONLY_PACKAGES, &err); - return err == U_ZERO_ERROR; -#endif -} - -int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - - InitializeICU(); - - return RUN_ALL_TESTS(); -}
diff --git a/googleurl/src/gurl_unittest.cc b/googleurl/src/gurl_unittest.cc deleted file mode 100644 index b548cc2..0000000 --- a/googleurl/src/gurl_unittest.cc +++ /dev/null
@@ -1,432 +0,0 @@ -// Copyright 2007 Google Inc. All Rights Reserved. -// Author: brettw@google.com (Brett Wilson) - -#include "googleurl/src/gurl.h" -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_test_utils.h" -#include "testing/base/public/gunit.h" - -// Some implementations of base/basictypes.h may define ARRAYSIZE. -// If it's not defined, we define it to the ARRAYSIZE_UNSAFE macro -// which is in our version of basictypes.h. -#ifndef ARRAYSIZE -#define ARRAYSIZE ARRAYSIZE_UNSAFE -#endif - -using url_test_utils::WStringToUTF16; -using url_test_utils::ConvertUTF8ToUTF16; - -namespace { - -template<typename CHAR> -void SetupReplacement(void (url_canon::Replacements<CHAR>::*func)(const CHAR*, - const url_parse::Component&), - url_canon::Replacements<CHAR>* replacements, - const CHAR* str) { - if (str) { - url_parse::Component comp; - if (str[0]) - comp.len = static_cast<int>(strlen(str)); - (replacements->*func)(str, comp); - } -} - -// Returns the canonicalized string for the given URL string for the -// GURLTest.Types test. -std::string TypesTestCase(const char* src) { - GURL gurl(src); - return gurl.possibly_invalid_spec(); -} - -} // namespace - -// Different types of URLs should be handled differently by url_util, and -// handed off to different canonicalizers. -TEST(GURLTest, Types) { - // URLs with unknown schemes should be treated as path URLs, even when they - // have things like "://". - EXPECT_EQ("something:///HOSTNAME.com/", - TypesTestCase("something:///HOSTNAME.com/")); - - // In the reverse, known schemes should always trigger standard URL handling. - EXPECT_EQ("http://hostname.com/", TypesTestCase("http:HOSTNAME.com")); - EXPECT_EQ("http://hostname.com/", TypesTestCase("http:/HOSTNAME.com")); - EXPECT_EQ("http://hostname.com/", TypesTestCase("http://HOSTNAME.com")); - EXPECT_EQ("http://hostname.com/", TypesTestCase("http:///HOSTNAME.com")); - -#ifdef WIN32 - // URLs that look like absolute Windows drive specs. - EXPECT_EQ("file:///C:/foo.txt", TypesTestCase("c:\\foo.txt")); - EXPECT_EQ("file:///Z:/foo.txt", TypesTestCase("Z|foo.txt")); - EXPECT_EQ("file://server/foo.txt", TypesTestCase("\\\\server\\foo.txt")); - EXPECT_EQ("file://server/foo.txt", TypesTestCase("//server/foo.txt")); -#endif -} - -// Test the basic creation and querying of components in a GURL. We assume -// the parser is already tested and works, so we are mostly interested if the -// object does the right thing with the results. -TEST(GURLTest, Components) { - GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); - EXPECT_TRUE(url.is_valid()); - EXPECT_TRUE(url.SchemeIs("http")); - EXPECT_FALSE(url.SchemeIsFile()); - - // This is the narrow version of the URL, which should match the wide input. - EXPECT_EQ("http://user:pass@google.com:99/foo;bar?q=a#ref", url.spec()); - - EXPECT_EQ("http", url.scheme()); - EXPECT_EQ("user", url.username()); - EXPECT_EQ("pass", url.password()); - EXPECT_EQ("google.com", url.host()); - EXPECT_EQ("99", url.port()); - EXPECT_EQ(99, url.IntPort()); - EXPECT_EQ("/foo;bar", url.path()); - EXPECT_EQ("q=a", url.query()); - EXPECT_EQ("ref", url.ref()); -} - -TEST(GURLTest, Empty) { - GURL url; - EXPECT_FALSE(url.is_valid()); - EXPECT_EQ("", url.spec()); - - EXPECT_EQ("", url.scheme()); - EXPECT_EQ("", url.username()); - EXPECT_EQ("", url.password()); - EXPECT_EQ("", url.host()); - EXPECT_EQ("", url.port()); - EXPECT_EQ(url_parse::PORT_UNSPECIFIED, url.IntPort()); - EXPECT_EQ("", url.path()); - EXPECT_EQ("", url.query()); - EXPECT_EQ("", url.ref()); -} - -TEST(GURLTest, Copy) { - GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); - - GURL url2(url); - EXPECT_TRUE(url2.is_valid()); - - EXPECT_EQ("http://user:pass@google.com:99/foo;bar?q=a#ref", url2.spec()); - EXPECT_EQ("http", url2.scheme()); - EXPECT_EQ("user", url2.username()); - EXPECT_EQ("pass", url2.password()); - EXPECT_EQ("google.com", url2.host()); - EXPECT_EQ("99", url2.port()); - EXPECT_EQ(99, url2.IntPort()); - EXPECT_EQ("/foo;bar", url2.path()); - EXPECT_EQ("q=a", url2.query()); - EXPECT_EQ("ref", url2.ref()); - - // Copying of invalid URL should be invalid - GURL invalid; - GURL invalid2(invalid); - EXPECT_FALSE(invalid2.is_valid()); - EXPECT_EQ("", invalid2.spec()); - EXPECT_EQ("", invalid2.scheme()); - EXPECT_EQ("", invalid2.username()); - EXPECT_EQ("", invalid2.password()); - EXPECT_EQ("", invalid2.host()); - EXPECT_EQ("", invalid2.port()); - EXPECT_EQ(url_parse::PORT_UNSPECIFIED, invalid2.IntPort()); - EXPECT_EQ("", invalid2.path()); - EXPECT_EQ("", invalid2.query()); - EXPECT_EQ("", invalid2.ref()); -} - -// Given an invalid URL, we should still get most of the components. -TEST(GURLTest, Invalid) { - GURL url("http:google.com:foo"); - EXPECT_FALSE(url.is_valid()); - EXPECT_EQ("http://google.com:foo/", url.possibly_invalid_spec()); - - EXPECT_EQ("http", url.scheme()); - EXPECT_EQ("", url.username()); - EXPECT_EQ("", url.password()); - EXPECT_EQ("google.com", url.host()); - EXPECT_EQ("foo", url.port()); - EXPECT_EQ(url_parse::PORT_INVALID, url.IntPort()); - EXPECT_EQ("/", url.path()); - EXPECT_EQ("", url.query()); - EXPECT_EQ("", url.ref()); -} - -TEST(GURLTest, Resolve) { - // The tricky cases for relative URL resolving are tested in the - // canonicalizer unit test. Here, we just test that the GURL integration - // works properly. - struct ResolveCase { - const char* base; - const char* relative; - bool expected_valid; - const char* expected; - } resolve_cases[] = { - {"http://www.google.com/", "foo.html", true, "http://www.google.com/foo.html"}, - {"http://www.google.com/", "http://images.google.com/foo.html", true, "http://images.google.com/foo.html"}, - {"http://www.google.com/blah/bloo?c#d", "../../../hello/./world.html?a#b", true, "http://www.google.com/hello/world.html?a#b"}, - {"http://www.google.com/foo#bar", "#com", true, "http://www.google.com/foo#com"}, - {"http://www.google.com/", "Https:images.google.com", true, "https://images.google.com/"}, - // Unknown schemes are not standard. - {"data:blahblah", "http://google.com/", true, "http://google.com/"}, - {"data:blahblah", "http:google.com", true, "http://google.com/"}, - {"data:/blahblah", "file.html", false, ""}, - }; - - for (size_t i = 0; i < ARRAYSIZE(resolve_cases); i++) { - // 8-bit code path. - GURL input(resolve_cases[i].base); - GURL output = input.Resolve(resolve_cases[i].relative); - EXPECT_EQ(resolve_cases[i].expected_valid, output.is_valid()) << i; - EXPECT_EQ(resolve_cases[i].expected, output.spec()) << i; - - // Wide code path. - GURL inputw(ConvertUTF8ToUTF16(resolve_cases[i].base)); - GURL outputw = - input.Resolve(ConvertUTF8ToUTF16(resolve_cases[i].relative)); - EXPECT_EQ(resolve_cases[i].expected_valid, outputw.is_valid()) << i; - EXPECT_EQ(resolve_cases[i].expected, outputw.spec()) << i; - } -} - -TEST(GURLTest, GetOrigin) { - struct TestCase { - const char* input; - const char* expected; - } cases[] = { - {"http://www.google.com", "http://www.google.com/"}, - {"javascript:window.alert(\"hello,world\");", ""}, - {"http://user:pass@www.google.com:21/blah#baz", "http://www.google.com:21/"}, - {"http://user@www.google.com", "http://www.google.com/"}, - {"http://:pass@www.google.com", "http://www.google.com/"}, - {"http://:@www.google.com", "http://www.google.com/"}, - }; - for (size_t i = 0; i < ARRAYSIZE(cases); i++) { - GURL url(cases[i].input); - GURL origin = url.GetOrigin(); - EXPECT_EQ(cases[i].expected, origin.spec()); - } -} - -TEST(GURLTest, GetWithEmptyPath) { - struct TestCase { - const char* input; - const char* expected; - } cases[] = { - {"http://www.google.com", "http://www.google.com/"}, - {"javascript:window.alert(\"hello, world\");", ""}, - {"http://www.google.com/foo/bar.html?baz=22", "http://www.google.com/"}, - }; - - for (size_t i = 0; i < ARRAYSIZE(cases); i++) { - GURL url(cases[i].input); - GURL empty_path = url.GetWithEmptyPath(); - EXPECT_EQ(cases[i].expected, empty_path.spec()); - } -} - -TEST(GURLTest, Replacements) { - // The url canonicalizer replacement test will handle most of these case. - // The most important thing to do here is to check that the proper - // canonicalizer gets called based on the scheme of the input. - struct ReplaceCase { - const char* base; - const char* scheme; - const char* username; - const char* password; - const char* host; - const char* port; - const char* path; - const char* query; - const char* ref; - const char* expected; - } replace_cases[] = { - {"http://www.google.com/foo/bar.html?foo#bar", NULL, NULL, NULL, NULL, NULL, "/", "", "", "http://www.google.com/"}, - {"http://www.google.com/foo/bar.html?foo#bar", "javascript", "", "", "", "", "window.open('foo');", "", "", "javascript:window.open('foo');"}, - {"file:///C:/foo/bar.txt", "http", NULL, NULL, "www.google.com", "99", "/foo","search", "ref", "http://www.google.com:99/foo?search#ref"}, -#ifdef WIN32 - {"http://www.google.com/foo/bar.html?foo#bar", "file", "", "", "", "", "c:\\", "", "", "file:///C:/"}, -#endif - }; - - for (size_t i = 0; i < ARRAYSIZE(replace_cases); i++) { - const ReplaceCase& cur = replace_cases[i]; - GURL url(cur.base); - GURL::Replacements repl; - SetupReplacement(&GURL::Replacements::SetScheme, &repl, cur.scheme); - SetupReplacement(&GURL::Replacements::SetUsername, &repl, cur.username); - SetupReplacement(&GURL::Replacements::SetPassword, &repl, cur.password); - SetupReplacement(&GURL::Replacements::SetHost, &repl, cur.host); - SetupReplacement(&GURL::Replacements::SetPort, &repl, cur.port); - SetupReplacement(&GURL::Replacements::SetPath, &repl, cur.path); - SetupReplacement(&GURL::Replacements::SetQuery, &repl, cur.query); - SetupReplacement(&GURL::Replacements::SetRef, &repl, cur.ref); - GURL output = url.ReplaceComponents(repl); - - EXPECT_EQ(replace_cases[i].expected, output.spec()); - } -} - -TEST(GURLTest, PathForRequest) { - struct TestCase { - const char* input; - const char* expected; - } cases[] = { - {"http://www.google.com", "/"}, - {"http://www.google.com/", "/"}, - {"http://www.google.com/foo/bar.html?baz=22", "/foo/bar.html?baz=22"}, - {"http://www.google.com/foo/bar.html#ref", "/foo/bar.html"}, - {"http://www.google.com/foo/bar.html?query#ref", "/foo/bar.html?query"}, - }; - - for (size_t i = 0; i < ARRAYSIZE(cases); i++) { - GURL url(cases[i].input); - std::string path_request = url.PathForRequest(); - EXPECT_EQ(cases[i].expected, path_request); - } -} - -TEST(GURLTest, EffectiveIntPort) { - struct PortTest { - const char* spec; - int expected_int_port; - } port_tests[] = { - // http - {"http://www.google.com/", 80}, - {"http://www.google.com:80/", 80}, - {"http://www.google.com:443/", 443}, - - // https - {"https://www.google.com/", 443}, - {"https://www.google.com:443/", 443}, - {"https://www.google.com:80/", 80}, - - // ftp - {"ftp://www.google.com/", 21}, - {"ftp://www.google.com:21/", 21}, - {"ftp://www.google.com:80/", 80}, - - // gopher - {"gopher://www.google.com/", 70}, - {"gopher://www.google.com:70/", 70}, - {"gopher://www.google.com:80/", 80}, - - // file - no port - {"file://www.google.com/", url_parse::PORT_UNSPECIFIED}, - {"file://www.google.com:443/", url_parse::PORT_UNSPECIFIED}, - - // data - no port - {"data:www.google.com:90", url_parse::PORT_UNSPECIFIED}, - {"data:www.google.com", url_parse::PORT_UNSPECIFIED}, - }; - - for (size_t i = 0; i < ARRAYSIZE(port_tests); i++) { - GURL url(port_tests[i].spec); - EXPECT_EQ(port_tests[i].expected_int_port, url.EffectiveIntPort()); - } -} - -TEST(GURLTest, IPAddress) { - struct IPTest { - const char* spec; - bool expected_ip; - } ip_tests[] = { - {"http://www.google.com/", false}, - {"http://192.168.9.1/", true}, - {"http://192.168.9.1.2/", false}, - {"http://192.168.m.1/", false}, - {"http://2001:db8::1/", false}, - {"http://[2001:db8::1]/", true}, - {"", false}, - {"some random input!", false}, - }; - - for (size_t i = 0; i < ARRAYSIZE(ip_tests); i++) { - GURL url(ip_tests[i].spec); - EXPECT_EQ(ip_tests[i].expected_ip, url.HostIsIPAddress()); - } -} - -TEST(GURLTest, HostNoBrackets) { - struct TestCase { - const char* input; - const char* expected_host; - const char* expected_plainhost; - } cases[] = { - {"http://www.google.com", "www.google.com", "www.google.com"}, - {"http://[2001:db8::1]/", "[2001:db8::1]", "2001:db8::1"}, - {"http://[::]/", "[::]", "::"}, - - // Don't require a valid URL, but don't crash either. - {"http://[]/", "[]", ""}, - {"http://[x]/", "[x]", "x"}, - {"http://[x/", "[x", "[x"}, - {"http://x]/", "x]", "x]"}, - {"http://[/", "[", "["}, - {"http://]/", "]", "]"}, - {"", "", ""}, - }; - for (size_t i = 0; i < ARRAYSIZE(cases); i++) { - GURL url(cases[i].input); - EXPECT_EQ(cases[i].expected_host, url.host()); - EXPECT_EQ(cases[i].expected_plainhost, url.HostNoBrackets()); - } -} - -TEST(GURLTest, DomainIs) { - const char google_domain[] = "google.com"; - - GURL url_1("http://www.google.com:99/foo"); - EXPECT_TRUE(url_1.DomainIs(google_domain)); - - GURL url_2("http://google.com:99/foo"); - EXPECT_TRUE(url_2.DomainIs(google_domain)); - - GURL url_3("http://google.com./foo"); - EXPECT_TRUE(url_3.DomainIs(google_domain)); - - GURL url_4("http://google.com/foo"); - EXPECT_FALSE(url_4.DomainIs("google.com.")); - - GURL url_5("http://google.com./foo"); - EXPECT_TRUE(url_5.DomainIs("google.com.")); - - GURL url_6("http://www.google.com./foo"); - EXPECT_TRUE(url_6.DomainIs(".com.")); - - GURL url_7("http://www.balabala.com/foo"); - EXPECT_FALSE(url_7.DomainIs(google_domain)); - - GURL url_8("http://www.google.com.cn/foo"); - EXPECT_FALSE(url_8.DomainIs(google_domain)); - - GURL url_9("http://www.iamnotgoogle.com/foo"); - EXPECT_FALSE(url_9.DomainIs(google_domain)); - - GURL url_10("http://www.iamnotgoogle.com../foo"); - EXPECT_FALSE(url_10.DomainIs(".com")); -} - -// Newlines should be stripped from inputs. -TEST(GURLTest, Newlines) { - // Constructor. - GURL url_1(" \t ht\ntp://\twww.goo\rgle.com/as\ndf \n "); - EXPECT_EQ("http://www.google.com/asdf", url_1.spec()); - - // Relative path resolver. - GURL url_2 = url_1.Resolve(" \n /fo\to\r "); - EXPECT_EQ("http://www.google.com/foo", url_2.spec()); - - // Note that newlines are NOT stripped from ReplaceComponents. -} - -TEST(GURLTest, IsStandard) { - GURL a("http:foo/bar"); - EXPECT_TRUE(a.IsStandard()); - - GURL b("foo:bar/baz"); - EXPECT_FALSE(b.IsStandard()); - - GURL c("foo://bar/baz"); - EXPECT_FALSE(c.IsStandard()); -}
diff --git a/googleurl/src/url_canon_fileurl.cc b/googleurl/src/url_canon_fileurl.cc deleted file mode 100644 index 97023eb..0000000 --- a/googleurl/src/url_canon_fileurl.cc +++ /dev/null
@@ -1,215 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -// Functions for canonicalizing "file:" URLs. - -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" -#include "googleurl/src/url_file.h" -#include "googleurl/src/url_parse_internal.h" - -namespace url_canon { - -namespace { - -#ifdef WIN32 - -// Given a pointer into the spec, this copies and canonicalizes the drive -// letter and colon to the output, if one is found. If there is not a drive -// spec, it won't do anything. The index of the next character in the input -// spec is returned (after the colon when a drive spec is found, the begin -// offset if one is not). -template<typename CHAR> -int FileDoDriveSpec(const CHAR* spec, int begin, int end, - CanonOutput* output) { - // The path could be one of several things: /foo/bar, c:/foo/bar, /c:/foo, - // (with backslashes instead of slashes as well). - int num_slashes = url_parse::CountConsecutiveSlashes(spec, begin, end); - int after_slashes = begin + num_slashes; - - if (!url_parse::DoesBeginWindowsDriveSpec(spec, after_slashes, end)) - return begin; // Haven't consumed any characters - - // A drive spec is the start of a path, so we need to add a slash for the - // authority terminator (typically the third slash). - output->push_back('/'); - - // DoesBeginWindowsDriveSpec will ensure that the drive letter is valid - // and that it is followed by a colon/pipe. - - // Normalize Windows drive letters to uppercase - if (spec[after_slashes] >= 'a' && spec[after_slashes] <= 'z') - output->push_back(spec[after_slashes] - 'a' + 'A'); - else - output->push_back(static_cast<char>(spec[after_slashes])); - - // Normalize the character following it to a colon rather than pipe. - output->push_back(':'); - return after_slashes + 2; -} - -#endif // WIN32 - -template<typename CHAR, typename UCHAR> -bool DoFileCanonicalizePath(const CHAR* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path) { - // Copies and normalizes the "c:" at the beginning, if present. - out_path->begin = output->length(); - int after_drive; -#ifdef WIN32 - after_drive = FileDoDriveSpec(spec, path.begin, path.end(), output); -#else - after_drive = path.begin; -#endif - - // Copies the rest of the path, starting from the slash following the - // drive colon (if any, Windows only), or the first slash of the path. - bool success = true; - if (after_drive < path.end()) { - // Use the regular path canonicalizer to canonicalize the rest of the - // path. Give it a fake output component to write into. DoCanonicalizeFile - // will compute the full path component. - url_parse::Component sub_path = - url_parse::MakeRange(after_drive, path.end()); - url_parse::Component fake_output_path; - success = CanonicalizePath(spec, sub_path, output, &fake_output_path); - } else { - // No input path, canonicalize to a slash. - output->push_back('/'); - } - - out_path->len = output->length() - out_path->begin; - return success; -} - -template<typename CHAR, typename UCHAR> -bool DoCanonicalizeFileURL(const URLComponentSource<CHAR>& source, - const url_parse::Parsed& parsed, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - // Things we don't set in file: URLs. - new_parsed->username = url_parse::Component(); - new_parsed->password = url_parse::Component(); - new_parsed->port = url_parse::Component(); - - // Scheme (known, so we don't bother running it through the more - // complicated scheme canonicalizer). - new_parsed->scheme.begin = output->length(); - output->Append("file://", 7); - new_parsed->scheme.len = 4; - - // Append the host. For many file URLs, this will be empty. For UNC, this - // will be present. - // TODO(brettw) This doesn't do any checking for host name validity. We - // should probably handle validity checking of UNC hosts differently than - // for regular IP hosts. - bool success = CanonicalizeHost(source.host, parsed.host, - output, &new_parsed->host); - success &= DoFileCanonicalizePath<CHAR, UCHAR>(source.path, parsed.path, - output, &new_parsed->path); - CanonicalizeQuery(source.query, parsed.query, query_converter, - output, &new_parsed->query); - - // Ignore failure for refs since the URL can probably still be loaded. - CanonicalizeRef(source.ref, parsed.ref, output, &new_parsed->ref); - - return success; -} - -} // namespace - -bool CanonicalizeFileURL(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - return DoCanonicalizeFileURL<char, unsigned char>( - URLComponentSource<char>(spec), parsed, query_converter, - output, new_parsed); -} - -bool CanonicalizeFileURL(const char16* spec, - int spec_len, - const url_parse::Parsed& parsed, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - return DoCanonicalizeFileURL<char16, char16>( - URLComponentSource<char16>(spec), parsed, query_converter, - output, new_parsed); -} - -bool FileCanonicalizePath(const char* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path) { - return DoFileCanonicalizePath<char, unsigned char>(spec, path, - output, out_path); -} - -bool FileCanonicalizePath(const char16* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path) { - return DoFileCanonicalizePath<char16, char16>(spec, path, - output, out_path); -} - -bool ReplaceFileURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char>& replacements, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); - SetupOverrideComponents(base, replacements, &source, &parsed); - return DoCanonicalizeFileURL<char, unsigned char>( - source, parsed, query_converter, output, new_parsed); -} - -bool ReplaceFileURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - RawCanonOutput<1024> utf8; - URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); - SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); - return DoCanonicalizeFileURL<char, unsigned char>( - source, parsed, query_converter, output, new_parsed); -} - -} // namespace url_canon
diff --git a/googleurl/src/url_canon_icu.cc b/googleurl/src/url_canon_icu.cc deleted file mode 100644 index b06808c..0000000 --- a/googleurl/src/url_canon_icu.cc +++ /dev/null
@@ -1,207 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -// ICU integration functions. - -#include <stdlib.h> -#include <string.h> -#include <unicode/ucnv.h> -#include <unicode/ucnv_cb.h> -#include <unicode/uidna.h> - -#include "googleurl/src/url_canon_icu.h" -#include "googleurl/src/url_canon_internal.h" // for _itoa_s - -#include "base/logging.h" - -namespace url_canon { - -namespace { - -// Called when converting a character that can not be represented, this will -// append an escaped version of the numerical character reference for that code -// point. It is of the form "Ӓ" and we will escape the non-digits to -// "%26%231234%3B". Why? This is what Netscape did back in the olden days. -void appendURLEscapedChar(const void* context, - UConverterFromUnicodeArgs* from_args, - const UChar* code_units, - int32_t length, - UChar32 code_point, - UConverterCallbackReason reason, - UErrorCode* err) { - if (reason == UCNV_UNASSIGNED) { - *err = U_ZERO_ERROR; - - const static int prefix_len = 6; - const static char prefix[prefix_len + 1] = "%26%23"; // "&#" percent-escaped - ucnv_cbFromUWriteBytes(from_args, prefix, prefix_len, 0, err); - - DCHECK(code_point < 0x110000); - char number[8]; // Max Unicode code point is 7 digits. - _itoa_s(code_point, number, 10); - int number_len = static_cast<int>(strlen(number)); - ucnv_cbFromUWriteBytes(from_args, number, number_len, 0, err); - - const static int postfix_len = 3; - const static char postfix[postfix_len + 1] = "%3B"; // ";" percent-escaped - ucnv_cbFromUWriteBytes(from_args, postfix, postfix_len, 0, err); - } -} - -// A class for scoping the installation of the invalid character callback. -class AppendHandlerInstaller { - public: - // The owner of this object must ensure that the converter is alive for the - // duration of this object's lifetime. - AppendHandlerInstaller(UConverter* converter) : converter_(converter) { - UErrorCode err = U_ZERO_ERROR; - ucnv_setFromUCallBack(converter_, appendURLEscapedChar, 0, - &old_callback_, &old_context_, &err); - } - - ~AppendHandlerInstaller() { - UErrorCode err = U_ZERO_ERROR; - ucnv_setFromUCallBack(converter_, old_callback_, old_context_, 0, 0, &err); - } - - private: - UConverter* converter_; - - UConverterFromUCallback old_callback_; - const void* old_context_; -}; - -} // namespace - -ICUCharsetConverter::ICUCharsetConverter(UConverter* converter) - : converter_(converter) { -} - -void ICUCharsetConverter::ConvertFromUTF16(const char16* input, - int input_len, - CanonOutput* output) { - // Install our error handler. It will be called for character that can not - // be represented in the destination character set. - AppendHandlerInstaller handler(converter_); - - int begin_offset = output->length(); - int dest_capacity = output->capacity() - begin_offset; - output->set_length(output->length()); - - do { - UErrorCode err = U_ZERO_ERROR; - char* dest = &output->data()[begin_offset]; - int required_capacity = ucnv_fromUChars(converter_, dest, dest_capacity, - input, input_len, &err); - if (err != U_BUFFER_OVERFLOW_ERROR) { - output->set_length(begin_offset + required_capacity); - return; - } - - // Output didn't fit, expand - dest_capacity = required_capacity; - output->Resize(begin_offset + dest_capacity); - } while (true); -} - -// Converts the Unicode input representing a hostname to ASCII using IDN rules. -// The output must be ASCII, but is represented as wide characters. -// -// On success, the output will be filled with the ASCII host name and it will -// return true. Unlike most other canonicalization functions, this assumes that -// the output is empty. The beginning of the host will be at offset 0, and -// the length of the output will be set to the length of the new host name. -// -// On error, this will return false. The output in this case is undefined. -bool IDNToASCII(const char16* src, int src_len, CanonOutputW* output) { - DCHECK(output->length() == 0); // Output buffer is assumed empty. - while (true) { - // Use ALLOW_UNASSIGNED to be more tolerant of hostnames that violate - // the spec (which do exist). This does not present any risk and is a - // little more future proof. - UErrorCode err = U_ZERO_ERROR; - int num_converted = uidna_IDNToASCII(src, src_len, output->data(), - output->capacity(), - UIDNA_ALLOW_UNASSIGNED, NULL, &err); - if (err == U_ZERO_ERROR) { - output->set_length(num_converted); - return true; - } - if (err != U_BUFFER_OVERFLOW_ERROR) - return false; // Unknown error, give up. - - // Not enough room in our buffer, expand. - output->Resize(output->capacity() * 2); - } -} - -bool ReadUTFChar(const char* str, int* begin, int length, - unsigned* code_point_out) { - int code_point; // Avoids warning when U8_NEXT writes -1 to it. - U8_NEXT(str, *begin, length, code_point); - *code_point_out = static_cast<unsigned>(code_point); - - // The ICU macro above moves to the next char, we want to point to the last - // char consumed. - (*begin)--; - - // Validate the decoded value. - if (U_IS_UNICODE_CHAR(code_point)) - return true; - *code_point_out = kUnicodeReplacementCharacter; - return false; -} - -bool ReadUTFChar(const char16* str, int* begin, int length, - unsigned* code_point) { - if (U16_IS_SURROGATE(str[*begin])) { - if (!U16_IS_SURROGATE_LEAD(str[*begin]) || *begin + 1 >= length || - !U16_IS_TRAIL(str[*begin + 1])) { - // Invalid surrogate pair. - *code_point = kUnicodeReplacementCharacter; - return false; - } else { - // Valid surrogate pair. - *code_point = U16_GET_SUPPLEMENTARY(str[*begin], str[*begin + 1]); - (*begin)++; - } - } else { - // Not a surrogate, just one 16-bit word. - *code_point = str[*begin]; - } - - if (U_IS_UNICODE_CHAR(*code_point)) - return true; - - // Invalid code point. - *code_point = kUnicodeReplacementCharacter; - return false; -} - -} // namespace url_canon
diff --git a/googleurl/src/url_canon_icu.h b/googleurl/src/url_canon_icu.h deleted file mode 100644 index 6bc52c3..0000000 --- a/googleurl/src/url_canon_icu.h +++ /dev/null
@@ -1,63 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -// ICU integration functions. - -#ifndef GOOGLEURL_SRC_URL_CANON_ICU_H__ -#define GOOGLEURL_SRC_URL_CANON_ICU_H__ - -#include "googleurl/src/url_canon.h" - -typedef struct UConverter UConverter; - -namespace url_canon { - -// An implementation of CharsetConverter that implementations can use to -// interface the canonicalizer with ICU's conversion routines. -class ICUCharsetConverter : public CharsetConverter { - public: - // Constructs a converter using an already-existing ICU character set - // converter. This converter is NOT owned by this object; the lifetime must - // be managed by the creator such that it is alive as long as this is. - GURL_API ICUCharsetConverter(UConverter* converter); - - GURL_API virtual ~ICUCharsetConverter() {} - - GURL_API virtual void ConvertFromUTF16(const char16* input, - int input_len, - CanonOutput* output); - - private: - // The ICU converter, not owned by this class. - UConverter* converter_; -}; - -} // namespace url_canon - -#endif // GOOGLEURL_SRC_URL_CANON_ICU_H__
diff --git a/googleurl/src/url_canon_ip.h b/googleurl/src/url_canon_ip.h deleted file mode 100644 index 0a01c9f..0000000 --- a/googleurl/src/url_canon_ip.h +++ /dev/null
@@ -1,101 +0,0 @@ -// Copyright 2008, Google 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: -// -// * 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 Google Inc. 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. - -#ifndef GOOGLEURL_SRC_URL_CANON_IP_H__ -#define GOOGLEURL_SRC_URL_CANON_IP_H__ - -#include "base/string16.h" -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_common.h" -#include "googleurl/src/url_parse.h" - -namespace url_canon { - -// Searches the host name for the portions of the IPv4 address. On success, -// each component will be placed into |components| and it will return true. -// It will return false if the host can not be separated as an IPv4 address -// or if there are any non-7-bit characters or other characters that can not -// be in an IP address. (This is important so we fail as early as possible for -// common non-IP hostnames.) -// -// Not all components may exist. If there are only 3 components, for example, -// the last one will have a length of -1 or 0 to indicate it does not exist. -// -// Note that many platform's inet_addr will ignore everything after a space -// in certain curcumstances if the stuff before the space looks like an IP -// address. IE6 is included in this. We do NOT handle this case. In many cases, -// the browser's canonicalization will get run before this which converts -// spaces to %20 (in the case of IE7) or rejects them (in the case of -// Mozilla), so this code path never gets hit. Our host canonicalization will -// notice these spaces and escape them, which will make IP address finding -// fail. This seems like better behavior than stripping after a space. -GURL_API bool FindIPv4Components(const char* spec, - const url_parse::Component& host, - url_parse::Component components[4]); -GURL_API bool FindIPv4Components(const char16* spec, - const url_parse::Component& host, - url_parse::Component components[4]); - -// Converts an IPv4 address to a 32-bit number (network byte order). -// -// Possible return values: -// IPV4 - IPv4 address was successfully parsed. -// BROKEN - Input was formatted like an IPv4 address, but overflow occurred -// during parsing. -// NEUTRAL - Input couldn't possibly be interpreted as an IPv4 address. -// It might be an IPv6 address, or a hostname. -// -// On success, |num_ipv4_components| will be populated with the number of -// components in the IPv4 address. -GURL_API CanonHostInfo::Family IPv4AddressToNumber( - const char* spec, - const url_parse::Component& host, - unsigned char address[4], - int* num_ipv4_components); -GURL_API CanonHostInfo::Family IPv4AddressToNumber( - const char16* spec, - const url_parse::Component& host, - unsigned char address[4], - int* num_ipv4_components); - -// Converts an IPv6 address to a 128-bit number (network byte order), returning -// true on success. False means that the input was not a valid IPv6 address. -// -// NOTE that |host| is expected to be surrounded by square brackets. -// i.e. "[::1]" rather than "::1". -GURL_API bool IPv6AddressToNumber(const char* spec, - const url_parse::Component& host, - unsigned char address[16]); -GURL_API bool IPv6AddressToNumber(const char16* spec, - const url_parse::Component& host, - unsigned char address[16]); - -} // namespace url_canon - -#endif // GOOGLEURL_SRC_URL_CANON_IP_H__
diff --git a/googleurl/src/url_canon_mailtourl.cc b/googleurl/src/url_canon_mailtourl.cc deleted file mode 100644 index 97868b8..0000000 --- a/googleurl/src/url_canon_mailtourl.cc +++ /dev/null
@@ -1,137 +0,0 @@ -// Copyright 2008, Google 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: -// -// * 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 Google Inc. 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. - -// Functions for canonicalizing "mailto:" URLs. - -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" -#include "googleurl/src/url_file.h" -#include "googleurl/src/url_parse_internal.h" - -namespace url_canon { - -namespace { - - -template<typename CHAR, typename UCHAR> -bool DoCanonicalizeMailtoURL(const URLComponentSource<CHAR>& source, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - - // mailto: only uses {scheme, path, query} -- clear the rest. - new_parsed->username = url_parse::Component(); - new_parsed->password = url_parse::Component(); - new_parsed->host = url_parse::Component(); - new_parsed->port = url_parse::Component(); - new_parsed->ref = url_parse::Component(); - - // Scheme (known, so we don't bother running it through the more - // complicated scheme canonicalizer). - new_parsed->scheme.begin = output->length(); - output->Append("mailto:", 7); - new_parsed->scheme.len = 6; - - bool success = true; - - // Path - if (parsed.path.is_valid()) { - new_parsed->path.begin = output->length(); - - // Copy the path using path URL's more lax escaping rules. - // We convert to UTF-8 and escape non-ASCII, but leave all - // ASCII characters alone. - int end = parsed.path.end(); - for (int i = parsed.path.begin; i < end; ++i) { - UCHAR uch = static_cast<UCHAR>(source.path[i]); - if (uch < 0x20 || uch >= 0x80) - success &= AppendUTF8EscapedChar(source.path, &i, end, output); - else - output->push_back(static_cast<char>(uch)); - } - - new_parsed->path.len = output->length() - new_parsed->path.begin; - } else { - // No path at all - new_parsed->path.reset(); - } - - // Query -- always use the default utf8 charset converter. - CanonicalizeQuery(source.query, parsed.query, NULL, - output, &new_parsed->query); - - return success; -} - -} // namespace - -bool CanonicalizeMailtoURL(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - return DoCanonicalizeMailtoURL<char, unsigned char>( - URLComponentSource<char>(spec), parsed, output, new_parsed); -} - -bool CanonicalizeMailtoURL(const char16* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - return DoCanonicalizeMailtoURL<char16, char16>( - URLComponentSource<char16>(spec), parsed, output, new_parsed); -} - -bool ReplaceMailtoURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); - SetupOverrideComponents(base, replacements, &source, &parsed); - return DoCanonicalizeMailtoURL<char, unsigned char>( - source, parsed, output, new_parsed); -} - -bool ReplaceMailtoURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - RawCanonOutput<1024> utf8; - URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); - SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); - return DoCanonicalizeMailtoURL<char, unsigned char>( - source, parsed, output, new_parsed); -} - -} // namespace url_canon
diff --git a/googleurl/src/url_canon_pathurl.cc b/googleurl/src/url_canon_pathurl.cc deleted file mode 100644 index 4a990c7..0000000 --- a/googleurl/src/url_canon_pathurl.cc +++ /dev/null
@@ -1,128 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -// Functions for canonicalizing "path" URLs. Not to be confused with the path -// of a URL, these are URLs that have no authority section, only a path. For -// example, "javascript:" and "data:". - -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" - -namespace url_canon { - -namespace { - -template<typename CHAR, typename UCHAR> -bool DoCanonicalizePathURL(const URLComponentSource<CHAR>& source, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - // Scheme: this will append the colon. - bool success = CanonicalizeScheme(source.scheme, parsed.scheme, - output, &new_parsed->scheme); - - // We assume there's no authority for path URLs. Note that hosts should never - // have -1 length. - new_parsed->username.reset(); - new_parsed->password.reset(); - new_parsed->host.reset(); - new_parsed->port.reset(); - - if (parsed.path.is_valid()) { - // Copy the path using path URL's more lax escaping rules (think for - // javascript:). We convert to UTF-8 and escape non-ASCII, but leave all - // ASCII characters alone. This helps readability of JavaStript. - new_parsed->path.begin = output->length(); - int end = parsed.path.end(); - for (int i = parsed.path.begin; i < end; i++) { - UCHAR uch = static_cast<UCHAR>(source.path[i]); - if (uch < 0x20 || uch >= 0x80) - success &= AppendUTF8EscapedChar(source.path, &i, end, output); - else - output->push_back(static_cast<char>(uch)); - } - new_parsed->path.len = output->length() - new_parsed->path.begin; - } else { - // Empty path. - new_parsed->path.reset(); - } - - // Assume there's no query or ref. - new_parsed->query.reset(); - new_parsed->ref.reset(); - - return success; -} - -} // namespace - -bool CanonicalizePathURL(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - return DoCanonicalizePathURL<char, unsigned char>( - URLComponentSource<char>(spec), parsed, output, new_parsed); -} - -bool CanonicalizePathURL(const char16* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - return DoCanonicalizePathURL<char16, char16>( - URLComponentSource<char16>(spec), parsed, output, new_parsed); -} - -bool ReplacePathURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); - SetupOverrideComponents(base, replacements, &source, &parsed); - return DoCanonicalizePathURL<char, unsigned char>( - source, parsed, output, new_parsed); -} - -bool ReplacePathURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed) { - RawCanonOutput<1024> utf8; - URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); - SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); - return DoCanonicalizePathURL<char, unsigned char>( - source, parsed, output, new_parsed); -} - -} // namespace url_canon
diff --git a/googleurl/src/url_canon_stdstring.h b/googleurl/src/url_canon_stdstring.h deleted file mode 100644 index c43b777..0000000 --- a/googleurl/src/url_canon_stdstring.h +++ /dev/null
@@ -1,134 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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 header file defines a canonicalizer output method class for STL -// strings. Because the canonicalizer tries not to be dependent on the STL, -// we have segregated it here. - -#ifndef GOOGLEURL_SRC_URL_CANON_STDSTRING_H__ -#define GOOGLEURL_SRC_URL_CANON_STDSTRING_H__ - -#include <string> -#include "googleurl/src/url_canon.h" - -namespace url_canon { - -// Write into a std::string given in the constructor. This object does not own -// the string itself, and the user must ensure that the string stays alive -// throughout the lifetime of this object. -// -// The given string will be appended to; any existing data in the string will -// be preserved. The caller should reserve() the amount of data in the string -// they expect to be written. We will resize if necessary, but that's slow. -// -// Note that when canonicalization is complete, the string will likely have -// unused space at the end because we make the string very big to start out -// with (by |initial_size|). This ends up being important because resize -// operations are slow, and because the base class needs to write directly -// into the buffer. -// -// Therefore, the user should call Complete() before using the string that -// this class wrote into. -class StdStringCanonOutput : public CanonOutput { - public: - StdStringCanonOutput(std::string* str) - : CanonOutput(), - str_(str) { - cur_len_ = static_cast<int>(str_->size()); // Append to existing data. - str_->resize(str_->capacity()); - buffer_ = &(*str_)[0]; - buffer_len_ = static_cast<int>(str_->size()); - } - virtual ~StdStringCanonOutput() { - // Nothing to do, we don't own the string. - } - - // Must be called after writing has completed but before the string is used. - void Complete() { - str_->resize(cur_len_); - buffer_len_ = cur_len_; - } - - virtual void Resize(int sz) { - str_->resize(sz); - buffer_ = &(*str_)[0]; - buffer_len_ = sz; - } - - protected: - std::string* str_; -}; - -// An extension of the Replacements class that allows the setters to use -// standard strings. -// -// The strings passed as arguments are not copied and must remain valid until -// this class goes out of scope. -template<typename STR> -class StdStringReplacements : - public url_canon::Replacements<typename STR::value_type> { - public: - void SetSchemeStr(const STR& s) { - this->SetScheme(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } - void SetUsernameStr(const STR& s) { - this->SetUsername(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } - void SetPasswordStr(const STR& s) { - this->SetPassword(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } - void SetHostStr(const STR& s) { - this->SetHost(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } - void SetPortStr(const STR& s) { - this->SetPort(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } - void SetPathStr(const STR& s) { - this->SetPath(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } - void SetQueryStr(const STR& s) { - this->SetQuery(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } - void SetRefStr(const STR& s) { - this->SetRef(s.data(), - url_parse::Component(0, static_cast<int>(s.length()))); - } -}; - -} // namespace url_canon - -#endif // GOOGLEURL_SRC_URL_CANON_STDSTRING_H__ -
diff --git a/googleurl/src/url_canon_unittest.cc b/googleurl/src/url_canon_unittest.cc deleted file mode 100644 index 731d82f..0000000 --- a/googleurl/src/url_canon_unittest.cc +++ /dev/null
@@ -1,1955 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -#include <errno.h> -#include <unicode/ucnv.h> - -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_icu.h" -#include "googleurl/src/url_canon_internal.h" -#include "googleurl/src/url_canon_stdstring.h" -#include "googleurl/src/url_parse.h" -#include "googleurl/src/url_test_utils.h" -#include "testing/base/public/gunit.h" - -// Some implementations of base/basictypes.h may define ARRAYSIZE. -// If it's not defined, we define it to the ARRAYSIZE_UNSAFE macro -// which is in our version of basictypes.h. -#ifndef ARRAYSIZE -#define ARRAYSIZE ARRAYSIZE_UNSAFE -#endif - -using url_test_utils::WStringToUTF16; -using url_test_utils::ConvertUTF8ToUTF16; -using url_test_utils::ConvertUTF16ToUTF8; -using url_canon::CanonHostInfo; - -namespace { - -struct ComponentCase { - const char* input; - const char* expected; - url_parse::Component expected_component; - bool expected_success; -}; - -// ComponentCase but with dual 8-bit/16-bit input. Generally, the unit tests -// treat each input as optional, and will only try processing if non-NULL. -// The output is always 8-bit. -struct DualComponentCase { - const char* input8; - const wchar_t* input16; - const char* expected; - url_parse::Component expected_component; - bool expected_success; -}; - -// Test cases for CanonicalizeIPAddress(). The inputs are identical to -// DualComponentCase, but the output has extra CanonHostInfo fields. -struct IPAddressCase { - const char* input8; - const wchar_t* input16; - const char* expected; - url_parse::Component expected_component; - - // CanonHostInfo fields, for verbose output. - CanonHostInfo::Family expected_family; - int expected_num_ipv4_components; -}; - -struct ReplaceCase { - const char* base; - const char* scheme; - const char* username; - const char* password; - const char* host; - const char* port; - const char* path; - const char* query; - const char* ref; - const char* expected; -}; - -// Wrapper around a UConverter object that managers creation and destruction. -class UConvScoper { - public: - explicit UConvScoper(const char* charset_name) { - UErrorCode err = U_ZERO_ERROR; - converter_ = ucnv_open(charset_name, &err); - } - - ~UConvScoper() { - if (converter_) - ucnv_close(converter_); - } - - // Returns the converter object, may be NULL. - UConverter* converter() const { return converter_; } - - private: - UConverter* converter_; -}; - -// Magic string used in the replacements code that tells SetupReplComp to -// call the clear function. -const char kDeleteComp[] = "|"; - -// Sets up a replacement for a single component. This is given pointers to -// the set and clear function for the component being replaced, and will -// either set the component (if it exists) or clear it (if the replacement -// string matches kDeleteComp). -// -// This template is currently used only for the 8-bit case, and the strlen -// causes it to fail in other cases. It is left a template in case we have -// tests for wide replacements. -template<typename CHAR> -void SetupReplComp( - void (url_canon::Replacements<CHAR>::*set)(const CHAR*, - const url_parse::Component&), - void (url_canon::Replacements<CHAR>::*clear)(), - url_canon::Replacements<CHAR>* rep, - const CHAR* str) { - if (str && str[0] == kDeleteComp[0]) { - (rep->*clear)(); - } else if (str) { - (rep->*set)(str, url_parse::Component(0, static_cast<int>(strlen(str)))); - } -} - -} // namespace - -TEST(URLCanonTest, UTF) { - // Low-level test that we handle reading, canonicalization, and writing - // UTF-8/UTF-16 strings properly. - struct UTFCase { - const char* input8; - const wchar_t* input16; - bool expected_success; - const char* output; - } utf_cases[] = { - // Valid canonical input should get passed through & escaped. - {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true, "%E4%BD%A0%E5%A5%BD"}, - // Test a characer that takes > 16 bits (U+10300 = old italic letter A) - {"\xF0\x90\x8C\x80", L"\xd800\xdf00", true, "%F0%90%8C%80"}, - // Non-shortest-form UTF-8 are invalid. The bad char should be replaced - // with the invalid character (EF BF DB in UTF-8). - {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", NULL, false, "%EF%BF%BD%E5%A5%BD"}, - // Invalid UTF-8 sequences should be marked as invalid (the first - // sequence is truncated). - {"\xe4\xa0\xe5\xa5\xbd", L"\xd800\x597d", false, "%EF%BF%BD%E5%A5%BD"}, - // Character going off the end. - {"\xe4\xbd\xa0\xe5\xa5", L"\x4f60\xd800", false, "%E4%BD%A0%EF%BF%BD"}, - // ...same with low surrogates with no high surrogate. - {"\xed\xb0\x80", L"\xdc00", false, "%EF%BF%BD"}, - // Test a UTF-8 encoded surrogate value is marked as invalid. - // ED A0 80 = U+D800 - {"\xed\xa0\x80", NULL, false, "%EF%BF%BD"}, - }; - - std::string out_str; - for (size_t i = 0; i < ARRAYSIZE(utf_cases); i++) { - if (utf_cases[i].input8) { - out_str.clear(); - url_canon::StdStringCanonOutput output(&out_str); - - int input_len = static_cast<int>(strlen(utf_cases[i].input8)); - bool success = true; - for (int ch = 0; ch < input_len; ch++) { - success &= AppendUTF8EscapedChar(utf_cases[i].input8, &ch, input_len, - &output); - } - output.Complete(); - EXPECT_EQ(utf_cases[i].expected_success, success); - EXPECT_EQ(std::string(utf_cases[i].output), out_str); - } - if (utf_cases[i].input16) { - out_str.clear(); - url_canon::StdStringCanonOutput output(&out_str); - - string16 input_str(WStringToUTF16(utf_cases[i].input16)); - int input_len = static_cast<int>(input_str.length()); - bool success = true; - for (int ch = 0; ch < input_len; ch++) { - success &= AppendUTF8EscapedChar(input_str.c_str(), &ch, input_len, - &output); - } - output.Complete(); - EXPECT_EQ(utf_cases[i].expected_success, success); - EXPECT_EQ(std::string(utf_cases[i].output), out_str); - } - - if (utf_cases[i].input8 && utf_cases[i].input16 && - utf_cases[i].expected_success) { - // Check that the UTF-8 and UTF-16 inputs are equivalent. - - // UTF-16 -> UTF-8 - std::string input8_str(utf_cases[i].input8); - string16 input16_str(WStringToUTF16(utf_cases[i].input16)); - EXPECT_EQ(input8_str, ConvertUTF16ToUTF8(input16_str)); - - // UTF-8 -> UTF-16 - EXPECT_EQ(input16_str, ConvertUTF8ToUTF16(input8_str)); - } - } -} - -TEST(URLCanonTest, ICUCharsetConverter) { - struct ICUCase { - const wchar_t* input; - const char* encoding; - const char* expected; - } icu_cases[] = { - // UTF-8. - {L"Hello, world", "utf-8", "Hello, world"}, - {L"\x4f60\x597d", "utf-8", "\xe4\xbd\xa0\xe5\xa5\xbd"}, - // Non-BMP UTF-8. - {L"!\xd800\xdf00!", "utf-8", "!\xf0\x90\x8c\x80!"}, - // Big5 - {L"\x4f60\x597d", "big5", "\xa7\x41\xa6\x6e"}, - // Unrepresentable character in the destination set. - {L"hello\x4f60\x06de\x597dworld", "big5", "hello\xa7\x41%26%231758%3B\xa6\x6eworld"}, - }; - - for (size_t i = 0; i < ARRAYSIZE(icu_cases); i++) { - UConvScoper conv(icu_cases[i].encoding); - ASSERT_TRUE(conv.converter() != NULL); - url_canon::ICUCharsetConverter converter(conv.converter()); - - std::string str; - url_canon::StdStringCanonOutput output(&str); - - string16 input_str(WStringToUTF16(icu_cases[i].input)); - int input_len = static_cast<int>(input_str.length()); - converter.ConvertFromUTF16(input_str.c_str(), input_len, &output); - output.Complete(); - - EXPECT_STREQ(icu_cases[i].expected, str.c_str()); - } - - // Test string sizes around the resize boundary for the output to make sure - // the converter resizes as needed. - const int static_size = 16; - UConvScoper conv("utf-8"); - ASSERT_TRUE(conv.converter()); - url_canon::ICUCharsetConverter converter(conv.converter()); - for (int i = static_size - 2; i <= static_size + 2; i++) { - // Make a string with the appropriate length. - string16 input; - for (int ch = 0; ch < i; ch++) - input.push_back('a'); - - url_canon::RawCanonOutput<static_size> output; - converter.ConvertFromUTF16(input.c_str(), static_cast<int>(input.length()), - &output); - EXPECT_EQ(input.length(), static_cast<size_t>(output.length())); - } -} - -TEST(URLCanonTest, Scheme) { - // Here, we're mostly testing that unusual characters are handled properly. - // The canonicalizer doesn't do any parsing or whitespace detection. It will - // also do its best on error, and will escape funny sequences (these won't be - // valid schemes and it will return error). - // - // Note that the canonicalizer will append a colon to the output to separate - // out the rest of the URL, which is not present in the input. We check, - // however, that the output range includes everything but the colon. - ComponentCase scheme_cases[] = { - {"http", "http:", url_parse::Component(0, 4), true}, - {"HTTP", "http:", url_parse::Component(0, 4), true}, - {" HTTP ", "%20http%20:", url_parse::Component(0, 10),false}, - {"htt: ", "htt%3A%20:", url_parse::Component(0, 9), false}, - {"\xe4\xbd\xa0\xe5\xa5\xbdhttp", "%E4%BD%A0%E5%A5%BDhttp:", url_parse::Component(0, 22), false}, - // Don't re-escape something already escaped. Note that it will - // "canonicalize" the 'A' to 'a', but that's OK. - {"ht%3Atp", "ht%3atp:", url_parse::Component(0, 7), false}, - }; - - std::string out_str; - - for (size_t i = 0; i < arraysize(scheme_cases); i++) { - int url_len = static_cast<int>(strlen(scheme_cases[i].input)); - url_parse::Component in_comp(0, url_len); - url_parse::Component out_comp; - - out_str.clear(); - url_canon::StdStringCanonOutput output1(&out_str); - bool success = url_canon::CanonicalizeScheme(scheme_cases[i].input, - in_comp, &output1, &out_comp); - output1.Complete(); - - EXPECT_EQ(scheme_cases[i].expected_success, success); - EXPECT_EQ(std::string(scheme_cases[i].expected), out_str); - EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len); - - // Now try the wide version - out_str.clear(); - url_canon::StdStringCanonOutput output2(&out_str); - - string16 wide_input(ConvertUTF8ToUTF16(scheme_cases[i].input)); - in_comp.len = static_cast<int>(wide_input.length()); - success = url_canon::CanonicalizeScheme(wide_input.c_str(), in_comp, - &output2, &out_comp); - output2.Complete(); - - EXPECT_EQ(scheme_cases[i].expected_success, success); - EXPECT_EQ(std::string(scheme_cases[i].expected), out_str); - EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len); - } - - // Test the case where the scheme is declared nonexistant, it should be - // converted into an empty scheme. - url_parse::Component out_comp; - out_str.clear(); - url_canon::StdStringCanonOutput output(&out_str); - - EXPECT_TRUE(url_canon::CanonicalizeScheme("", url_parse::Component(0, -1), - &output, &out_comp)); - output.Complete(); - - EXPECT_EQ(std::string(":"), out_str); - EXPECT_EQ(0, out_comp.begin); - EXPECT_EQ(0, out_comp.len); -} - -TEST(URLCanonTest, Host) { - IPAddressCase host_cases[] = { - // Basic canonicalization, uppercase should be converted to lowercase. - {"GoOgLe.CoM", L"GoOgLe.CoM", "google.com", url_parse::Component(0, 10), CanonHostInfo::NEUTRAL, -1}, - // Spaces and some other characters should be escaped. - {"Goo%20 goo%7C|.com", L"Goo%20 goo%7C|.com", "goo%20%20goo%7C%7C.com", url_parse::Component(0, 22), CanonHostInfo::NEUTRAL, -1}, - // Exciting different types of spaces! - {NULL, L"GOO\x00a0\x3000goo.com", "goo%20%20goo.com", url_parse::Component(0, 16), CanonHostInfo::NEUTRAL, -1}, - // Other types of space (no-break, zero-width, zero-width-no-break) are - // name-prepped away to nothing. - {NULL, L"GOO\x200b\x2060\xfeffgoo.com", "googoo.com", url_parse::Component(0, 10), CanonHostInfo::NEUTRAL, -1}, - // Ideographic full stop (full-width period for Chinese, etc.) should be - // treated as a dot. - {NULL, L"www.foo\x3002" L"bar.com", "www.foo.bar.com", url_parse::Component(0, 15), CanonHostInfo::NEUTRAL, -1}, - // Invalid unicode characters should fail... - // ...In wide input, ICU will barf and we'll end up with the input as - // escaped UTF-8 (the invalid character should be replaced with the - // replacement character). - {"\xef\xb7\x90zyx.com", L"\xfdd0zyx.com", "%EF%BF%BDzyx.com", url_parse::Component(0, 16), CanonHostInfo::BROKEN, -1}, - // ...This is the same as previous but with with escaped. - {"%ef%b7%90zyx.com", L"%ef%b7%90zyx.com", "%EF%BF%BDzyx.com", url_parse::Component(0, 16), CanonHostInfo::BROKEN, -1}, - // Test name prepping, fullwidth input should be converted to ASCII and NOT - // IDN-ized. This is "Go" in fullwidth UTF-8/UTF-16. - {"\xef\xbc\xa7\xef\xbd\x8f.com", L"\xff27\xff4f.com", "go.com", url_parse::Component(0, 6), CanonHostInfo::NEUTRAL, -1}, - // Test that fullwidth escaped values are properly name-prepped, - // then converted or rejected. - // ...%41 in fullwidth = 'A' (also as escaped UTF-8 input) - {"\xef\xbc\x85\xef\xbc\x94\xef\xbc\x91.com", L"\xff05\xff14\xff11.com", "a.com", url_parse::Component(0, 5), CanonHostInfo::NEUTRAL, -1}, - {"%ef%bc%85%ef%bc%94%ef%bc%91.com", L"%ef%bc%85%ef%bc%94%ef%bc%91.com", "a.com", url_parse::Component(0, 5), CanonHostInfo::NEUTRAL, -1}, - // ...%00 in fullwidth should fail (also as escaped UTF-8 input) - {"\xef\xbc\x85\xef\xbc\x90\xef\xbc\x90.com", L"\xff05\xff10\xff10.com", "%00.com", url_parse::Component(0, 7), CanonHostInfo::BROKEN, -1}, - {"%ef%bc%85%ef%bc%90%ef%bc%90.com", L"%ef%bc%85%ef%bc%90%ef%bc%90.com", "%00.com", url_parse::Component(0, 7), CanonHostInfo::BROKEN, -1}, - // Basic IDN support, UTF-8 and UTF-16 input should be converted to IDN - {"\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d\x4f60\x597d", "xn--6qqa088eba", url_parse::Component(0, 14), CanonHostInfo::NEUTRAL, -1}, - // Mixed UTF-8 and escaped UTF-8 (narrow case) and UTF-16 and escaped - // UTF-8 (wide case). The output should be equivalent to the true wide - // character input above). - {"%E4%BD%A0%E5%A5%BD\xe4\xbd\xa0\xe5\xa5\xbd", L"%E4%BD%A0%E5%A5%BD\x4f60\x597d", "xn--6qqa088eba", url_parse::Component(0, 14), CanonHostInfo::NEUTRAL, -1}, - // Invalid escaped characters should fail and the percents should be - // escaped. - {"%zz%66%a", L"%zz%66%a", "%25zzf%25a", url_parse::Component(0, 10), CanonHostInfo::BROKEN, -1}, - // If we get an invalid character that has been escaped. - {"%25", L"%25", "%25", url_parse::Component(0, 3), CanonHostInfo::BROKEN, -1}, - {"hello%00", L"hello%00", "hello%00", url_parse::Component(0, 8), CanonHostInfo::BROKEN, -1}, - // Escaped numbers should be treated like IP addresses if they are. - {"%30%78%63%30%2e%30%32%35%30.01", L"%30%78%63%30%2e%30%32%35%30.01", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 3}, - {"%30%78%63%30%2e%30%32%35%30.01%2e", L"%30%78%63%30%2e%30%32%35%30.01%2e", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 3}, - // Invalid escaping should trigger the regular host error handling. - {"%3g%78%63%30%2e%30%32%35%30%2E.01", L"%3g%78%63%30%2e%30%32%35%30%2E.01", "%253gxc0.0250..01", url_parse::Component(0, 17), CanonHostInfo::BROKEN, -1}, - // Something that isn't exactly an IP should get treated as a host and - // spaces escaped. - {"192.168.0.1 hello", L"192.168.0.1 hello", "192.168.0.1%20hello", url_parse::Component(0, 19), CanonHostInfo::NEUTRAL, -1}, - // Fullwidth and escaped UTF-8 fullwidth should still be treated as IP. - // These are "0Xc0.0250.01" in fullwidth. - {"\xef\xbc\x90%Ef%bc\xb8%ef%Bd%83\xef\xbc\x90%EF%BC%8E\xef\xbc\x90\xef\xbc\x92\xef\xbc\x95\xef\xbc\x90\xef\xbc%8E\xef\xbc\x90\xef\xbc\x91", L"\xff10\xff38\xff43\xff10\xff0e\xff10\xff12\xff15\xff10\xff0e\xff10\xff11", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 3}, - // Broken IP addresses get marked as such. - {"192.168.0.257", L"192.168.0.257", "192.168.0.257", url_parse::Component(0, 13), CanonHostInfo::BROKEN, -1}, - {"[google.com]", L"[google.com]", "[google.com]", url_parse::Component(0, 12), CanonHostInfo::BROKEN, -1}, - // Cyrillic letter followed buy ( should return punicode for ( escaped before punicode string was created. I.e. - // if ( is escaped after punicode is created we would get xn--%28-8tb (incorrect). - {"\xd1\x82(", L"\x0442(", "xn--%28-7ed", url_parse::Component(0, 11), CanonHostInfo::NEUTRAL, -1}, - }; - - // CanonicalizeHost() non-verbose. - std::string out_str; - for (size_t i = 0; i < arraysize(host_cases); i++) { - // Narrow version. - if (host_cases[i].input8) { - int host_len = static_cast<int>(strlen(host_cases[i].input8)); - url_parse::Component in_comp(0, host_len); - url_parse::Component out_comp; - - out_str.clear(); - url_canon::StdStringCanonOutput output(&out_str); - - bool success = url_canon::CanonicalizeHost(host_cases[i].input8, in_comp, - &output, &out_comp); - output.Complete(); - - EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN, - success); - EXPECT_EQ(std::string(host_cases[i].expected), out_str); - EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len); - } - - // Wide version. - if (host_cases[i].input16) { - string16 input16(WStringToUTF16(host_cases[i].input16)); - int host_len = static_cast<int>(input16.length()); - url_parse::Component in_comp(0, host_len); - url_parse::Component out_comp; - - out_str.clear(); - url_canon::StdStringCanonOutput output(&out_str); - - bool success = url_canon::CanonicalizeHost(input16.c_str(), in_comp, - &output, &out_comp); - output.Complete(); - - EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN, - success); - EXPECT_EQ(std::string(host_cases[i].expected), out_str); - EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len); - } - } - - // CanonicalizeHostVerbose() - for (size_t i = 0; i < arraysize(host_cases); i++) { - // Narrow version. - if (host_cases[i].input8) { - int host_len = static_cast<int>(strlen(host_cases[i].input8)); - url_parse::Component in_comp(0, host_len); - - out_str.clear(); - url_canon::StdStringCanonOutput output(&out_str); - CanonHostInfo host_info; - - url_canon::CanonicalizeHostVerbose(host_cases[i].input8, in_comp, - &output, &host_info); - output.Complete(); - - EXPECT_EQ(host_cases[i].expected_family, host_info.family); - EXPECT_EQ(std::string(host_cases[i].expected), out_str); - EXPECT_EQ(host_cases[i].expected_component.begin, - host_info.out_host.begin); - EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len); - if (host_cases[i].expected_family == CanonHostInfo::IPV4) { - EXPECT_EQ(host_cases[i].expected_num_ipv4_components, - host_info.num_ipv4_components); - } - } - - // Wide version. - if (host_cases[i].input16) { - string16 input16(WStringToUTF16(host_cases[i].input16)); - int host_len = static_cast<int>(input16.length()); - url_parse::Component in_comp(0, host_len); - - out_str.clear(); - url_canon::StdStringCanonOutput output(&out_str); - CanonHostInfo host_info; - - url_canon::CanonicalizeHostVerbose(input16.c_str(), in_comp, - &output, &host_info); - output.Complete(); - - EXPECT_EQ(host_cases[i].expected_family, host_info.family); - EXPECT_EQ(std::string(host_cases[i].expected), out_str); - EXPECT_EQ(host_cases[i].expected_component.begin, - host_info.out_host.begin); - EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len); - if (host_cases[i].expected_family == CanonHostInfo::IPV4) { - EXPECT_EQ(host_cases[i].expected_num_ipv4_components, - host_info.num_ipv4_components); - } - } - } -} - -TEST(URLCanonTest, IPv4) { - IPAddressCase cases[] = { - // Empty is not an IP address. - {"", L"", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - {".", L".", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // Regular IP addresses in different bases. - {"192.168.0.1", L"192.168.0.1", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 4}, - {"0300.0250.00.01", L"0300.0250.00.01", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 4}, - {"0xC0.0Xa8.0x0.0x1", L"0xC0.0Xa8.0x0.0x1", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 4}, - // Non-IP addresses due to invalid characters. - {"192.168.9.com", L"192.168.9.com", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // Invalid characters for the base should be rejected. - {"19a.168.0.1", L"19a.168.0.1", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - {"0308.0250.00.01", L"0308.0250.00.01", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - {"0xCG.0xA8.0x0.0x1", L"0xCG.0xA8.0x0.0x1", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // If there are not enough components, the last one should fill them out. - {"192", L"192", "0.0.0.192", url_parse::Component(0, 9), CanonHostInfo::IPV4, 1}, - {"0xC0a80001", L"0xC0a80001", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 1}, - {"030052000001", L"030052000001", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 1}, - {"000030052000001", L"000030052000001", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 1}, - {"192.168", L"192.168", "192.0.0.168", url_parse::Component(0, 11), CanonHostInfo::IPV4, 2}, - {"192.0x00A80001", L"192.0x000A80001", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 2}, - {"0xc0.052000001", L"0xc0.052000001", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 2}, - {"192.168.1", L"192.168.1", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 3}, - // Too many components means not an IP address. - {"192.168.0.0.1", L"192.168.0.0.1", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // We allow a single trailing dot. - {"192.168.0.1.", L"192.168.0.1.", "192.168.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 4}, - {"192.168.0.1. hello", L"192.168.0.1. hello", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - {"192.168.0.1..", L"192.168.0.1..", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // Two dots in a row means not an IP address. - {"192.168..1", L"192.168..1", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // Any numerical overflow should be marked as BROKEN. - {"0x100.0", L"0x100.0", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0x100.0.0", L"0x100.0.0", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0x100.0.0.0", L"0x100.0.0.0", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0.0x100.0.0", L"0.0x100.0.0", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0.0.0x100.0", L"0.0.0x100.0", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0.0.0.0x100", L"0.0.0.0x100", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0.0.0x10000", L"0.0.0x10000", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0.0x1000000", L"0.0x1000000", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0x100000000", L"0x100000000", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // Repeat the previous tests, minus 1, to verify boundaries. - {"0xFF.0", L"0xFF.0", "255.0.0.0", url_parse::Component(0, 9), CanonHostInfo::IPV4, 2}, - {"0xFF.0.0", L"0xFF.0.0", "255.0.0.0", url_parse::Component(0, 9), CanonHostInfo::IPV4, 3}, - {"0xFF.0.0.0", L"0xFF.0.0.0", "255.0.0.0", url_parse::Component(0, 9), CanonHostInfo::IPV4, 4}, - {"0.0xFF.0.0", L"0.0xFF.0.0", "0.255.0.0", url_parse::Component(0, 9), CanonHostInfo::IPV4, 4}, - {"0.0.0xFF.0", L"0.0.0xFF.0", "0.0.255.0", url_parse::Component(0, 9), CanonHostInfo::IPV4, 4}, - {"0.0.0.0xFF", L"0.0.0.0xFF", "0.0.0.255", url_parse::Component(0, 9), CanonHostInfo::IPV4, 4}, - {"0.0.0xFFFF", L"0.0.0xFFFF", "0.0.255.255", url_parse::Component(0, 11), CanonHostInfo::IPV4, 3}, - {"0.0xFFFFFF", L"0.0xFFFFFF", "0.255.255.255", url_parse::Component(0, 13), CanonHostInfo::IPV4, 2}, - {"0xFFFFFFFF", L"0xFFFFFFFF", "255.255.255.255", url_parse::Component(0, 15), CanonHostInfo::IPV4, 1}, - // Old trunctations tests. They're all "BROKEN" now. - {"276.256.0xf1a2.077777", L"276.256.0xf1a2.077777", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"192.168.0.257", L"192.168.0.257", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"192.168.0xa20001", L"192.168.0xa20001", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"192.015052000001", L"192.015052000001", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"0X12C0a80001", L"0X12C0a80001", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"276.1.2", L"276.1.2", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // Spaces should be rejected. - {"192.168.0.1 hello", L"192.168.0.1 hello", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // Very large numbers. - {"0000000000000300.0x00000000000000fF.00000000000000001", L"0000000000000300.0x00000000000000fF.00000000000000001", "192.255.0.1", url_parse::Component(0, 11), CanonHostInfo::IPV4, 3}, - {"0000000000000300.0xffffffffFFFFFFFF.3022415481470977", L"0000000000000300.0xffffffffFFFFFFFF.3022415481470977", "", url_parse::Component(0, 11), CanonHostInfo::BROKEN, -1}, - // A number has no length limit, but long numbers can still overflow. - {"00000000000000000001", L"00000000000000000001", "0.0.0.1", url_parse::Component(0, 7), CanonHostInfo::IPV4, 1}, - {"0000000000000000100000000000000001", L"0000000000000000100000000000000001", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // If a long component is non-numeric, it's a hostname, *not* a broken IP. - {"0.0.0.000000000000000000z", L"0.0.0.000000000000000000z", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - {"0.0.0.100000000000000000z", L"0.0.0.100000000000000000z", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // Truncation of all zeros should still result in 0. - {"0.00.0x.0x0", L"0.00.0x.0x0", "0.0.0.0", url_parse::Component(0, 7), CanonHostInfo::IPV4, 4}, - }; - - for (size_t i = 0; i < arraysize(cases); i++) { - // 8-bit version. - url_parse::Component component(0, - static_cast<int>(strlen(cases[i].input8))); - - std::string out_str1; - url_canon::StdStringCanonOutput output1(&out_str1); - url_canon::CanonHostInfo host_info; - url_canon::CanonicalizeIPAddress(cases[i].input8, component, &output1, - &host_info); - output1.Complete(); - - EXPECT_EQ(cases[i].expected_family, host_info.family); - if (host_info.family == CanonHostInfo::IPV4) { - EXPECT_STREQ(cases[i].expected, out_str1.c_str()); - EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); - EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); - EXPECT_EQ(cases[i].expected_num_ipv4_components, - host_info.num_ipv4_components); - } - - // 16-bit version. - string16 input16(WStringToUTF16(cases[i].input16)); - component = url_parse::Component(0, static_cast<int>(input16.length())); - - std::string out_str2; - url_canon::StdStringCanonOutput output2(&out_str2); - url_canon::CanonicalizeIPAddress(input16.c_str(), component, &output2, - &host_info); - output2.Complete(); - - EXPECT_EQ(cases[i].expected_family, host_info.family); - if (host_info.family == CanonHostInfo::IPV4) { - EXPECT_STREQ(cases[i].expected, out_str2.c_str()); - EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); - EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); - EXPECT_EQ(cases[i].expected_num_ipv4_components, - host_info.num_ipv4_components); - } - } -} - -TEST(URLCanonTest, IPv6) { - IPAddressCase cases[] = { - // Empty is not an IP address. - {"", L"", "", url_parse::Component(), CanonHostInfo::NEUTRAL, -1}, - // Non-IPs with [:] characters are marked BROKEN. - {":", L":", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[", L"[", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[:", L"[:", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"]", L"]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {":]", L":]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[]", L"[]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[:]", L"[:]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // Regular IP address is invalid without bounding '[' and ']'. - {"2001:db8::1", L"2001:db8::1", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[2001:db8::1", L"[2001:db8::1", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"2001:db8::1]", L"2001:db8::1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // Regular IP addresses. - {"[::]", L"[::]", "[::]", url_parse::Component(0,4), CanonHostInfo::IPV6, -1}, - {"[::1]", L"[::1]", "[::1]", url_parse::Component(0,5), CanonHostInfo::IPV6, -1}, - {"[1::]", L"[1::]", "[1::]", url_parse::Component(0,5), CanonHostInfo::IPV6, -1}, - {"[::192.168.0.1]", L"[::192.168.0.1]", "[::c0a8:1]", url_parse::Component(0,10), CanonHostInfo::IPV6, -1}, - {"[::ffff:192.168.0.1]", L"[::ffff:192.168.0.1]", "[::ffff:c0a8:1]", url_parse::Component(0,15), CanonHostInfo::IPV6, -1}, - - // Leading zeros should be stripped. - {"[000:01:02:003:004:5:6:007]", L"[000:01:02:003:004:5:6:007]", "[0:1:2:3:4:5:6:7]", url_parse::Component(0,17), CanonHostInfo::IPV6, -1}, - - // Upper case letters should be lowercased. - {"[A:b:c:DE:fF:0:1:aC]", L"[A:b:c:DE:fF:0:1:aC]", "[a:b:c:de:ff:0:1:ac]", url_parse::Component(0,20), CanonHostInfo::IPV6, -1}, - - // The same address can be written with different contractions, but should - // get canonicalized to the same thing. - {"[1:0:0:2::3:0]", L"[1:0:0:2::3:0]", "[1::2:0:0:3:0]", url_parse::Component(0,14), CanonHostInfo::IPV6, -1}, - {"[1::2:0:0:3:0]", L"[1::2:0:0:3:0]", "[1::2:0:0:3:0]", url_parse::Component(0,14), CanonHostInfo::IPV6, -1}, - - // IPv4 addresses - // Only mapped and compat addresses can have IPv4 syntax embedded. - {"[::eeee:192.168.0.1]", L"[::eeee:192.168.0.1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[2001::192.168.0.1]", L"[2001::192.168.0.1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[1:2:192.168.0.1:5:6]", L"[1:2:192.168.0.1:5:6]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - - // IPv4 with last component missing. - {"[::ffff:192.1.2]", L"[::ffff:192.1.2]", "[::ffff:c001:2]", url_parse::Component(0,15), CanonHostInfo::IPV6, -1}, - - // IPv4 using hex. - // TODO(eroman): Should this format be disallowed? - {"[::ffff:0xC0.0Xa8.0x0.0x1]", L"[::ffff:0xC0.0Xa8.0x0.0x1]", "[::ffff:c0a8:1]", url_parse::Component(0,15), CanonHostInfo::IPV6, -1}, - - // There may be zeros surrounding the "::" contraction. - {"[0:0::0:0:8]", L"[0:0::0:0:8]", "[::8]", url_parse::Component(0,5), CanonHostInfo::IPV6, -1}, - - {"[2001:db8::1]", L"[2001:db8::1]", "[2001:db8::1]", url_parse::Component(0,13), CanonHostInfo::IPV6, -1}, - - // Can only have one "::" contraction in an IPv6 string literal. - {"[2001::db8::1]", L"[2001::db8::1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // No more than 2 consecutive ':'s. - {"[2001:db8:::1]", L"[2001:db8:::1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[:::]", L"[:::]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // Non-IP addresses due to invalid characters. - {"[2001::.com]", L"[2001::.com]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // If there are not enough components, the last one should fill them out. - // ... omitted at this time ... - // Too many components means not an IP address. Similarly with too few if using IPv4 compat or mapped addresses. - {"[::192.168.0.0.1]", L"[::192.168.0.0.1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[::ffff:192.168.0.0.1]", L"[::ffff:192.168.0.0.1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[1:2:3:4:5:6:7:8:9]", L"[1:2:3:4:5:6:7:8:9]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // Too many bits (even though 8 comonents, the last one holds 32 bits). - {"[0:0:0:0:0:0:0:192.168.0.1]", L"[0:0:0:0:0:0:0:192.168.0.1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - - // Too many bits specified -- the contraction would have to be zero-length - // to not exceed 128 bits. - {"[1:2:3:4:5:6::192.168.0.1]", L"[1:2:3:4:5:6::192.168.0.1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - - // The contraction is for 16 bits of zero. - {"[1:2:3:4:5:6::8]", L"[1:2:3:4:5:6::8]", "[1:2:3:4:5:6:0:8]", url_parse::Component(0,17), CanonHostInfo::IPV6, -1}, - - // Cannot have a trailing colon. - {"[1:2:3:4:5:6:7:8:]", L"[1:2:3:4:5:6:7:8:]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[1:2:3:4:5:6:192.168.0.1:]", L"[1:2:3:4:5:6:192.168.0.1:]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - - // Cannot have negative numbers. - {"[-1:2:3:4:5:6:7:8]", L"[-1:2:3:4:5:6:7:8]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - - // Scope ID -- the URL may contain an optional ["%" <scope_id>] section. - // The scope_id should be included in the canonicalized URL, and is an - // unsigned decimal number. - - // Invalid because no ID was given after the percent. - - // Don't allow scope-id - {"[1::%1]", L"[1::%1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[1::%eth0]", L"[1::%eth0]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[1::%]", L"[1::%]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[%]", L"[%]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[::%:]", L"[::%:]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - - // Don't allow leading or trailing colons. - {"[:0:0::0:0:8]", L"[:0:0::0:0:8]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[0:0::0:0:8:]", L"[0:0::0:0:8:]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - {"[:0:0::0:0:8:]", L"[:0:0::0:0:8:]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - - // We allow a single trailing dot. - // ... omitted at this time ... - // Two dots in a row means not an IP address. - {"[::192.168..1]", L"[::192.168..1]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - // Any non-first components get truncated to one byte. - // ... omitted at this time ... - // Spaces should be rejected. - {"[::1 hello]", L"[::1 hello]", "", url_parse::Component(), CanonHostInfo::BROKEN, -1}, - }; - - for (size_t i = 0; i < arraysize(cases); i++) { - // 8-bit version. - url_parse::Component component(0, - static_cast<int>(strlen(cases[i].input8))); - - std::string out_str1; - url_canon::StdStringCanonOutput output1(&out_str1); - url_canon::CanonHostInfo host_info; - url_canon::CanonicalizeIPAddress(cases[i].input8, component, &output1, - &host_info); - output1.Complete(); - - EXPECT_EQ(cases[i].expected_family, host_info.family); - if (host_info.family == CanonHostInfo::IPV6) { - EXPECT_STREQ(cases[i].expected, out_str1.c_str()); - EXPECT_EQ(cases[i].expected_component.begin, - host_info.out_host.begin); - EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); - } - - // 16-bit version. - string16 input16(WStringToUTF16(cases[i].input16)); - component = url_parse::Component(0, static_cast<int>(input16.length())); - - std::string out_str2; - url_canon::StdStringCanonOutput output2(&out_str2); - url_canon::CanonicalizeIPAddress(input16.c_str(), component, &output2, - &host_info); - output2.Complete(); - - EXPECT_EQ(cases[i].expected_family, host_info.family); - if (host_info.family == CanonHostInfo::IPV6) { - EXPECT_STREQ(cases[i].expected, out_str2.c_str()); - EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); - EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); - } - } -} - -TEST(URLCanonTest, IPEmpty) { - std::string out_str1; - url_canon::StdStringCanonOutput output1(&out_str1); - url_canon::CanonHostInfo host_info; - - // This tests tests. - const char spec[] = "192.168.0.1"; - url_canon::CanonicalizeIPAddress(spec, url_parse::Component(), - &output1, &host_info); - EXPECT_FALSE(host_info.IsIPAddress()); - - url_canon::CanonicalizeIPAddress(spec, url_parse::Component(0, 0), - &output1, &host_info); - EXPECT_FALSE(host_info.IsIPAddress()); -} - -TEST(URLCanonTest, UserInfo) { - // Note that the canonicalizer should escape and treat empty components as - // not being there. - - // We actually parse a full input URL so we can get the initial components. - struct UserComponentCase { - const char* input; - const char* expected; - url_parse::Component expected_username; - url_parse::Component expected_password; - bool expected_success; - } user_info_cases[] = { - {"http://user:pass@host.com/", "user:pass@", url_parse::Component(0, 4), url_parse::Component(5, 4), true}, - {"http://@host.com/", "", url_parse::Component(0, -1), url_parse::Component(0, -1), true}, - {"http://:@host.com/", "", url_parse::Component(0, -1), url_parse::Component(0, -1), true}, - {"http://foo:@host.com/", "foo@", url_parse::Component(0, 3), url_parse::Component(0, -1), true}, - {"http://:foo@host.com/", ":foo@", url_parse::Component(0, 0), url_parse::Component(1, 3), true}, - {"http://^ :$\t@host.com/", "%5E%20:$%09@", url_parse::Component(0, 6), url_parse::Component(7, 4), true}, - {"http://user:pass@/", "user:pass@", url_parse::Component(0, 4), url_parse::Component(5, 4), true}, - {"http://%2540:bar@domain.com/", "%2540:bar@", url_parse::Component(0, 5), url_parse::Component(6, 3), true }, - - // IE7 compatability: old versions allowed backslashes in usernames, but - // IE7 does not. We disallow it as well. - {"ftp://me\\mydomain:pass@foo.com/", "", url_parse::Component(0, -1), url_parse::Component(0, -1), true}, - }; - - for (size_t i = 0; i < ARRAYSIZE(user_info_cases); i++) { - int url_len = static_cast<int>(strlen(user_info_cases[i].input)); - url_parse::Parsed parsed; - url_parse::ParseStandardURL(user_info_cases[i].input, url_len, &parsed); - url_parse::Component out_user, out_pass; - std::string out_str; - url_canon::StdStringCanonOutput output1(&out_str); - - bool success = url_canon::CanonicalizeUserInfo(user_info_cases[i].input, - parsed.username, - user_info_cases[i].input, - parsed.password, - &output1, &out_user, - &out_pass); - output1.Complete(); - - EXPECT_EQ(user_info_cases[i].expected_success, success); - EXPECT_EQ(std::string(user_info_cases[i].expected), out_str); - EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin); - EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len); - EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin); - EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len); - - // Now try the wide version - out_str.clear(); - url_canon::StdStringCanonOutput output2(&out_str); - string16 wide_input(ConvertUTF8ToUTF16(user_info_cases[i].input)); - success = url_canon::CanonicalizeUserInfo(wide_input.c_str(), - parsed.username, - wide_input.c_str(), - parsed.password, - &output2, &out_user, &out_pass); - output2.Complete(); - - EXPECT_EQ(user_info_cases[i].expected_success, success); - EXPECT_EQ(std::string(user_info_cases[i].expected), out_str); - EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin); - EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len); - EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin); - EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len); - } -} - -TEST(URLCanonTest, Port) { - // We only need to test that the number gets properly put into the output - // buffer. The parser unit tests will test scanning the number correctly. - // - // Note that the CanonicalizePort will always prepend a colon to the output - // to separate it from the colon that it assumes preceeds it. - struct PortCase { - const char* input; - int default_port; - const char* expected; - url_parse::Component expected_component; - bool expected_success; - } port_cases[] = { - // Invalid input should be copied w/ failure. - {"as df", 80, ":as%20df", url_parse::Component(1, 7), false}, - {"-2", 80, ":-2", url_parse::Component(1, 2), false}, - // Default port should be omitted. - {"80", 80, "", url_parse::Component(0, -1), true}, - {"8080", 80, ":8080", url_parse::Component(1, 4), true}, - // PORT_UNSPECIFIED should mean always keep the port. - {"80", url_parse::PORT_UNSPECIFIED, ":80", url_parse::Component(1, 2), true}, - }; - - for (size_t i = 0; i < ARRAYSIZE(port_cases); i++) { - int url_len = static_cast<int>(strlen(port_cases[i].input)); - url_parse::Component in_comp(0, url_len); - url_parse::Component out_comp; - std::string out_str; - url_canon::StdStringCanonOutput output1(&out_str); - bool success = url_canon::CanonicalizePort(port_cases[i].input, in_comp, - port_cases[i].default_port, - &output1, &out_comp); - output1.Complete(); - - EXPECT_EQ(port_cases[i].expected_success, success); - EXPECT_EQ(std::string(port_cases[i].expected), out_str); - EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len); - - // Now try the wide version - out_str.clear(); - url_canon::StdStringCanonOutput output2(&out_str); - string16 wide_input(ConvertUTF8ToUTF16(port_cases[i].input)); - success = url_canon::CanonicalizePort(wide_input.c_str(), in_comp, - port_cases[i].default_port, - &output2, &out_comp); - output2.Complete(); - - EXPECT_EQ(port_cases[i].expected_success, success); - EXPECT_EQ(std::string(port_cases[i].expected), out_str); - EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len); - } -} - -TEST(URLCanonTest, Path) { - DualComponentCase path_cases[] = { - // ----- path collapsing tests ----- - {"/././foo", L"/././foo", "/foo", url_parse::Component(0, 4), true}, - {"/./.foo", L"/./.foo", "/.foo", url_parse::Component(0, 5), true}, - {"/foo/.", L"/foo/.", "/foo/", url_parse::Component(0, 5), true}, - {"/foo/./", L"/foo/./", "/foo/", url_parse::Component(0, 5), true}, - // double dots followed by a slash or the end of the string count - {"/foo/bar/..", L"/foo/bar/..", "/foo/", url_parse::Component(0, 5), true}, - {"/foo/bar/../", L"/foo/bar/../", "/foo/", url_parse::Component(0, 5), true}, - // don't count double dots when they aren't followed by a slash - {"/foo/..bar", L"/foo/..bar", "/foo/..bar", url_parse::Component(0, 10), true}, - // some in the middle - {"/foo/bar/../ton", L"/foo/bar/../ton", "/foo/ton", url_parse::Component(0, 8), true}, - {"/foo/bar/../ton/../../a", L"/foo/bar/../ton/../../a", "/a", url_parse::Component(0, 2), true}, - // we should not be able to go above the root - {"/foo/../../..", L"/foo/../../..", "/", url_parse::Component(0, 1), true}, - {"/foo/../../../ton", L"/foo/../../../ton", "/ton", url_parse::Component(0, 4), true}, - // escaped dots should be unescaped and treated the same as dots - {"/foo/%2e", L"/foo/%2e", "/foo/", url_parse::Component(0, 5), true}, - {"/foo/%2e%2", L"/foo/%2e%2", "/foo/.%2", url_parse::Component(0, 8), true}, - {"/foo/%2e./%2e%2e/.%2e/%2e.bar", L"/foo/%2e./%2e%2e/.%2e/%2e.bar", "/..bar", url_parse::Component(0, 6), true}, - // Multiple slashes in a row should be preserved and treated like empty - // directory names. - {"////../..", L"////../..", "//", url_parse::Component(0, 2), true}, - - // ----- escaping tests ----- - {"/foo", L"/foo", "/foo", url_parse::Component(0, 4), true}, - // Valid escape sequence - {"/%20foo", L"/%20foo", "/%20foo", url_parse::Component(0, 7), true}, - // Invalid escape sequence we should pass through unchanged. - {"/foo%", L"/foo%", "/foo%", url_parse::Component(0, 5), true}, - {"/foo%2", L"/foo%2", "/foo%2", url_parse::Component(0, 6), true}, - // Invalid escape sequence: bad characters should be treated the same as - // the sourrounding text, not as escaped (in this case, UTF-8). - {"/foo%2zbar", L"/foo%2zbar", "/foo%2zbar", url_parse::Component(0, 10), true}, - {"/foo%2\xc2\xa9zbar", NULL, "/foo%2%C2%A9zbar", url_parse::Component(0, 16), true}, - {NULL, L"/foo%2\xc2\xa9zbar", "/foo%2%C3%82%C2%A9zbar", url_parse::Component(0, 22), true}, - // Regular characters that are escaped should be unescaped - {"/foo%41%7a", L"/foo%41%7a", "/fooAz", url_parse::Component(0, 6), true}, - // Funny characters that are unescaped should be escaped - {"/foo\x09\x91%91", NULL, "/foo%09%91%91", url_parse::Component(0, 13), true}, - {NULL, L"/foo\x09\x91%91", "/foo%09%C2%91%91", url_parse::Component(0, 16), true}, - // Invalid characters that are escaped should cause a failure. - {"/foo%00%51", L"/foo%00%51", "/foo%00Q", url_parse::Component(0, 8), false}, - // Some characters should be passed through unchanged regardless of esc. - {"/(%28:%3A%29)", L"/(%28:%3A%29)", "/(%28:%3A%29)", url_parse::Component(0, 13), true}, - // Characters that are properly escaped should not have the case changed - // of hex letters. - {"/%3A%3a%3C%3c", L"/%3A%3a%3C%3c", "/%3A%3a%3C%3c", url_parse::Component(0, 13), true}, - // Funny characters that are unescaped should be escaped - {"/foo\tbar", L"/foo\tbar", "/foo%09bar", url_parse::Component(0, 10), true}, - // Backslashes should get converted to forward slashes - {"\\foo\\bar", L"\\foo\\bar", "/foo/bar", url_parse::Component(0, 8), true}, - // Hashes found in paths (possibly only when the caller explicitly sets - // the path on an already-parsed URL) should be escaped. - {"/foo#bar", L"/foo#bar", "/foo%23bar", url_parse::Component(0, 10), true}, - // %7f should be allowed and %3D should not be unescaped (these were wrong - // in a previous version). - {"/%7Ffp3%3Eju%3Dduvgw%3Dd", L"/%7Ffp3%3Eju%3Dduvgw%3Dd", "/%7Ffp3%3Eju%3Dduvgw%3Dd", url_parse::Component(0, 24), true}, - // @ should be passed through unchanged (escaped or unescaped). - {"/@asdf%40", L"/@asdf%40", "/@asdf%40", url_parse::Component(0, 9), true}, - - // ----- encoding tests ----- - // Basic conversions - {"/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", L"/\x4f60\x597d\x4f60\x597d", "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD", url_parse::Component(0, 37), true}, - // Invalid unicode characters should fail. We only do validation on - // UTF-16 input, so this doesn't happen on 8-bit. - {"/\xef\xb7\x90zyx", NULL, "/%EF%B7%90zyx", url_parse::Component(0, 13), true}, - {NULL, L"/\xfdd0zyx", "/%EF%BF%BDzyx", url_parse::Component(0, 13), false}, - }; - - for (size_t i = 0; i < arraysize(path_cases); i++) { - if (path_cases[i].input8) { - int len = static_cast<int>(strlen(path_cases[i].input8)); - url_parse::Component in_comp(0, len); - url_parse::Component out_comp; - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - bool success = url_canon::CanonicalizePath(path_cases[i].input8, in_comp, - &output, &out_comp); - output.Complete(); - - EXPECT_EQ(path_cases[i].expected_success, success); - EXPECT_EQ(path_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(path_cases[i].expected_component.len, out_comp.len); - EXPECT_EQ(path_cases[i].expected, out_str); - } - - if (path_cases[i].input16) { - string16 input16(WStringToUTF16(path_cases[i].input16)); - int len = static_cast<int>(input16.length()); - url_parse::Component in_comp(0, len); - url_parse::Component out_comp; - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - - bool success = url_canon::CanonicalizePath(input16.c_str(), in_comp, - &output, &out_comp); - output.Complete(); - - EXPECT_EQ(path_cases[i].expected_success, success); - EXPECT_EQ(path_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(path_cases[i].expected_component.len, out_comp.len); - EXPECT_EQ(path_cases[i].expected, out_str); - } - } - - // Manual test: embedded NULLs should be escaped and the URL should be marked - // as invalid. - const char path_with_null[] = "/ab\0c"; - url_parse::Component in_comp(0, 5); - url_parse::Component out_comp; - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - bool success = url_canon::CanonicalizePath(path_with_null, in_comp, - &output, &out_comp); - output.Complete(); - EXPECT_FALSE(success); - EXPECT_EQ("/ab%00c", out_str); -} - -TEST(URLCanonTest, Query) { - struct QueryCase { - const char* input8; - const wchar_t* input16; - const char* encoding; - const char* expected; - } query_cases[] = { - // Regular ASCII case in some different encodings. - {"foo=bar", L"foo=bar", NULL, "?foo=bar"}, - {"foo=bar", L"foo=bar", "utf-8", "?foo=bar"}, - {"foo=bar", L"foo=bar", "shift_jis", "?foo=bar"}, - {"foo=bar", L"foo=bar", "gb2312", "?foo=bar"}, - // Allow question marks in the query without escaping - {"as?df", L"as?df", NULL, "?as?df"}, - // Always escape '#' since it would mark the ref. - {"as#df", L"as#df", NULL, "?as%23df"}, - // Escape some questionable 8-bit characters, but never unescape. - {"\x02hello\x7f bye", L"\x02hello\x7f bye", NULL, "?%02hello%7F%20bye"}, - {"%40%41123", L"%40%41123", NULL, "?%40%41123"}, - // Chinese input/output - {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", NULL, "?q=%E4%BD%A0%E5%A5%BD"}, - {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "gb2312", "?q=%C4%E3%BA%C3"}, - {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "big5", "?q=%A7A%A6n"}, - // Unencodable character in the destination character set should be - // escaped. The escape sequence unescapes to be the entity name: - // "?q=你" - {"q=Chinese\xef\xbc\xa7", L"q=Chinese\xff27", "iso-8859-1", "?q=Chinese%26%2365319%3B"}, - // Invalid UTF-8/16 input should be replaced with invalid characters. - {"q=\xed\xed", L"q=\xd800\xd800", NULL, "?q=%EF%BF%BD%EF%BF%BD"}, - // Don't allow < or > because sometimes they are used for XSS if the - // URL is echoed in content. Firefox does this, IE doesn't. - {"q=<asdf>", L"q=<asdf>", NULL, "?q=%3Casdf%3E"}, - // Escape double quotemarks in the query. - {"q=\"asdf\"", L"q=\"asdf\"", NULL, "?q=%22asdf%22"}, - }; - - for (size_t i = 0; i < ARRAYSIZE(query_cases); i++) { - url_parse::Component out_comp; - - UConvScoper conv(query_cases[i].encoding); - ASSERT_TRUE(!query_cases[i].encoding || conv.converter()); - url_canon::ICUCharsetConverter converter(conv.converter()); - - // Map NULL to a NULL converter pointer. - url_canon::ICUCharsetConverter* conv_pointer = &converter; - if (!query_cases[i].encoding) - conv_pointer = NULL; - - if (query_cases[i].input8) { - int len = static_cast<int>(strlen(query_cases[i].input8)); - url_parse::Component in_comp(0, len); - std::string out_str; - - url_canon::StdStringCanonOutput output(&out_str); - url_canon::CanonicalizeQuery(query_cases[i].input8, in_comp, - conv_pointer, &output, &out_comp); - output.Complete(); - - EXPECT_EQ(query_cases[i].expected, out_str); - } - - if (query_cases[i].input16) { - string16 input16(WStringToUTF16(query_cases[i].input16)); - int len = static_cast<int>(input16.length()); - url_parse::Component in_comp(0, len); - std::string out_str; - - url_canon::StdStringCanonOutput output(&out_str); - url_canon::CanonicalizeQuery(input16.c_str(), in_comp, - conv_pointer, &output, &out_comp); - output.Complete(); - - EXPECT_EQ(query_cases[i].expected, out_str); - } - } - - // Extra test for input with embedded NULL; - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_parse::Component out_comp; - url_canon::CanonicalizeQuery("a \x00z\x01", url_parse::Component(0, 5), NULL, - &output, &out_comp); - output.Complete(); - EXPECT_EQ("?a%20%00z%01", out_str); -} - -TEST(URLCanonTest, Ref) { - // Refs are trivial, it just checks the encoding. - DualComponentCase ref_cases[] = { - // Regular one, we shouldn't escape spaces, et al. - {"hello, world", L"hello, world", "#hello, world", url_parse::Component(1, 12), true}, - // UTF-8/wide input should be preserved - {"\xc2\xa9", L"\xa9", "#\xc2\xa9", url_parse::Component(1, 2), true}, - // Test a characer that takes > 16 bits (U+10300 = old italic letter A) - {"\xF0\x90\x8C\x80ss", L"\xd800\xdf00ss", "#\xF0\x90\x8C\x80ss", url_parse::Component(1, 6), true}, - // Escaping should be preserved unchanged, even invalid ones - {"%41%a", L"%41%a", "#%41%a", url_parse::Component(1, 5), true}, - // Invalid UTF-8/16 input should be flagged and the input made valid - {"\xc2", NULL, "#\xef\xbf\xbd", url_parse::Component(1, 3), true}, - {NULL, L"\xd800\x597d", "#\xef\xbf\xbd\xe5\xa5\xbd", url_parse::Component(1, 6), true}, - // Test a Unicode invalid character. - {"a\xef\xb7\x90", L"a\xfdd0", "#a\xef\xbf\xbd", url_parse::Component(1, 4), true}, - // Refs can have # signs and we should preserve them. - {"asdf#qwer", L"asdf#qwer", "#asdf#qwer", url_parse::Component(1, 9), true}, - {"#asdf", L"#asdf", "##asdf", url_parse::Component(1, 5), true}, - }; - - for (size_t i = 0; i < arraysize(ref_cases); i++) { - // 8-bit input - if (ref_cases[i].input8) { - int len = static_cast<int>(strlen(ref_cases[i].input8)); - url_parse::Component in_comp(0, len); - url_parse::Component out_comp; - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_canon::CanonicalizeRef(ref_cases[i].input8, in_comp, - &output, &out_comp); - output.Complete(); - - EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len); - EXPECT_EQ(ref_cases[i].expected, out_str); - } - - // 16-bit input - if (ref_cases[i].input16) { - string16 input16(WStringToUTF16(ref_cases[i].input16)); - int len = static_cast<int>(input16.length()); - url_parse::Component in_comp(0, len); - url_parse::Component out_comp; - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_canon::CanonicalizeRef(input16.c_str(), in_comp, &output, &out_comp); - output.Complete(); - - EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin); - EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len); - EXPECT_EQ(ref_cases[i].expected, out_str); - } - } - - // Try one with an embedded NULL. It should be stripped. - const char null_input[5] = "ab\x00z"; - url_parse::Component null_input_component(0, 4); - url_parse::Component out_comp; - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_canon::CanonicalizeRef(null_input, null_input_component, - &output, &out_comp); - output.Complete(); - - EXPECT_EQ(1, out_comp.begin); - EXPECT_EQ(3, out_comp.len); - EXPECT_EQ("#abz", out_str); -} - -TEST(URLCanonTest, CanonicalizeStandardURL) { - // The individual component canonicalize tests should have caught the cases - // for each of those components. Here, we just need to test that the various - // parts are included or excluded properly, and have the correct separators. - struct URLCase { - const char* input; - const char* expected; - bool expected_success; - } cases[] = { - {"http://www.google.com/foo?bar=baz#", "http://www.google.com/foo?bar=baz#", true}, - {"http://[www.google.com]/", "http://[www.google.com]/", false}, - {"ht\ttp:@www.google.com:80/;p?#", "ht%09tp://www.google.com:80/;p?#", false}, - {"http:////////user:@google.com:99?foo", "http://user@google.com:99/?foo", true}, - {"www.google.com", ":www.google.com/", true}, - {"http://192.0x00A80001", "http://192.168.0.1/", true}, - {"http://www/foo%2Ehtml", "http://www/foo.html", true}, - {"http://user:pass@/", "http://user:pass@/", false}, - {"http://%25DOMAIN:foobar@foodomain.com/", "http://%25DOMAIN:foobar@foodomain.com/", true}, - - // Backslashes should get converted to forward slashes. - {"http:\\\\www.google.com\\foo", "http://www.google.com/foo", true}, - - // Busted refs shouldn't make the whole thing fail. - {"http://www.google.com/asdf#\xc2", "http://www.google.com/asdf#\xef\xbf\xbd", true}, - - // Basic port tests. - {"http://foo:80/", "http://foo/", true}, - {"http://foo:81/", "http://foo:81/", true}, - {"httpa://foo:80/", "httpa://foo:80/", true}, - {"http://foo:-80/", "http://foo:-80/", false}, - - {"https://foo:443/", "https://foo/", true}, - {"https://foo:80/", "https://foo:80/", true}, - {"ftp://foo:21/", "ftp://foo/", true}, - {"ftp://foo:80/", "ftp://foo:80/", true}, - {"gopher://foo:70/", "gopher://foo/", true}, - {"gopher://foo:443/", "gopher://foo:443/", true}, - {"ws://foo:80/", "ws://foo/", true}, - {"ws://foo:81/", "ws://foo:81/", true}, - {"ws://foo:443/", "ws://foo:443/", true}, - {"ws://foo:815/", "ws://foo:815/", true}, - {"wss://foo:80/", "wss://foo:80/", true}, - {"wss://foo:81/", "wss://foo:81/", true}, - {"wss://foo:443/", "wss://foo/", true}, - {"wss://foo:815/", "wss://foo:815/", true}, - }; - - for (size_t i = 0; i < ARRAYSIZE(cases); i++) { - int url_len = static_cast<int>(strlen(cases[i].input)); - url_parse::Parsed parsed; - url_parse::ParseStandardURL(cases[i].input, url_len, &parsed); - - url_parse::Parsed out_parsed; - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - bool success = url_canon::CanonicalizeStandardURL( - cases[i].input, url_len, parsed, NULL, &output, &out_parsed); - output.Complete(); - - EXPECT_EQ(cases[i].expected_success, success); - EXPECT_EQ(cases[i].expected, out_str); - } -} - -// The codepath here is the same as for regular canonicalization, so we just -// need to test that things are replaced or not correctly. -TEST(URLCanonTest, ReplaceStandardURL) { - ReplaceCase replace_cases[] = { - // Common case of truncating the path. - {"http://www.google.com/foo?bar=baz#ref", NULL, NULL, NULL, NULL, NULL, "/", kDeleteComp, kDeleteComp, "http://www.google.com/"}, - // Replace everything - {"http://a:b@google.com:22/foo;bar?baz@cat", "https", "me", "pw", "host.com", "99", "/path", "query", "ref", "https://me:pw@host.com:99/path?query#ref"}, - // Replace nothing - {"http://a:b@google.com:22/foo?baz@cat", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "http://a:b@google.com:22/foo?baz@cat"}, - }; - - for (size_t i = 0; i < arraysize(replace_cases); i++) { - const ReplaceCase& cur = replace_cases[i]; - int base_len = static_cast<int>(strlen(cur.base)); - url_parse::Parsed parsed; - url_parse::ParseStandardURL(cur.base, base_len, &parsed); - - url_canon::Replacements<char> r; - typedef url_canon::Replacements<char> R; // Clean up syntax. - - // Note that for the scheme we pass in a different clear function since - // there is no function to clear the scheme. - SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); - SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); - SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); - SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); - SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); - SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); - SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); - SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_parse::Parsed out_parsed; - url_canon::ReplaceStandardURL(replace_cases[i].base, parsed, - r, NULL, &output, &out_parsed); - output.Complete(); - - EXPECT_EQ(replace_cases[i].expected, out_str); - } - - // The path pointer should be ignored if the address is invalid. - { - const char src[] = "http://www.google.com/here_is_the_path"; - int src_len = static_cast<int>(strlen(src)); - - url_parse::Parsed parsed; - url_parse::ParseStandardURL(src, src_len, &parsed); - - // Replace the path to 0 length string. By using 1 as the string address, - // the test should get an access violation if it tries to dereference it. - url_canon::Replacements<char> r; - r.SetPath(reinterpret_cast<char*>(0x00000001), url_parse::Component(0, 0)); - std::string out_str1; - url_canon::StdStringCanonOutput output1(&out_str1); - url_parse::Parsed new_parsed; - url_canon::ReplaceStandardURL(src, parsed, r, NULL, &output1, &new_parsed); - output1.Complete(); - EXPECT_STREQ("http://www.google.com/", out_str1.c_str()); - - // Same with an "invalid" path. - r.SetPath(reinterpret_cast<char*>(0x00000001), url_parse::Component()); - std::string out_str2; - url_canon::StdStringCanonOutput output2(&out_str2); - url_canon::ReplaceStandardURL(src, parsed, r, NULL, &output2, &new_parsed); - output2.Complete(); - EXPECT_STREQ("http://www.google.com/", out_str2.c_str()); - } -} - -TEST(URLCanonTest, ReplaceFileURL) { - ReplaceCase replace_cases[] = { - // Replace everything - {"file:///C:/gaba?query#ref", NULL, NULL, NULL, "filer", NULL, "/foo", "b", "c", "file://filer/foo?b#c"}, - // Replace nothing - {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///C:/gaba?query#ref"}, - // Clear non-path components (common) - {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "file:///C:/gaba"}, - // Replace path with something that doesn't begin with a slash and make - // sure it get added properly. - {"file:///C:/gaba", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "file:///interesting/"}, - {"file:///home/gaba?query#ref", NULL, NULL, NULL, "filer", NULL, "/foo", "b", "c", "file://filer/foo?b#c"}, - {"file:///home/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///home/gaba?query#ref"}, - {"file:///home/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "file:///home/gaba"}, - {"file:///home/gaba", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "file:///interesting/"}, - }; - - for (size_t i = 0; i < arraysize(replace_cases); i++) { - const ReplaceCase& cur = replace_cases[i]; - int base_len = static_cast<int>(strlen(cur.base)); - url_parse::Parsed parsed; - url_parse::ParseFileURL(cur.base, base_len, &parsed); - - url_canon::Replacements<char> r; - typedef url_canon::Replacements<char> R; // Clean up syntax. - SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); - SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); - SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); - SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); - SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); - SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); - SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); - SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_parse::Parsed out_parsed; - url_canon::ReplaceFileURL(cur.base, parsed, - r, NULL, &output, &out_parsed); - output.Complete(); - - EXPECT_EQ(replace_cases[i].expected, out_str); - } -} - -TEST(URLCanonTest, ReplacePathURL) { - ReplaceCase replace_cases[] = { - // Replace everything - {"data:foo", "javascript", NULL, NULL, NULL, NULL, "alert('foo?');", NULL, NULL, "javascript:alert('foo?');"}, - // Replace nothing - {"data:foo", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "data:foo"}, - // Replace one or the other - {"data:foo", "javascript", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "javascript:foo"}, - {"data:foo", NULL, NULL, NULL, NULL, NULL, "bar", NULL, NULL, "data:bar"}, - {"data:foo", NULL, NULL, NULL, NULL, NULL, kDeleteComp, NULL, NULL, "data:"}, - }; - - for (size_t i = 0; i < arraysize(replace_cases); i++) { - const ReplaceCase& cur = replace_cases[i]; - int base_len = static_cast<int>(strlen(cur.base)); - url_parse::Parsed parsed; - url_parse::ParsePathURL(cur.base, base_len, &parsed); - - url_canon::Replacements<char> r; - typedef url_canon::Replacements<char> R; // Clean up syntax. - SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); - SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); - SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); - SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); - SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); - SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); - SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); - SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_parse::Parsed out_parsed; - url_canon::ReplacePathURL(cur.base, parsed, - r, &output, &out_parsed); - output.Complete(); - - EXPECT_EQ(replace_cases[i].expected, out_str); - } -} - -TEST(URLCanonTest, ReplaceMailtoURL) { - ReplaceCase replace_cases[] = { - // Replace everything - {"mailto:jon@foo.com?body=sup", "mailto", NULL, NULL, NULL, NULL, "addr1", "to=tony", NULL, "mailto:addr1?to=tony"}, - // Replace nothing - {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "mailto:jon@foo.com?body=sup"}, - // Replace the path - {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", NULL, NULL, "mailto:jason?body=sup"}, - // Replace the query - {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "custom=1", NULL, "mailto:jon@foo.com?custom=1"}, - // Replace the path and query - {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", "custom=1", NULL, "mailto:jason?custom=1"}, - // Set the query to empty (should leave trailing question mark) - {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "", NULL, "mailto:jon@foo.com?"}, - // Clear the query - {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "|", NULL, "mailto:jon@foo.com"}, - // Clear the path - {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "|", NULL, NULL, "mailto:?body=sup"}, - // Clear the path + query - {"mailto:", NULL, NULL, NULL, NULL, NULL, "|", "|", NULL, "mailto:"}, - // Setting the ref should have no effect - {"mailto:addr1", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "BLAH", "mailto:addr1"}, - }; - - for (size_t i = 0; i < arraysize(replace_cases); i++) { - const ReplaceCase& cur = replace_cases[i]; - int base_len = static_cast<int>(strlen(cur.base)); - url_parse::Parsed parsed; - url_parse::ParseMailtoURL(cur.base, base_len, &parsed); - - url_canon::Replacements<char> r; - typedef url_canon::Replacements<char> R; - SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); - SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); - SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); - SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); - SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); - SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); - SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); - SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - url_parse::Parsed out_parsed; - url_canon::ReplaceMailtoURL(cur.base, parsed, - r, &output, &out_parsed); - output.Complete(); - - EXPECT_EQ(replace_cases[i].expected, out_str); - } -} - -TEST(URLCanonTest, CanonicalizeFileURL) { - struct URLCase { - const char* input; - const char* expected; - bool expected_success; - url_parse::Component expected_host; - url_parse::Component expected_path; - } cases[] = { -#ifdef _WIN32 - // Windows-style paths - {"file:c:\\foo\\bar.html", "file:///C:/foo/bar.html", true, url_parse::Component(), url_parse::Component(7, 16)}, - {" File:c|////foo\\bar.html", "file:///C:////foo/bar.html", true, url_parse::Component(), url_parse::Component(7, 19)}, - {"file:", "file:///", true, url_parse::Component(), url_parse::Component(7, 1)}, - {"file:UNChost/path", "file://unchost/path", true, url_parse::Component(7, 7), url_parse::Component(14, 5)}, - // CanonicalizeFileURL supports absolute Windows style paths for IE - // compatability. Note that the caller must decide that this is a file - // URL itself so it can call the file canonicalizer. This is usually - // done automatically as part of relative URL resolving. - {"c:\\foo\\bar", "file:///C:/foo/bar", true, url_parse::Component(), url_parse::Component(7, 11)}, - {"C|/foo/bar", "file:///C:/foo/bar", true, url_parse::Component(), url_parse::Component(7, 11)}, - {"/C|\\foo\\bar", "file:///C:/foo/bar", true, url_parse::Component(), url_parse::Component(7, 11)}, - {"//C|/foo/bar", "file:///C:/foo/bar", true, url_parse::Component(), url_parse::Component(7, 11)}, - {"//server/file", "file://server/file", true, url_parse::Component(7, 6), url_parse::Component(13, 5)}, - {"\\\\server\\file", "file://server/file", true, url_parse::Component(7, 6), url_parse::Component(13, 5)}, - {"/\\server/file", "file://server/file", true, url_parse::Component(7, 6), url_parse::Component(13, 5)}, - // We should preserve the number of slashes after the colon for IE - // compatability, except when there is none, in which case we should - // add one. - {"file:c:foo/bar.html", "file:///C:/foo/bar.html", true, url_parse::Component(), url_parse::Component(7, 16)}, - {"file:/\\/\\C:\\\\//foo\\bar.html", "file:///C:////foo/bar.html", true, url_parse::Component(), url_parse::Component(7, 19)}, - // Three slashes should be non-UNC, even if there is no drive spec (IE - // does this, which makes the resulting request invalid). - {"file:///foo/bar.txt", "file:///foo/bar.txt", true, url_parse::Component(), url_parse::Component(7, 12)}, - // TODO(brettw) we should probably fail for invalid host names, which - // would change the expected result on this test. We also currently allow - // colon even though it's probably invalid, because its currently the - // "natural" result of the way the canonicalizer is written. There doesn't - // seem to be a strong argument for why allowing it here would be bad, so - // we just tolerate it and the load will fail later. - {"FILE:/\\/\\7:\\\\//foo\\bar.html", "file://7:////foo/bar.html", false, url_parse::Component(7, 2), url_parse::Component(9, 16)}, - {"file:filer/home\\me", "file://filer/home/me", true, url_parse::Component(7, 5), url_parse::Component(12, 8)}, - // Make sure relative paths can't go above the "C:" - {"file:///C:/foo/../../../bar.html", "file:///C:/bar.html", true, url_parse::Component(), url_parse::Component(7, 12)}, - // Busted refs shouldn't make the whole thing fail. - {"file:///C:/asdf#\xc2", "file:///C:/asdf#\xef\xbf\xbd", true, url_parse::Component(), url_parse::Component(7, 8)}, -#else - // Unix-style paths - {"file:///home/me", "file:///home/me", true, url_parse::Component(), url_parse::Component(7, 8)}, - // Windowsy ones should get still treated as Unix-style. - {"file:c:\\foo\\bar.html", "file:///c:/foo/bar.html", true, url_parse::Component(), url_parse::Component(7, 16)}, - {"file:c|//foo\\bar.html", "file:///c%7C//foo/bar.html", true, url_parse::Component(), url_parse::Component(7, 19)}, - // file: tests from WebKit (LayoutTests/fast/loader/url-parse-1.html) - {"//", "file:///", true, url_parse::Component(), url_parse::Component(7, 1)}, - {"///", "file:///", true, url_parse::Component(), url_parse::Component(7, 1)}, - {"///test", "file:///test", true, url_parse::Component(), url_parse::Component(7, 5)}, - {"file://test", "file://test/", true, url_parse::Component(7, 4), url_parse::Component(11, 1)}, - {"file://localhost", "file://localhost/", true, url_parse::Component(7, 9), url_parse::Component(16, 1)}, - {"file://localhost/", "file://localhost/", true, url_parse::Component(7, 9), url_parse::Component(16, 1)}, - {"file://localhost/test", "file://localhost/test", true, url_parse::Component(7, 9), url_parse::Component(16, 5)}, -#endif // _WIN32 - }; - - for (size_t i = 0; i < ARRAYSIZE(cases); i++) { - int url_len = static_cast<int>(strlen(cases[i].input)); - url_parse::Parsed parsed; - url_parse::ParseFileURL(cases[i].input, url_len, &parsed); - - url_parse::Parsed out_parsed; - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - bool success = url_canon::CanonicalizeFileURL(cases[i].input, url_len, - parsed, NULL, &output, - &out_parsed); - output.Complete(); - - EXPECT_EQ(cases[i].expected_success, success); - EXPECT_EQ(cases[i].expected, out_str); - - // Make sure the spec was properly identified, the file canonicalizer has - // different code for writing the spec. - EXPECT_EQ(0, out_parsed.scheme.begin); - EXPECT_EQ(4, out_parsed.scheme.len); - - EXPECT_EQ(cases[i].expected_host.begin, out_parsed.host.begin); - EXPECT_EQ(cases[i].expected_host.len, out_parsed.host.len); - - EXPECT_EQ(cases[i].expected_path.begin, out_parsed.path.begin); - EXPECT_EQ(cases[i].expected_path.len, out_parsed.path.len); - } -} - -TEST(URLCanonTest, CanonicalizePathURL) { - // Path URLs should get canonicalized schemes but nothing else. - struct PathCase { - const char* input; - const char* expected; - } path_cases[] = { - {"javascript:", "javascript:"}, - {"JavaScript:Foo", "javascript:Foo"}, - {":\":This /is interesting;?#", ":\":This /is interesting;?#"}, - }; - - for (size_t i = 0; i < ARRAYSIZE(path_cases); i++) { - int url_len = static_cast<int>(strlen(path_cases[i].input)); - url_parse::Parsed parsed; - url_parse::ParsePathURL(path_cases[i].input, url_len, &parsed); - - url_parse::Parsed out_parsed; - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - bool success = url_canon::CanonicalizePathURL(path_cases[i].input, url_len, - parsed, &output, - &out_parsed); - output.Complete(); - - EXPECT_TRUE(success); - EXPECT_EQ(path_cases[i].expected, out_str); - - EXPECT_EQ(0, out_parsed.host.begin); - EXPECT_EQ(-1, out_parsed.host.len); - - // When we end with a colon at the end, there should be no path. - if (path_cases[i].input[url_len - 1] == ':') { - EXPECT_EQ(0, out_parsed.path.begin); - EXPECT_EQ(-1, out_parsed.path.len); - } - } -} - -TEST(URLCanonTest, CanonicalizeMailtoURL) { - struct URLCase { - const char* input; - const char* expected; - bool expected_success; - url_parse::Component expected_path; - url_parse::Component expected_query; - } cases[] = { - {"mailto:addr1", "mailto:addr1", true, url_parse::Component(7, 5), url_parse::Component()}, - {"mailto:addr1@foo.com", "mailto:addr1@foo.com", true, url_parse::Component(7, 13), url_parse::Component()}, - // Trailing whitespace is stripped. - {"MaIlTo:addr1 \t ", "mailto:addr1", true, url_parse::Component(7, 5), url_parse::Component()}, - {"MaIlTo:addr1?to=jon", "mailto:addr1?to=jon", true, url_parse::Component(7, 5), url_parse::Component(13,6)}, - {"mailto:addr1,addr2", "mailto:addr1,addr2", true, url_parse::Component(7, 11), url_parse::Component()}, - {"mailto:addr1, addr2", "mailto:addr1, addr2", true, url_parse::Component(7, 12), url_parse::Component()}, - {"mailto:addr1%2caddr2", "mailto:addr1%2caddr2", true, url_parse::Component(7, 13), url_parse::Component()}, - {"mailto:\xF0\x90\x8C\x80", "mailto:%F0%90%8C%80", true, url_parse::Component(7, 12), url_parse::Component()}, - // Null character should be escaped to %00 - {"mailto:addr1\0addr2?foo", "mailto:addr1%00addr2?foo", true, url_parse::Component(7, 13), url_parse::Component(21, 3)}, - // Invalid -- UTF-8 encoded surrogate value. - {"mailto:\xed\xa0\x80", "mailto:%EF%BF%BD", false, url_parse::Component(7, 9), url_parse::Component()}, - {"mailto:addr1?", "mailto:addr1?", true, url_parse::Component(7, 5), url_parse::Component(13, 0)}, - }; - - // Define outside of loop to catch bugs where components aren't reset - url_parse::Parsed parsed; - url_parse::Parsed out_parsed; - - for (size_t i = 0; i < ARRAYSIZE(cases); i++) { - int url_len = static_cast<int>(strlen(cases[i].input)); - if (i == 8) { - // The 9th test case purposely has a '\0' in it -- don't count it - // as the string terminator. - url_len = 22; - } - url_parse::ParseMailtoURL(cases[i].input, url_len, &parsed); - - std::string out_str; - url_canon::StdStringCanonOutput output(&out_str); - bool success = url_canon::CanonicalizeMailtoURL(cases[i].input, url_len, - parsed, &output, - &out_parsed); - output.Complete(); - - EXPECT_EQ(cases[i].expected_success, success); - EXPECT_EQ(cases[i].expected, out_str); - - // Make sure the spec was properly identified - EXPECT_EQ(0, out_parsed.scheme.begin); - EXPECT_EQ(6, out_parsed.scheme.len); - - EXPECT_EQ(cases[i].expected_path.begin, out_parsed.path.begin); - EXPECT_EQ(cases[i].expected_path.len, out_parsed.path.len); - - EXPECT_EQ(cases[i].expected_query.begin, out_parsed.query.begin); - EXPECT_EQ(cases[i].expected_query.len, out_parsed.query.len); - } -} - -#ifndef WIN32 - -TEST(URLCanonTest, _itoa_s) { - // We fill the buffer with 0xff to ensure that it's getting properly - // null-terminated. We also allocate one byte more than what we tell - // _itoa_s about, and ensure that the extra byte is untouched. - char buf[6]; - memset(buf, 0xff, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itoa_s(12, buf, sizeof(buf) - 1, 10)); - EXPECT_STREQ("12", buf); - EXPECT_EQ('\xFF', buf[3]); - - // Test the edge cases - exactly the buffer size and one over - memset(buf, 0xff, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itoa_s(1234, buf, sizeof(buf) - 1, 10)); - EXPECT_STREQ("1234", buf); - EXPECT_EQ('\xFF', buf[5]); - - memset(buf, 0xff, sizeof(buf)); - EXPECT_EQ(EINVAL, url_canon::_itoa_s(12345, buf, sizeof(buf) - 1, 10)); - EXPECT_EQ('\xFF', buf[5]); // should never write to this location - - // Test the template overload (note that this will see the full buffer) - memset(buf, 0xff, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itoa_s(12, buf, 10)); - EXPECT_STREQ("12", buf); - EXPECT_EQ('\xFF', buf[3]); - - memset(buf, 0xff, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itoa_s(12345, buf, 10)); - EXPECT_STREQ("12345", buf); - - EXPECT_EQ(EINVAL, url_canon::_itoa_s(123456, buf, 10)); - - // Test that radix 16 is supported. - memset(buf, 0xff, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itoa_s(1234, buf, sizeof(buf) - 1, 16)); - EXPECT_STREQ("4d2", buf); - EXPECT_EQ('\xFF', buf[5]); -} - -TEST(URLCanonTest, _itow_s) { - // We fill the buffer with 0xff to ensure that it's getting properly - // null-terminated. We also allocate one byte more than what we tell - // _itoa_s about, and ensure that the extra byte is untouched. - char16 buf[6]; - const char fill_mem = 0xff; - const char16 fill_char = 0xffff; - memset(buf, fill_mem, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itow_s(12, buf, sizeof(buf) / 2 - 1, 10)); - EXPECT_EQ(WStringToUTF16(L"12"), string16(buf)); - EXPECT_EQ(fill_char, buf[3]); - - // Test the edge cases - exactly the buffer size and one over - EXPECT_EQ(0, url_canon::_itow_s(1234, buf, sizeof(buf) / 2 - 1, 10)); - EXPECT_EQ(WStringToUTF16(L"1234"), string16(buf)); - EXPECT_EQ(fill_char, buf[5]); - - memset(buf, fill_mem, sizeof(buf)); - EXPECT_EQ(EINVAL, url_canon::_itow_s(12345, buf, sizeof(buf) / 2 - 1, 10)); - EXPECT_EQ(fill_char, buf[5]); // should never write to this location - - // Test the template overload (note that this will see the full buffer) - memset(buf, fill_mem, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itow_s(12, buf, 10)); - EXPECT_EQ(WStringToUTF16(L"12"), string16(buf)); - EXPECT_EQ(fill_char, buf[3]); - - memset(buf, fill_mem, sizeof(buf)); - EXPECT_EQ(0, url_canon::_itow_s(12345, buf, 10)); - EXPECT_EQ(WStringToUTF16(L"12345"), string16(buf)); - - EXPECT_EQ(EINVAL, url_canon::_itow_s(123456, buf, 10)); -} - -#endif // !WIN32 - -// Returns true if the given two structures are the same. -static bool ParsedIsEqual(const url_parse::Parsed& a, - const url_parse::Parsed& b) { - return a.scheme.begin == b.scheme.begin && a.scheme.len == b.scheme.len && - a.username.begin == b.username.begin && a.username.len == b.username.len && - a.password.begin == b.password.begin && a.password.len == b.password.len && - a.host.begin == b.host.begin && a.host.len == b.host.len && - a.port.begin == b.port.begin && a.port.len == b.port.len && - a.path.begin == b.path.begin && a.path.len == b.path.len && - a.query.begin == b.query.begin && a.query.len == b.query.len && - a.ref.begin == b.ref.begin && a.ref.len == b.ref.len; -} - -TEST(URLCanonTest, ResolveRelativeURL) { - struct RelativeCase { - const char* base; // Input base URL: MUST BE CANONICAL - bool is_base_hier; // Is the base URL hierarchical - bool is_base_file; // Tells us if the base is a file URL. - const char* test; // Input URL to test against. - bool succeed_relative; // Whether we expect IsRelativeURL to succeed - bool is_rel; // Whether we expect |test| to be relative or not. - bool succeed_resolve; // Whether we expect ResolveRelativeURL to succeed. - const char* resolved; // What we expect in the result when resolving. - } rel_cases[] = { - // Basic absolute input. - {"http://host/a", true, false, "http://another/", true, false, false, NULL}, - {"http://host/a", true, false, "http:////another/", true, false, false, NULL}, - // Empty relative URLs should only remove the ref part of the URL, - // leaving the rest unchanged. - {"http://foo/bar", true, false, "", true, true, true, "http://foo/bar"}, - {"http://foo/bar#ref", true, false, "", true, true, true, "http://foo/bar"}, - {"http://foo/bar#", true, false, "", true, true, true, "http://foo/bar"}, - // Spaces at the ends of the relative path should be ignored. - {"http://foo/bar", true, false, " another ", true, true, true, "http://foo/another"}, - {"http://foo/bar", true, false, " . ", true, true, true, "http://foo/"}, - {"http://foo/bar", true, false, " \t ", true, true, true, "http://foo/bar"}, - // Matching schemes without two slashes are treated as relative. - {"http://host/a", true, false, "http:path", true, true, true, "http://host/path"}, - {"http://host/a/", true, false, "http:path", true, true, true, "http://host/a/path"}, - {"http://host/a", true, false, "http:/path", true, true, true, "http://host/path"}, - {"http://host/a", true, false, "HTTP:/path", true, true, true, "http://host/path"}, - // Nonmatching schemes are absolute. - {"http://host/a", true, false, "https:host2", true, false, false, NULL}, - {"http://host/a", true, false, "htto:/host2", true, false, false, NULL}, - // Absolute path input - {"http://host/a", true, false, "/b/c/d", true, true, true, "http://host/b/c/d"}, - {"http://host/a", true, false, "\\b\\c\\d", true, true, true, "http://host/b/c/d"}, - {"http://host/a", true, false, "/b/../c", true, true, true, "http://host/c"}, - {"http://host/a?b#c", true, false, "/b/../c", true, true, true, "http://host/c"}, - {"http://host/a", true, false, "\\b/../c?x#y", true, true, true, "http://host/c?x#y"}, - {"http://host/a?b#c", true, false, "/b/../c?x#y", true, true, true, "http://host/c?x#y"}, - // Relative path input - {"http://host/a", true, false, "b", true, true, true, "http://host/b"}, - {"http://host/a", true, false, "bc/de", true, true, true, "http://host/bc/de"}, - {"http://host/a/", true, false, "bc/de?query#ref", true, true, true, "http://host/a/bc/de?query#ref"}, - {"http://host/a/", true, false, ".", true, true, true, "http://host/a/"}, - {"http://host/a/", true, false, "..", true, true, true, "http://host/"}, - {"http://host/a/", true, false, "./..", true, true, true, "http://host/"}, - {"http://host/a/", true, false, "../.", true, true, true, "http://host/"}, - {"http://host/a/", true, false, "././.", true, true, true, "http://host/a/"}, - {"http://host/a?query#ref", true, false, "../../../foo", true, true, true, "http://host/foo"}, - // Query input - {"http://host/a", true, false, "?foo=bar", true, true, true, "http://host/a?foo=bar"}, - {"http://host/a?x=y#z", true, false, "?", true, true, true, "http://host/a?"}, - {"http://host/a?x=y#z", true, false, "?foo=bar#com", true, true, true, "http://host/a?foo=bar#com"}, - // Ref input - {"http://host/a", true, false, "#ref", true, true, true, "http://host/a#ref"}, - {"http://host/a#b", true, false, "#", true, true, true, "http://host/a#"}, - {"http://host/a?foo=bar#hello", true, false, "#bye", true, true, true, "http://host/a?foo=bar#bye"}, - // Non-hierarchical base: no relative handling. Relative input should - // error, and if a scheme is present, it should be treated as absolute. - {"data:foobar", false, false, "baz.html", false, false, false, NULL}, - {"data:foobar", false, false, "data:baz", true, false, false, NULL}, - {"data:foobar", false, false, "data:/base", true, false, false, NULL}, - // Non-hierarchical base: absolute input should succeed. - {"data:foobar", false, false, "http://host/", true, false, false, NULL}, - {"data:foobar", false, false, "http:host", true, false, false, NULL}, - // Invalid schemes should be treated as relative. - {"http://foo/bar", true, false, "./asd:fgh", true, true, true, "http://foo/asd:fgh"}, - {"http://foo/bar", true, false, ":foo", true, true, true, "http://foo/:foo"}, - {"http://foo/bar", true, false, " hello world", true, true, true, "http://foo/hello%20world"}, - {"data:asdf", false, false, ":foo", false, false, false, NULL}, - // We should treat semicolons like any other character in URL resolving - {"http://host/a", true, false, ";foo", true, true, true, "http://host/;foo"}, - {"http://host/a;", true, false, ";foo", true, true, true, "http://host/;foo"}, - {"http://host/a", true, false, ";/../bar", true, true, true, "http://host/bar"}, - // Relative URLs can also be written as "//foo/bar" which is relative to - // the scheme. In this case, it would take the old scheme, so for http - // the example would resolve to "http://foo/bar". - {"http://host/a", true, false, "//another", true, true, true, "http://another/"}, - {"http://host/a", true, false, "//another/path?query#ref", true, true, true, "http://another/path?query#ref"}, - {"http://host/a", true, false, "///another/path", true, true, true, "http://another/path"}, - {"http://host/a", true, false, "//Another\\path", true, true, true, "http://another/path"}, - {"http://host/a", true, false, "//", true, true, false, "http:"}, - // IE will also allow one or the other to be a backslash to get the same - // behavior. - {"http://host/a", true, false, "\\/another/path", true, true, true, "http://another/path"}, - {"http://host/a", true, false, "/\\Another\\path", true, true, true, "http://another/path"}, -#ifdef WIN32 - // Resolving against Windows file base URLs. - {"file:///C:/foo", true, true, "http://host/", true, false, false, NULL}, - {"file:///C:/foo", true, true, "bar", true, true, true, "file:///C:/bar"}, - {"file:///C:/foo", true, true, "../../../bar.html", true, true, true, "file:///C:/bar.html"}, - {"file:///C:/foo", true, true, "/../bar.html", true, true, true, "file:///C:/bar.html"}, - // But two backslashes on Windows should be UNC so should be treated - // as absolute. - {"http://host/a", true, false, "\\\\another\\path", true, false, false, NULL}, - // IE doesn't support drive specs starting with two slashes. It fails - // immediately and doesn't even try to load. We fix it up to either - // an absolute path or UNC depending on what it looks like. - {"file:///C:/something", true, true, "//c:/foo", true, true, true, "file:///C:/foo"}, - {"file:///C:/something", true, true, "//localhost/c:/foo", true, true, true, "file:///C:/foo"}, - // Windows drive specs should be allowed and treated as absolute. - {"file:///C:/foo", true, true, "c:", true, false, false, NULL}, - {"file:///C:/foo", true, true, "c:/foo", true, false, false, NULL}, - {"http://host/a", true, false, "c:\\foo", true, false, false, NULL}, - // Relative paths with drive letters should be allowed when the base is - // also a file. - {"file:///C:/foo", true, true, "/z:/bar", true, true, true, "file:///Z:/bar"}, - // Treat absolute paths as being off of the drive. - {"file:///C:/foo", true, true, "/bar", true, true, true, "file:///C:/bar"}, - {"file://localhost/C:/foo", true, true, "/bar", true, true, true, "file://localhost/C:/bar"}, - {"file:///C:/foo/com/", true, true, "/bar", true, true, true, "file:///C:/bar"}, - // On Windows, two slashes without a drive letter when the base is a file - // means that the path is UNC. - {"file:///C:/something", true, true, "//somehost/path", true, true, true, "file://somehost/path"}, - {"file:///C:/something", true, true, "/\\//somehost/path", true, true, true, "file://somehost/path"}, -#else - // On Unix we fall back to relative behavior since there's nothing else - // reasonable to do. - {"http://host/a", true, false, "\\\\Another\\path", true, true, true, "http://another/path"}, -#endif - // Even on Windows, we don't allow relative drive specs when the base - // is not file. - {"http://host/a", true, false, "/c:\\foo", true, true, true, "http://host/c:/foo"}, - {"http://host/a", true, false, "//c:\\foo", true, true, true, "http://c/foo"}, - }; - - for (size_t i = 0; i < ARRAYSIZE(rel_cases); i++) { - const RelativeCase& cur_case = rel_cases[i]; - - url_parse::Parsed parsed; - int base_len = static_cast<int>(strlen(cur_case.base)); - if (cur_case.is_base_file) - url_parse::ParseFileURL(cur_case.base, base_len, &parsed); - else if (cur_case.is_base_hier) - url_parse::ParseStandardURL(cur_case.base, base_len, &parsed); - else - url_parse::ParsePathURL(cur_case.base, base_len, &parsed); - - // First see if it is relative. - int test_len = static_cast<int>(strlen(cur_case.test)); - bool is_relative; - url_parse::Component relative_component; - bool succeed_is_rel = url_canon::IsRelativeURL( - cur_case.base, parsed, cur_case.test, test_len, cur_case.is_base_hier, - &is_relative, &relative_component); - - EXPECT_EQ(cur_case.succeed_relative, succeed_is_rel) << - "succeed is rel failure on " << cur_case.test; - EXPECT_EQ(cur_case.is_rel, is_relative) << - "is rel failure on " << cur_case.test; - // Now resolve it. - if (succeed_is_rel && is_relative && cur_case.is_rel) { - std::string resolved; - url_canon::StdStringCanonOutput output(&resolved); - url_parse::Parsed resolved_parsed; - - bool succeed_resolve = url_canon::ResolveRelativeURL( - cur_case.base, parsed, cur_case.is_base_file, - cur_case.test, relative_component, NULL, &output, &resolved_parsed); - output.Complete(); - - EXPECT_EQ(cur_case.succeed_resolve, succeed_resolve); - EXPECT_EQ(cur_case.resolved, resolved) << " on " << cur_case.test; - - // Verify that the output parsed structure is the same as parsing a - // the URL freshly. - url_parse::Parsed ref_parsed; - int resolved_len = static_cast<int>(resolved.size()); - if (cur_case.is_base_file) - url_parse::ParseFileURL(resolved.c_str(), resolved_len, &ref_parsed); - else if (cur_case.is_base_hier) - url_parse::ParseStandardURL(resolved.c_str(), resolved_len, &ref_parsed); - else - url_parse::ParsePathURL(resolved.c_str(), resolved_len, &ref_parsed); - EXPECT_TRUE(ParsedIsEqual(ref_parsed, resolved_parsed)); - } - } -} - -// It used to be when we did a replacement with a long buffer of UTF-16 -// characters, we would get invalid data in the URL. This is because the buffer -// it used to hold the UTF-8 data was resized, while some pointers were still -// kept to the old buffer that was removed. -TEST(URLCanonTest, ReplacementOverflow) { - const char src[] = "file:///C:/foo/bar"; - int src_len = static_cast<int>(strlen(src)); - url_parse::Parsed parsed; - url_parse::ParseFileURL(src, src_len, &parsed); - - // Override two components, the path with something short, and the query with - // sonething long enough to trigger the bug. - url_canon::Replacements<char16> repl; - string16 new_query; - for (int i = 0; i < 4800; i++) - new_query.push_back('a'); - - string16 new_path(WStringToUTF16(L"/foo")); - repl.SetPath(new_path.c_str(), url_parse::Component(0, 4)); - repl.SetQuery(new_query.c_str(), - url_parse::Component(0, static_cast<int>(new_query.length()))); - - // Call ReplaceComponents on the string. It doesn't matter if we call it for - // standard URLs, file URLs, etc, since they will go to the same replacement - // function that was buggy. - url_parse::Parsed repl_parsed; - std::string repl_str; - url_canon::StdStringCanonOutput repl_output(&repl_str); - url_canon::ReplaceFileURL(src, parsed, repl, NULL, &repl_output, &repl_parsed); - repl_output.Complete(); - - // Generate the expected string and check. - std::string expected("file:///foo?"); - for (size_t i = 0; i < new_query.length(); i++) - expected.push_back('a'); - EXPECT_TRUE(expected == repl_str); -}
diff --git a/googleurl/src/url_common.h b/googleurl/src/url_common.h deleted file mode 100644 index 7e7e27a..0000000 --- a/googleurl/src/url_common.h +++ /dev/null
@@ -1,48 +0,0 @@ -// Copyright 2010, Google 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: -// -// * 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 Google Inc. 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. - -#ifndef GOOGLEURL_SRC_URL_COMMON_H__ -#define GOOGLEURL_SRC_URL_COMMON_H__ - -#if !defined(GURL_IMPLEMENTATION) -#define GURL_IMPLEMENTATION 0 -#endif - -#if defined(WIN32) && defined(GURL_DLL) -#if GURL_IMPLEMENTATION -#define GURL_API __declspec(dllexport) -#else -#define GURL_API __declspec(dllimport) -#endif -#else -#define GURL_API -#endif - -#endif // GOOGLEURL_SRC_URL_COMMON_H__ -
diff --git a/googleurl/src/url_file.h b/googleurl/src/url_file.h deleted file mode 100644 index c1b8ac9..0000000 --- a/googleurl/src/url_file.h +++ /dev/null
@@ -1,108 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -// Provides shared functions used by the internals of the parser and -// canonicalizer for file URLs. Do not use outside of these modules. - -#ifndef GOOGLEURL_SRC_URL_FILE_H__ -#define GOOGLEURL_SRC_URL_FILE_H__ - -#include "googleurl/src/url_parse_internal.h" - -namespace url_parse { - -#ifdef WIN32 - -// We allow both "c:" and "c|" as drive identifiers. -inline bool IsWindowsDriveSeparator(char16 ch) { - return ch == ':' || ch == '|'; -} -inline bool IsWindowsDriveLetter(char16 ch) { - return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); -} - -#endif // WIN32 - -// Returns the index of the next slash in the input after the given index, or -// spec_len if the end of the input is reached. -template<typename CHAR> -inline int FindNextSlash(const CHAR* spec, int begin_index, int spec_len) { - int idx = begin_index; - while (idx < spec_len && !IsURLSlash(spec[idx])) - idx++; - return idx; -} - -#ifdef WIN32 - -// Returns true if the start_offset in the given spec looks like it begins a -// drive spec, for example "c:". This function explicitly handles start_offset -// values that are equal to or larger than the spec_len to simplify callers. -// -// If this returns true, the spec is guaranteed to have a valid drive letter -// plus a colon starting at |start_offset|. -template<typename CHAR> -inline bool DoesBeginWindowsDriveSpec(const CHAR* spec, int start_offset, - int spec_len) { - int remaining_len = spec_len - start_offset; - if (remaining_len < 2) - return false; // Not enough room. - if (!IsWindowsDriveLetter(spec[start_offset])) - return false; // Doesn't start with a valid drive letter. - if (!IsWindowsDriveSeparator(spec[start_offset + 1])) - return false; // Isn't followed with a drive separator. - return true; -} - -// Returns true if the start_offset in the given text looks like it begins a -// UNC path, for example "\\". This function explicitly handles start_offset -// values that are equal to or larger than the spec_len to simplify callers. -// -// When strict_slashes is set, this function will only accept backslashes as is -// standard for Windows. Otherwise, it will accept forward slashes as well -// which we use for a lot of URL handling. -template<typename CHAR> -inline bool DoesBeginUNCPath(const CHAR* text, - int start_offset, - int len, - bool strict_slashes) { - int remaining_len = len - start_offset; - if (remaining_len < 2) - return false; - - if (strict_slashes) - return text[start_offset] == '\\' && text[start_offset + 1] == '\\'; - return IsURLSlash(text[start_offset]) && IsURLSlash(text[start_offset + 1]); -} - -#endif // WIN32 - -} // namespace url_parse - -#endif // GOOGLEURL_SRC_URL_FILE_H__
diff --git a/googleurl/src/url_parse_internal.h b/googleurl/src/url_parse_internal.h deleted file mode 100644 index 61bd068..0000000 --- a/googleurl/src/url_parse_internal.h +++ /dev/null
@@ -1,112 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -// Contains common inline helper functions used by the URL parsing routines. - -#ifndef GOOGLEURL_SRC_URL_PARSE_INTERNAL_H__ -#define GOOGLEURL_SRC_URL_PARSE_INTERNAL_H__ - -#include "googleurl/src/url_parse.h" - -namespace url_parse { - -// We treat slashes and backslashes the same for IE compatability. -inline bool IsURLSlash(char16 ch) { - return ch == '/' || ch == '\\'; -} - -// Returns true if we should trim this character from the URL because it is a -// space or a control character. -inline bool ShouldTrimFromURL(char16 ch) { - return ch <= ' '; -} - -// Given an already-initialized begin index and length, this shrinks the range -// to eliminate "should-be-trimmed" characters. Note that the length does *not* -// indicate the length of untrimmed data from |*begin|, but rather the position -// in the input string (so the string starts at character |*begin| in the spec, -// and goes until |*len|). -template<typename CHAR> -inline void TrimURL(const CHAR* spec, int* begin, int* len) { - // Strip leading whitespace and control characters. - while (*begin < *len && ShouldTrimFromURL(spec[*begin])) - (*begin)++; - - // Strip trailing whitespace and control characters. We need the >i test for - // when the input string is all blanks; we don't want to back past the input. - while (*len > *begin && ShouldTrimFromURL(spec[*len - 1])) - (*len)--; -} - -// Counts the number of consecutive slashes starting at the given offset -// in the given string of the given length. -template<typename CHAR> -inline int CountConsecutiveSlashes(const CHAR *str, - int begin_offset, int str_len) { - int count = 0; - while (begin_offset + count < str_len && - IsURLSlash(str[begin_offset + count])) - ++count; - return count; -} - -// Internal functions in url_parse.cc that parse the path, that is, everything -// following the authority section. The input is the range of everything -// following the authority section, and the output is the identified ranges. -// -// This is designed for the file URL parser or other consumers who may do -// special stuff at the beginning, but want regular path parsing, it just -// maps to the internal parsing function for paths. -void ParsePathInternal(const char* spec, - const Component& path, - Component* filepath, - Component* query, - Component* ref); -void ParsePathInternal(const char16* spec, - const Component& path, - Component* filepath, - Component* query, - Component* ref); - - -// Given a spec and a pointer to the character after the colon following the -// scheme, this parses it and fills in the structure, Every item in the parsed -// structure is filled EXCEPT for the scheme, which is untouched. -void ParseAfterScheme(const char* spec, - int spec_len, - int after_scheme, - Parsed* parsed); -void ParseAfterScheme(const char16* spec, - int spec_len, - int after_scheme, - Parsed* parsed); - -} // namespace url_parse - -#endif // GOOGLEURL_SRC_URL_PARSE_INTERNAL_H__
diff --git a/googleurl/src/url_test_utils.h b/googleurl/src/url_test_utils.h deleted file mode 100644 index fdadf7f..0000000 --- a/googleurl/src/url_test_utils.h +++ /dev/null
@@ -1,85 +0,0 @@ -// Copyright 2007 Google 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: -// -// * 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 Google Inc. 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. - -// Convenience functions for string conversions. -// These are mostly intended for use in unit tests. - -#ifndef GOOGLEURL_SRC_URL_TEST_UTILS_H__ -#define GOOGLEURL_SRC_URL_TEST_UTILS_H__ - -#include <string> - -#include "base/string16.h" -#include "googleurl/src/url_canon_internal.h" -#include "testing/base/public/gunit.h" - -namespace url_test_utils { - -// Converts a UTF-16 string from native wchar_t format to char16, by -// truncating the high 32 bits. This is not meant to handle true UTF-32 -// encoded strings. -inline string16 WStringToUTF16(const wchar_t* src) { - string16 str; - int length = static_cast<int>(wcslen(src)); - for (int i = 0; i < length; ++i) { - str.push_back(static_cast<char16>(src[i])); - } - return str; -} - -// Converts a string from UTF-8 to UTF-16 -inline string16 ConvertUTF8ToUTF16(const std::string& src) { - int length = static_cast<int>(src.length()); - EXPECT_LT(length, 1024); - url_canon::RawCanonOutputW<1024> output; - EXPECT_TRUE(url_canon::ConvertUTF8ToUTF16(src.data(), length, &output)); - return string16(output.data(), output.length()); -} - -// Converts a string from UTF-16 to UTF-8 -inline std::string ConvertUTF16ToUTF8(const string16& src) { - std::string str; - url_canon::StdStringCanonOutput output(&str); - EXPECT_TRUE(url_canon::ConvertUTF16ToUTF8(src.data(), - static_cast<int>(src.length()), - &output)); - output.Complete(); - return str; -} - -} // namespace url_test_utils - -// This operator allows EXPECT_EQ(astring16, anotherstring16); to work. -inline std::ostream& operator<<(std::ostream& os, - const string16& str) { - // Convert to UTF-8 and print the string - return os << url_test_utils::ConvertUTF16ToUTF8(str); -} - -#endif // GOOGLEURL_SRC_URL_TEST_UTILS_H__
diff --git a/googleurl/src/url_util.cc b/googleurl/src/url_util.cc deleted file mode 100644 index 7e100aa..0000000 --- a/googleurl/src/url_util.cc +++ /dev/null
@@ -1,553 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -#include <string.h> -#include <vector> - -#include "googleurl/src/url_util.h" - -#include "base/logging.h" -#include "googleurl/src/url_canon_internal.h" -#include "googleurl/src/url_file.h" - -namespace url_util { - -namespace { - -// ASCII-specific tolower. The standard library's tolower is locale sensitive, -// so we don't want to use it here. -template <class Char> inline Char ToLowerASCII(Char c) { - return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; -} - -// Backend for LowerCaseEqualsASCII. -template<typename Iter> -inline bool DoLowerCaseEqualsASCII(Iter a_begin, Iter a_end, const char* b) { - for (Iter it = a_begin; it != a_end; ++it, ++b) { - if (!*b || ToLowerASCII(*it) != *b) - return false; - } - return *b == 0; -} - -const char kFileScheme[] = "file"; // Used in a number of places. -const char kMailtoScheme[] = "mailto"; - -const int kNumStandardURLSchemes = 7; -const char* kStandardURLSchemes[kNumStandardURLSchemes] = { - "http", - "https", - kFileScheme, // Yes, file urls can have a hostname! - "ftp", - "gopher", - "ws", // WebSocket. - "wss", // WebSocket secure. -}; - -// List of the currently installed standard schemes. This list is lazily -// initialized by InitStandardSchemes and is leaked on shutdown to prevent -// any destructors from being called that will slow us down or cause problems. -std::vector<const char*>* standard_schemes = NULL; - -// See the LockStandardSchemes declaration in the header. -bool standard_schemes_locked = false; - -// Ensures that the standard_schemes list is initialized, does nothing if it -// already has values. -void InitStandardSchemes() { - if (standard_schemes) - return; - standard_schemes = new std::vector<const char*>; - for (int i = 0; i < kNumStandardURLSchemes; i++) - standard_schemes->push_back(kStandardURLSchemes[i]); -} - -// Given a string and a range inside the string, compares it to the given -// lower-case |compare_to| buffer. -template<typename CHAR> -inline bool CompareSchemeComponent(const CHAR* spec, - const url_parse::Component& component, - const char* compare_to) { - if (!component.is_nonempty()) - return compare_to[0] == 0; // When component is empty, match empty scheme. - return LowerCaseEqualsASCII(&spec[component.begin], - &spec[component.end()], - compare_to); -} - -// Returns true if the given scheme identified by |scheme| within |spec| is one -// of the registered "standard" schemes. -template<typename CHAR> -bool DoIsStandard(const CHAR* spec, const url_parse::Component& scheme) { - if (!scheme.is_nonempty()) - return false; // Empty or invalid schemes are non-standard. - - InitStandardSchemes(); - for (size_t i = 0; i < standard_schemes->size(); i++) { - if (LowerCaseEqualsASCII(&spec[scheme.begin], &spec[scheme.end()], - standard_schemes->at(i))) - return true; - } - return false; -} - -template<typename CHAR> -bool DoFindAndCompareScheme(const CHAR* str, - int str_len, - const char* compare, - url_parse::Component* found_scheme) { - // Before extracting scheme, canonicalize the URL to remove any whitespace. - // This matches the canonicalization done in DoCanonicalize function. - url_canon::RawCanonOutputT<CHAR> whitespace_buffer; - int spec_len; - const CHAR* spec = RemoveURLWhitespace(str, str_len, - &whitespace_buffer, &spec_len); - - url_parse::Component our_scheme; - if (!url_parse::ExtractScheme(spec, spec_len, &our_scheme)) { - // No scheme. - if (found_scheme) - *found_scheme = url_parse::Component(); - return false; - } - if (found_scheme) - *found_scheme = our_scheme; - return CompareSchemeComponent(spec, our_scheme, compare); -} - -template<typename CHAR> -bool DoCanonicalize(const CHAR* in_spec, int in_spec_len, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed) { - // Remove any whitespace from the middle of the relative URL, possibly - // copying to the new buffer. - url_canon::RawCanonOutputT<CHAR> whitespace_buffer; - int spec_len; - const CHAR* spec = RemoveURLWhitespace(in_spec, in_spec_len, - &whitespace_buffer, &spec_len); - - url_parse::Parsed parsed_input; -#ifdef WIN32 - // For Windows, we allow things that look like absolute Windows paths to be - // fixed up magically to file URLs. This is done for IE compatability. For - // example, this will change "c:/foo" into a file URL rather than treating - // it as a URL with the protocol "c". It also works for UNC ("\\foo\bar.txt"). - // There is similar logic in url_canon_relative.cc for - // - // For Max & Unix, we don't do this (the equivalent would be "/foo/bar" which - // has no meaning as an absolute path name. This is because browsers on Mac - // & Unix don't generally do this, so there is no compatibility reason for - // doing so. - if (url_parse::DoesBeginUNCPath(spec, 0, spec_len, false) || - url_parse::DoesBeginWindowsDriveSpec(spec, 0, spec_len)) { - url_parse::ParseFileURL(spec, spec_len, &parsed_input); - return url_canon::CanonicalizeFileURL(spec, spec_len, parsed_input, - charset_converter, - output, output_parsed); - } -#endif - - url_parse::Component scheme; - if (!url_parse::ExtractScheme(spec, spec_len, &scheme)) - return false; - - // This is the parsed version of the input URL, we have to canonicalize it - // before storing it in our object. - bool success; - if (CompareSchemeComponent(spec, scheme, kFileScheme)) { - // File URLs are special. - url_parse::ParseFileURL(spec, spec_len, &parsed_input); - success = url_canon::CanonicalizeFileURL(spec, spec_len, parsed_input, - charset_converter, - output, output_parsed); - - } else if (DoIsStandard(spec, scheme)) { - // All "normal" URLs. - url_parse::ParseStandardURL(spec, spec_len, &parsed_input); - success = url_canon::CanonicalizeStandardURL(spec, spec_len, parsed_input, - charset_converter, - output, output_parsed); - - } else if (CompareSchemeComponent(spec, scheme, kMailtoScheme)) { - // Mailto are treated like a standard url with only a scheme, path, query - url_parse::ParseMailtoURL(spec, spec_len, &parsed_input); - success = url_canon::CanonicalizeMailtoURL(spec, spec_len, parsed_input, - output, output_parsed); - - } else { - // "Weird" URLs like data: and javascript: - url_parse::ParsePathURL(spec, spec_len, &parsed_input); - success = url_canon::CanonicalizePathURL(spec, spec_len, parsed_input, - output, output_parsed); - } - return success; -} - -template<typename CHAR> -bool DoResolveRelative(const char* base_spec, - int base_spec_len, - const url_parse::Parsed& base_parsed, - const CHAR* in_relative, - int in_relative_length, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed) { - // Remove any whitespace from the middle of the relative URL, possibly - // copying to the new buffer. - url_canon::RawCanonOutputT<CHAR> whitespace_buffer; - int relative_length; - const CHAR* relative = RemoveURLWhitespace(in_relative, in_relative_length, - &whitespace_buffer, - &relative_length); - - // See if our base URL should be treated as "standard". - bool standard_base_scheme = - base_parsed.scheme.is_nonempty() && - DoIsStandard(base_spec, base_parsed.scheme); - - bool is_relative; - url_parse::Component relative_component; - if (!url_canon::IsRelativeURL(base_spec, base_parsed, - relative, relative_length, - standard_base_scheme, - &is_relative, - &relative_component)) { - // Error resolving. - return false; - } - - if (is_relative) { - // Relative, resolve and canonicalize. - bool file_base_scheme = base_parsed.scheme.is_nonempty() && - CompareSchemeComponent(base_spec, base_parsed.scheme, kFileScheme); - return url_canon::ResolveRelativeURL(base_spec, base_parsed, - file_base_scheme, relative, - relative_component, charset_converter, - output, output_parsed); - } - - // Not relative, canonicalize the input. - return DoCanonicalize(relative, relative_length, charset_converter, - output, output_parsed); -} - -template<typename CHAR> -bool DoReplaceComponents(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - const url_canon::Replacements<CHAR>& replacements, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* out_parsed) { - // If the scheme is overridden, just do a simple string substitution and - // reparse the whole thing. There are lots of edge cases that we really don't - // want to deal with. Like what happens if I replace "http://e:8080/foo" - // with a file. Does it become "file:///E:/8080/foo" where the port number - // becomes part of the path? Parsing that string as a file URL says "yes" - // but almost no sane rule for dealing with the components individually would - // come up with that. - // - // Why allow these crazy cases at all? Programatically, there is almost no - // case for replacing the scheme. The most common case for hitting this is - // in JS when building up a URL using the location object. In this case, the - // JS code expects the string substitution behavior: - // http://www.w3.org/TR/2008/WD-html5-20080610/structured.html#common3 - if (replacements.IsSchemeOverridden()) { - // Canonicalize the new scheme so it is 8-bit and can be concatenated with - // the existing spec. - url_canon::RawCanonOutput<128> scheme_replaced; - url_parse::Component scheme_replaced_parsed; - url_canon::CanonicalizeScheme( - replacements.sources().scheme, - replacements.components().scheme, - &scheme_replaced, &scheme_replaced_parsed); - - // We can assume that the input is canonicalized, which means it always has - // a colon after the scheme (or where the scheme would be). - int spec_after_colon = parsed.scheme.is_valid() ? parsed.scheme.end() + 1 - : 1; - if (spec_len - spec_after_colon > 0) { - scheme_replaced.Append(&spec[spec_after_colon], - spec_len - spec_after_colon); - } - - // We now need to completely re-parse the resulting string since its meaning - // may have changed with the different scheme. - url_canon::RawCanonOutput<128> recanonicalized; - url_parse::Parsed recanonicalized_parsed; - DoCanonicalize(scheme_replaced.data(), scheme_replaced.length(), - charset_converter, - &recanonicalized, &recanonicalized_parsed); - - // Recurse using the version with the scheme already replaced. This will now - // use the replacement rules for the new scheme. - // - // Warning: this code assumes that ReplaceComponents will re-check all - // components for validity. This is because we can't fail if DoCanonicalize - // failed above since theoretically the thing making it fail could be - // getting replaced here. If ReplaceComponents didn't re-check everything, - // we wouldn't know if something *not* getting replaced is a problem. - // If the scheme-specific replacers are made more intelligent so they don't - // re-check everything, we should instead recanonicalize the whole thing - // after this call to check validity (this assumes replacing the scheme is - // much much less common than other types of replacements, like clearing the - // ref). - url_canon::Replacements<CHAR> replacements_no_scheme = replacements; - replacements_no_scheme.SetScheme(NULL, url_parse::Component()); - return DoReplaceComponents(recanonicalized.data(), recanonicalized.length(), - recanonicalized_parsed, replacements_no_scheme, - charset_converter, output, out_parsed); - } - - // If we get here, then we know the scheme doesn't need to be replaced, so can - // just key off the scheme in the spec to know how to do the replacements. - if (CompareSchemeComponent(spec, parsed.scheme, kFileScheme)) { - return url_canon::ReplaceFileURL(spec, parsed, replacements, - charset_converter, output, out_parsed); - } - if (DoIsStandard(spec, parsed.scheme)) { - return url_canon::ReplaceStandardURL(spec, parsed, replacements, - charset_converter, output, out_parsed); - } - if (CompareSchemeComponent(spec, parsed.scheme, kMailtoScheme)) { - return url_canon::ReplaceMailtoURL(spec, parsed, replacements, - output, out_parsed); - } - - // Default is a path URL. - return url_canon::ReplacePathURL(spec, parsed, replacements, - output, out_parsed); -} - -} // namespace - -void Initialize() { - InitStandardSchemes(); -} - -void Shutdown() { - if (standard_schemes) { - delete standard_schemes; - standard_schemes = NULL; - } -} - -void AddStandardScheme(const char* new_scheme) { - // If this assert triggers, it means you've called AddStandardScheme after - // LockStandardSchemes have been called (see the header file for - // LockStandardSchemes for more). - // - // This normally means you're trying to set up a new standard scheme too late - // in your application's init process. Locate where your app does this - // initialization and calls LockStandardScheme, and add your new standard - // scheme there. - DCHECK(!standard_schemes_locked) << - "Trying to add a standard scheme after the list has been locked."; - - size_t scheme_len = strlen(new_scheme); - if (scheme_len == 0) - return; - - // Dulicate the scheme into a new buffer and add it to the list of standard - // schemes. This pointer will be leaked on shutdown. - char* dup_scheme = new char[scheme_len + 1]; - memcpy(dup_scheme, new_scheme, scheme_len + 1); - - InitStandardSchemes(); - standard_schemes->push_back(dup_scheme); -} - -void LockStandardSchemes() { - standard_schemes_locked = true; -} - -bool IsStandard(const char* spec, const url_parse::Component& scheme) { - return DoIsStandard(spec, scheme); -} - -bool IsStandard(const char16* spec, const url_parse::Component& scheme) { - return DoIsStandard(spec, scheme); -} - -bool FindAndCompareScheme(const char* str, - int str_len, - const char* compare, - url_parse::Component* found_scheme) { - return DoFindAndCompareScheme(str, str_len, compare, found_scheme); -} - -bool FindAndCompareScheme(const char16* str, - int str_len, - const char* compare, - url_parse::Component* found_scheme) { - return DoFindAndCompareScheme(str, str_len, compare, found_scheme); -} - -bool Canonicalize(const char* spec, - int spec_len, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed) { - return DoCanonicalize(spec, spec_len, charset_converter, - output, output_parsed); -} - -bool Canonicalize(const char16* spec, - int spec_len, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed) { - return DoCanonicalize(spec, spec_len, charset_converter, - output, output_parsed); -} - -bool ResolveRelative(const char* base_spec, - int base_spec_len, - const url_parse::Parsed& base_parsed, - const char* relative, - int relative_length, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed) { - return DoResolveRelative(base_spec, base_spec_len, base_parsed, - relative, relative_length, - charset_converter, output, output_parsed); -} - -bool ResolveRelative(const char* base_spec, - int base_spec_len, - const url_parse::Parsed& base_parsed, - const char16* relative, - int relative_length, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed) { - return DoResolveRelative(base_spec, base_spec_len, base_parsed, - relative, relative_length, - charset_converter, output, output_parsed); -} - -bool ReplaceComponents(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - const url_canon::Replacements<char>& replacements, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* out_parsed) { - return DoReplaceComponents(spec, spec_len, parsed, replacements, - charset_converter, output, out_parsed); -} - -bool ReplaceComponents(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - const url_canon::Replacements<char16>& replacements, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* out_parsed) { - return DoReplaceComponents(spec, spec_len, parsed, replacements, - charset_converter, output, out_parsed); -} - -// Front-ends for LowerCaseEqualsASCII. -bool LowerCaseEqualsASCII(const char* a_begin, - const char* a_end, - const char* b) { - return DoLowerCaseEqualsASCII(a_begin, a_end, b); -} - -bool LowerCaseEqualsASCII(const char* a_begin, - const char* a_end, - const char* b_begin, - const char* b_end) { - while (a_begin != a_end && b_begin != b_end && - ToLowerASCII(*a_begin) == *b_begin) { - a_begin++; - b_begin++; - } - return a_begin == a_end && b_begin == b_end; -} - -bool LowerCaseEqualsASCII(const char16* a_begin, - const char16* a_end, - const char* b) { - return DoLowerCaseEqualsASCII(a_begin, a_end, b); -} - -void DecodeURLEscapeSequences(const char* input, int length, - url_canon::CanonOutputW* output) { - url_canon::RawCanonOutputT<char> unescaped_chars; - for (int i = 0; i < length; i++) { - if (input[i] == '%') { - unsigned char ch; - if (url_canon::DecodeEscaped(input, &i, length, &ch)) { - unescaped_chars.push_back(ch); - } else { - // Invalid escape sequence, copy the percent literal. - unescaped_chars.push_back('%'); - } - } else { - // Regular non-escaped 8-bit character. - unescaped_chars.push_back(input[i]); - } - } - - // Convert that 8-bit to UTF-16. It's not clear IE does this at all to - // JavaScript URLs, but Firefox and Safari do. - for (int i = 0; i < unescaped_chars.length(); i++) { - unsigned char uch = static_cast<unsigned char>(unescaped_chars.at(i)); - if (uch < 0x80) { - // Non-UTF-8, just append directly - output->push_back(uch); - } else { - // next_ch will point to the last character of the decoded - // character. - int next_character = i; - unsigned code_point; - if (url_canon::ReadUTFChar(unescaped_chars.data(), &next_character, - unescaped_chars.length(), &code_point)) { - // Valid UTF-8 character, convert to UTF-16. - url_canon::AppendUTF16Value(code_point, output); - i = next_character; - } else { - // If there are any sequences that are not valid UTF-8, we keep - // invalid code points and promote to UTF-16. We copy all characters - // from the current position to the end of the identified sequence. - while (i < next_character) { - output->push_back(static_cast<unsigned char>(unescaped_chars.at(i))); - i++; - } - output->push_back(static_cast<unsigned char>(unescaped_chars.at(i))); - } - } - } -} - -} // namespace url_util
diff --git a/googleurl/src/url_util.h b/googleurl/src/url_util.h deleted file mode 100644 index ec4cf9e..0000000 --- a/googleurl/src/url_util.h +++ /dev/null
@@ -1,222 +0,0 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. - -#ifndef GOOGLEURL_SRC_URL_UTIL_H__ -#define GOOGLEURL_SRC_URL_UTIL_H__ - -#include <string> - -#include "base/string16.h" -#include "googleurl/src/url_common.h" -#include "googleurl/src/url_parse.h" -#include "googleurl/src/url_canon.h" - -namespace url_util { - -// Init ------------------------------------------------------------------------ - -// Initialization is NOT required, it will be implicitly initialized when first -// used. However, this implicit initialization is NOT threadsafe. If you are -// using this library in a threaded environment and don't have a consistent -// "first call" (an example might be calling "AddStandardScheme" with your -// special application-specific schemes) then you will want to call initialize -// before spawning any threads. -// -// It is OK to call this function more than once, subsequent calls will simply -// "noop", unless Shutdown() was called in the mean time. This will also be a -// "noop" if other calls to the library have forced an initialization -// beforehand. -GURL_API void Initialize(); - -// Cleanup is not required, except some strings may leak. For most user -// applications, this is fine. If you're using it in a library that may get -// loaded and unloaded, you'll want to unload to properly clean up your -// library. -GURL_API void Shutdown(); - -// Schemes -------------------------------------------------------------------- - -// Adds an application-defined scheme to the internal list of "standard" URL -// schemes. This function is not threadsafe and can not be called concurrently -// with any other url_util function. It will assert if the list of standard -// schemes has been locked (see LockStandardSchemes). -GURL_API void AddStandardScheme(const char* new_scheme); - -// Sets a flag to prevent future calls to AddStandardScheme from succeeding. -// -// This is designed to help prevent errors for multithreaded applications. -// Normal usage would be to call AddStandardScheme for your custom schemes at -// the beginning of program initialization, and then LockStandardSchemes. This -// prevents future callers from mistakenly calling AddStandardScheme when the -// program is running with multiple threads, where such usage would be -// dangerous. -// -// We could have had AddStandardScheme use a lock instead, but that would add -// some platform-specific dependencies we don't otherwise have now, and is -// overkill considering the normal usage is so simple. -GURL_API void LockStandardSchemes(); - -// Locates the scheme in the given string and places it into |found_scheme|, -// which may be NULL to indicate the caller does not care about the range. -// -// Returns whether the given |compare| scheme matches the scheme found in the -// input (if any). The |compare| scheme must be a valid canonical scheme or -// the result of the comparison is undefined. -GURL_API bool FindAndCompareScheme(const char* str, - int str_len, - const char* compare, - url_parse::Component* found_scheme); -GURL_API bool FindAndCompareScheme(const char16* str, - int str_len, - const char* compare, - url_parse::Component* found_scheme); -inline bool FindAndCompareScheme(const std::string& str, - const char* compare, - url_parse::Component* found_scheme) { - return FindAndCompareScheme(str.data(), static_cast<int>(str.size()), - compare, found_scheme); -} -inline bool FindAndCompareScheme(const string16& str, - const char* compare, - url_parse::Component* found_scheme) { - return FindAndCompareScheme(str.data(), static_cast<int>(str.size()), - compare, found_scheme); -} - -// Returns true if the given string represents a standard URL. This means that -// either the scheme is in the list of known standard schemes. -GURL_API bool IsStandard(const char* spec, - const url_parse::Component& scheme); -GURL_API bool IsStandard(const char16* spec, - const url_parse::Component& scheme); - -// TODO(brettw) remove this. This is a temporary compatibility hack to avoid -// breaking the WebKit build when this version is synced via Chrome. -inline bool IsStandard(const char* spec, int spec_len, - const url_parse::Component& scheme) { - return IsStandard(spec, scheme); -} - -// URL library wrappers ------------------------------------------------------- - -// Parses the given spec according to the extracted scheme type. Normal users -// should use the URL object, although this may be useful if performance is -// critical and you don't want to do the heap allocation for the std::string. -// -// As with the url_canon::Canonicalize* functions, the charset converter can -// be NULL to use UTF-8 (it will be faster in this case). -// -// Returns true if a valid URL was produced, false if not. On failure, the -// output and parsed structures will still be filled and will be consistent, -// but they will not represent a loadable URL. -GURL_API bool Canonicalize(const char* spec, - int spec_len, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed); -GURL_API bool Canonicalize(const char16* spec, - int spec_len, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed); - -// Resolves a potentially relative URL relative to the given parsed base URL. -// The base MUST be valid. The resulting canonical URL and parsed information -// will be placed in to the given out variables. -// -// The relative need not be relative. If we discover that it's absolute, this -// will produce a canonical version of that URL. See Canonicalize() for more -// about the charset_converter. -// -// Returns true if the output is valid, false if the input could not produce -// a valid URL. -GURL_API bool ResolveRelative(const char* base_spec, - int base_spec_len, - const url_parse::Parsed& base_parsed, - const char* relative, - int relative_length, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed); -GURL_API bool ResolveRelative(const char* base_spec, - int base_spec_len, - const url_parse::Parsed& base_parsed, - const char16* relative, - int relative_length, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* output_parsed); - -// Replaces components in the given VALID input url. The new canonical URL info -// is written to output and out_parsed. -// -// Returns true if the resulting URL is valid. -GURL_API bool ReplaceComponents( - const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - const url_canon::Replacements<char>& replacements, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* out_parsed); -GURL_API bool ReplaceComponents( - const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - const url_canon::Replacements<char16>& replacements, - url_canon::CharsetConverter* charset_converter, - url_canon::CanonOutput* output, - url_parse::Parsed* out_parsed); - -// String helper functions ---------------------------------------------------- - -// Compare the lower-case form of the given string against the given ASCII -// string. This is useful for doing checking if an input string matches some -// token, and it is optimized to avoid intermediate string copies. -// -// The versions of this function that don't take a b_end assume that the b -// string is NULL terminated. -GURL_API bool LowerCaseEqualsASCII(const char* a_begin, - const char* a_end, - const char* b); -GURL_API bool LowerCaseEqualsASCII(const char* a_begin, - const char* a_end, - const char* b_begin, - const char* b_end); -GURL_API bool LowerCaseEqualsASCII(const char16* a_begin, - const char16* a_end, - const char* b); - -// Unescapes the given string using URL escaping rules. -GURL_API void DecodeURLEscapeSequences(const char* input, int length, - url_canon::CanonOutputW* output); - -} // namespace url_util - -#endif // GOOGLEURL_SRC_URL_UTIL_H__
diff --git a/googleurl/src/url_util_unittest.cc b/googleurl/src/url_util_unittest.cc deleted file mode 100644 index bb04905..0000000 --- a/googleurl/src/url_util_unittest.cc +++ /dev/null
@@ -1,222 +0,0 @@ -// Copyright 2008, Google 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: -// -// * 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 Google Inc. 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. - -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_stdstring.h" -#include "googleurl/src/url_parse.h" -#include "googleurl/src/url_test_utils.h" -#include "googleurl/src/url_util.h" -#include "testing/base/public/gunit.h" - -// From googleurl/base/basictypes.h -#define ARRAYSIZE_UNSAFE(a) \ - ((sizeof(a) / sizeof(*(a))) / \ - static_cast<size_t>(!(sizeof(a) % sizeof(*(a))))) - -TEST(URLUtilTest, FindAndCompareScheme) { - url_parse::Component found_scheme; - - // Simple case where the scheme is found and matches. - const char kStr1[] = "http://www.com/"; - EXPECT_TRUE(url_util::FindAndCompareScheme( - kStr1, static_cast<int>(strlen(kStr1)), "http", NULL)); - EXPECT_TRUE(url_util::FindAndCompareScheme( - kStr1, static_cast<int>(strlen(kStr1)), "http", &found_scheme)); - EXPECT_TRUE(found_scheme == url_parse::Component(0, 4)); - - // A case where the scheme is found and doesn't match. - EXPECT_FALSE(url_util::FindAndCompareScheme( - kStr1, static_cast<int>(strlen(kStr1)), "https", &found_scheme)); - EXPECT_TRUE(found_scheme == url_parse::Component(0, 4)); - - // A case where there is no scheme. - const char kStr2[] = "httpfoobar"; - EXPECT_FALSE(url_util::FindAndCompareScheme( - kStr2, static_cast<int>(strlen(kStr2)), "http", &found_scheme)); - EXPECT_TRUE(found_scheme == url_parse::Component()); - - // When there is an empty scheme, it should match the empty scheme. - const char kStr3[] = ":foo.com/"; - EXPECT_TRUE(url_util::FindAndCompareScheme( - kStr3, static_cast<int>(strlen(kStr3)), "", &found_scheme)); - EXPECT_TRUE(found_scheme == url_parse::Component(0, 0)); - - // But when there is no scheme, it should fail. - EXPECT_FALSE(url_util::FindAndCompareScheme("", 0, "", &found_scheme)); - EXPECT_TRUE(found_scheme == url_parse::Component()); - - // When there is a whitespace char in scheme, it should canonicalize the url - // before comparison. - const char whtspc_str[] = " \r\n\tjav\ra\nscri\tpt:alert(1)"; - EXPECT_TRUE(url_util::FindAndCompareScheme( - whtspc_str, static_cast<int>(strlen(whtspc_str)), "javascript", - &found_scheme)); - EXPECT_TRUE(found_scheme == url_parse::Component(1, 10)); - - // Control characters should be stripped out on the ends, and kept in the - // middle. - const char ctrl_str[] = "\02jav\02scr\03ipt:alert(1)"; - EXPECT_FALSE(url_util::FindAndCompareScheme( - ctrl_str, static_cast<int>(strlen(ctrl_str)), "javascript", - &found_scheme)); - EXPECT_TRUE(found_scheme == url_parse::Component(1, 11)); -} - -TEST(URLUtilTest, ReplaceComponents) { - url_parse::Parsed parsed; - url_canon::RawCanonOutputT<char> output; - url_parse::Parsed new_parsed; - - // Check that the following calls do not cause crash - url_canon::Replacements<char> replacements; - replacements.SetRef("test", url_parse::Component(0, 4)); - url_util::ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, - &new_parsed); - url_util::ReplaceComponents("", 0, parsed, replacements, NULL, &output, - &new_parsed); - replacements.ClearRef(); - replacements.SetHost("test", url_parse::Component(0, 4)); - url_util::ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, - &new_parsed); - url_util::ReplaceComponents("", 0, parsed, replacements, NULL, &output, - &new_parsed); - - replacements.ClearHost(); - url_util::ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, - &new_parsed); - url_util::ReplaceComponents("", 0, parsed, replacements, NULL, &output, - &new_parsed); - url_util::ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, - &new_parsed); - url_util::ReplaceComponents("", 0, parsed, replacements, NULL, &output, - &new_parsed); -} - -static std::string CheckReplaceScheme(const char* base_url, - const char* scheme) { - // Make sure the input is canonicalized. - url_canon::RawCanonOutput<32> original; - url_parse::Parsed original_parsed; - url_util::Canonicalize(base_url, strlen(base_url), NULL, - &original, &original_parsed); - - url_canon::Replacements<char> replacements; - replacements.SetScheme(scheme, url_parse::Component(0, strlen(scheme))); - - std::string output_string; - url_canon::StdStringCanonOutput output(&output_string); - url_parse::Parsed output_parsed; - url_util::ReplaceComponents(original.data(), original.length(), - original_parsed, replacements, NULL, - &output, &output_parsed); - - output.Complete(); - return output_string; -} - -TEST(URLUtilTest, ReplaceScheme) { - EXPECT_EQ("https://google.com/", - CheckReplaceScheme("http://google.com/", "https")); - EXPECT_EQ("file://google.com/", - CheckReplaceScheme("http://google.com/", "file")); - EXPECT_EQ("http://home/Build", - CheckReplaceScheme("file:///Home/Build", "http")); - EXPECT_EQ("javascript:foo", - CheckReplaceScheme("about:foo", "javascript")); - EXPECT_EQ("://google.com/", - CheckReplaceScheme("http://google.com/", "")); - EXPECT_EQ("http://google.com/", - CheckReplaceScheme("about:google.com", "http")); - EXPECT_EQ("http:", CheckReplaceScheme("", "http")); - -#ifdef WIN32 - // Magic Windows drive letter behavior when converting to a file URL. - EXPECT_EQ("file:///E:/foo/", - CheckReplaceScheme("http://localhost/e:foo/", "file")); -#endif - - // This will probably change to "about://google.com/" when we fix - // http://crbug.com/160 which should also be an acceptable result. - EXPECT_EQ("about://google.com/", - CheckReplaceScheme("http://google.com/", "about")); -} - -TEST(URLUtilTest, DecodeURLEscapeSequences) { - struct DecodeCase { - const char* input; - const char* output; - } decode_cases[] = { - {"hello, world", "hello, world"}, - {"%01%02%03%04%05%06%07%08%09%0a%0B%0C%0D%0e%0f/", - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0B\x0C\x0D\x0e\x0f/"}, - {"%10%11%12%13%14%15%16%17%18%19%1a%1B%1C%1D%1e%1f/", - "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1B\x1C\x1D\x1e\x1f/"}, - {"%20%21%22%23%24%25%26%27%28%29%2a%2B%2C%2D%2e%2f/", - " !\"#$%&'()*+,-.//"}, - {"%30%31%32%33%34%35%36%37%38%39%3a%3B%3C%3D%3e%3f/", - "0123456789:;<=>?/"}, - {"%40%41%42%43%44%45%46%47%48%49%4a%4B%4C%4D%4e%4f/", - "@ABCDEFGHIJKLMNO/"}, - {"%50%51%52%53%54%55%56%57%58%59%5a%5B%5C%5D%5e%5f/", - "PQRSTUVWXYZ[\\]^_/"}, - {"%60%61%62%63%64%65%66%67%68%69%6a%6B%6C%6D%6e%6f/", - "`abcdefghijklmno/"}, - {"%70%71%72%73%74%75%76%77%78%79%7a%7B%7C%7D%7e%7f/", - "pqrstuvwxyz{|}~\x7f/"}, - // Test un-UTF-8-ization. - {"%e4%bd%a0%e5%a5%bd", "\xe4\xbd\xa0\xe5\xa5\xbd"}, - }; - - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(decode_cases); i++) { - const char* input = decode_cases[i].input; - url_canon::RawCanonOutputT<char16> output; - url_util::DecodeURLEscapeSequences(input, strlen(input), &output); - EXPECT_EQ(decode_cases[i].output, - url_test_utils::ConvertUTF16ToUTF8( - string16(output.data(), output.length()))); - } - - // Our decode should decode %00 - const char zero_input[] = "%00"; - url_canon::RawCanonOutputT<char16> zero_output; - url_util::DecodeURLEscapeSequences(zero_input, strlen(zero_input), - &zero_output); - EXPECT_NE("%00", - url_test_utils::ConvertUTF16ToUTF8( - string16(zero_output.data(), zero_output.length()))); - - // Test the error behavior for invalid UTF-8. - const char invalid_input[] = "%e4%a0%e5%a5%bd"; - const char16 invalid_expected[4] = {0x00e4, 0x00a0, 0x597d, 0}; - url_canon::RawCanonOutputT<char16> invalid_output; - url_util::DecodeURLEscapeSequences(invalid_input, strlen(invalid_input), - &invalid_output); - EXPECT_EQ(string16(invalid_expected), - string16(invalid_output.data(), invalid_output.length())); -}
diff --git a/googleurl/third_party/icu/build/using_icu.vsprops b/googleurl/third_party/icu/build/using_icu.vsprops deleted file mode 100644 index a3989ef..0000000 --- a/googleurl/third_party/icu/build/using_icu.vsprops +++ /dev/null
@@ -1,11 +0,0 @@ -<?xml version="1.0" encoding="Windows-1252"?> -<VisualStudioPropertySheet - ProjectType="Visual C++" - Version="8.00" - Name="using_icu" - > - <Tool - Name="VCCLCompilerTool" - AdditionalIncludeDirectories=""$(SolutionDir)..\..\third_party\icu\public\common";"$(SolutionDir)..\..\third_party\icu\public\i18n"" - /> -</VisualStudioPropertySheet>
diff --git a/src/base/strings/string16.cc b/src/base/strings/string16.cc new file mode 100644 index 0000000..2e749a3 --- /dev/null +++ b/src/base/strings/string16.cc
@@ -0,0 +1,74 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/string16.h" + +#if defined(WCHAR_T_IS_UTF16) + +#error This file should not be used on 2-byte wchar_t systems +// If this winds up being needed on 2-byte wchar_t systems, either the +// definitions below can be used, or the host system's wide character +// functions like wmemcmp can be wrapped. + +#elif defined(WCHAR_T_IS_UTF32) + +#include <ostream> + +namespace url { +namespace base { + +int c16memcmp(const char16* s1, const char16* s2, size_t n) { + // We cannot call memcmp because that changes the semantics. + while (n-- > 0) { + if (*s1 != *s2) { + // We cannot use (*s1 - *s2) because char16 is unsigned. + return ((*s1 < *s2) ? -1 : 1); + } + ++s1; + ++s2; + } + return 0; +} + +size_t c16len(const char16* s) { + const char16 *s_orig = s; + while (*s) { + ++s; + } + return s - s_orig; +} + +const char16* c16memchr(const char16* s, char16 c, size_t n) { + while (n-- > 0) { + if (*s == c) { + return s; + } + ++s; + } + return 0; +} + +char16* c16memmove(char16* s1, const char16* s2, size_t n) { + return static_cast<char16*>(memmove(s1, s2, n * sizeof(char16))); +} + +char16* c16memcpy(char16* s1, const char16* s2, size_t n) { + return static_cast<char16*>(memcpy(s1, s2, n * sizeof(char16))); +} + +char16* c16memset(char16* s, char16 c, size_t n) { + char16 *s_orig = s; + while (n-- > 0) { + *s = c; + ++s; + } + return s_orig; +} + +} // namespace base +} // namespace url + +template class std::basic_string<url::base::char16, url::base::string16_char_traits>; + +#endif // WCHAR_T_IS_UTF32
diff --git a/googleurl/base/string16.h b/src/base/strings/string16.h similarity index 74% rename from googleurl/base/string16.h rename to src/base/strings/string16.h index deedaf6..be488c3 100644 --- a/googleurl/base/string16.h +++ b/src/base/strings/string16.h
@@ -1,33 +1,9 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. -// -// 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#ifndef BASE_STRING16_H_ -#define BASE_STRING16_H_ +#ifndef BASE_STRINGS_STRING16_H_ +#define BASE_STRINGS_STRING16_H_ // WHAT: // A version of std::basic_string that provides 2-byte characters even when @@ -54,18 +30,27 @@ #include <string> #include "base/basictypes.h" +#include "build/build_config.h" -#ifdef WIN32 +#if defined(WCHAR_T_IS_UTF16) + +namespace url { +namespace base { typedef wchar_t char16; typedef std::wstring string16; +typedef std::char_traits<wchar_t> string16_char_traits; -#else // !WIN32 +} // namespace base +} // namespace url + +#elif defined(WCHAR_T_IS_UTF32) + +namespace url { +namespace base { typedef uint16 char16; -namespace base { - // char16 versions of the functions required by string16_char_traits; these // are based on the wide character functions of similar names ("w" or "wcs" // instead of "c16"). @@ -80,6 +65,10 @@ typedef char16 char_type; typedef int int_type; + // int_type needs to be able to hold each possible value of char_type, and in + // addition, the distinct value of eof(). + COMPILE_ASSERT(sizeof(int_type) > sizeof(char_type), unexpected_type_width); + typedef std::streamoff off_type; typedef mbstate_t state_type; typedef std::fpos<state_type> pos_type; @@ -141,7 +130,10 @@ } }; +typedef std::basic_string<char16, url::base::string16_char_traits> string16; + } // namespace base +} // namespace url // The string class will be explicitly instantiated only once, in string16.cc. // @@ -182,12 +174,9 @@ // // TODO(mark): File this bug with Apple and update this note with a bug number. -extern template class std::basic_string<char16, base::string16_char_traits>; +extern template +class std::basic_string<url::base::char16, url::base::string16_char_traits>; -typedef std::basic_string<char16, base::string16_char_traits> string16; +#endif // WCHAR_T_IS_UTF32 -extern std::ostream& operator<<(std::ostream& out, const string16& str); - -#endif // !WIN32 - -#endif // BASE_STRING16_H_ +#endif // BASE_STRINGS_STRING16_H_
diff --git a/src/base/strings/string_util.cc b/src/base/strings/string_util.cc new file mode 100644 index 0000000..8b2e068 --- /dev/null +++ b/src/base/strings/string_util.cc
@@ -0,0 +1,147 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/string_util.h" + +#include "base/basictypes.h" +#include "base/third_party/icu/icu_utf.h" + +static bool IsWildcard(base_icu::UChar32 character) { + return character == '*' || character == '?'; +} + +// Move the strings pointers to the point where they start to differ. +template <typename CHAR, typename NEXT> +static void EatSameChars(const CHAR** pattern, const CHAR* pattern_end, + const CHAR** string, const CHAR* string_end, + NEXT next) { + const CHAR* escape = NULL; + while (*pattern != pattern_end && *string != string_end) { + if (!escape && IsWildcard(**pattern)) { + // We don't want to match wildcard here, except if it's escaped. + return; + } + + // Check if the escapement char is found. If so, skip it and move to the + // next character. + if (!escape && **pattern == '\\') { + escape = *pattern; + next(pattern, pattern_end); + continue; + } + + // Check if the chars match, if so, increment the ptrs. + const CHAR* pattern_next = *pattern; + const CHAR* string_next = *string; + base_icu::UChar32 pattern_char = next(&pattern_next, pattern_end); + if (pattern_char == next(&string_next, string_end) && + pattern_char != CBU_SENTINEL) { + *pattern = pattern_next; + *string = string_next; + } else { + // Uh oh, it did not match, we are done. If the last char was an + // escapement, that means that it was an error to advance the ptr here, + // let's put it back where it was. This also mean that the MatchPattern + // function will return false because if we can't match an escape char + // here, then no one will. + if (escape) { + *pattern = escape; + } + return; + } + + escape = NULL; + } +} + +template <typename CHAR, typename NEXT> +static void EatWildcard(const CHAR** pattern, const CHAR* end, NEXT next) { + while (*pattern != end) { + if (!IsWildcard(**pattern)) + return; + next(pattern, end); + } +} + +template <typename CHAR, typename NEXT> +static bool MatchPatternT(const CHAR* eval, const CHAR* eval_end, + const CHAR* pattern, const CHAR* pattern_end, + int depth, + NEXT next) { + const int kMaxDepth = 16; + if (depth > kMaxDepth) + return false; + + // Eat all the matching chars. + EatSameChars(&pattern, pattern_end, &eval, eval_end, next); + + // If the string is empty, then the pattern must be empty too, or contains + // only wildcards. + if (eval == eval_end) { + EatWildcard(&pattern, pattern_end, next); + return pattern == pattern_end; + } + + // Pattern is empty but not string, this is not a match. + if (pattern == pattern_end) + return false; + + // If this is a question mark, then we need to compare the rest with + // the current string or the string with one character eaten. + const CHAR* next_pattern = pattern; + next(&next_pattern, pattern_end); + if (pattern[0] == '?') { + if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, + depth + 1, next)) + return true; + const CHAR* next_eval = eval; + next(&next_eval, eval_end); + if (MatchPatternT(next_eval, eval_end, next_pattern, pattern_end, + depth + 1, next)) + return true; + } + + // This is a *, try to match all the possible substrings with the remainder + // of the pattern. + if (pattern[0] == '*') { + // Collapse duplicate wild cards (********** into *) so that the + // method does not recurse unnecessarily. http://crbug.com/52839 + EatWildcard(&next_pattern, pattern_end, next); + + while (eval != eval_end) { + if (MatchPatternT(eval, eval_end, next_pattern, pattern_end, + depth + 1, next)) + return true; + eval++; + } + + // We reached the end of the string, let see if the pattern contains only + // wildcards. + if (eval == eval_end) { + EatWildcard(&pattern, pattern_end, next); + if (pattern != pattern_end) + return false; + return true; + } + } + + return false; +} + +struct NextCharUTF8 { + base_icu::UChar32 operator()(const char** p, const char* end) { + base_icu::UChar32 c; + int offset = 0; + CBU8_NEXT(*p, offset, end - *p, c); + *p += offset; + return c; + } +}; + +bool MatchPattern(const std::string& eval, + const std::string& pattern) { + return MatchPatternT(eval.data(), eval.data() + eval.size(), + pattern.data(), pattern.data() + pattern.size(), + 0, NextCharUTF8()); +}
diff --git a/src/base/strings/string_util.h b/src/base/strings/string_util.h new file mode 100644 index 0000000..ffc1579 --- /dev/null +++ b/src/base/strings/string_util.h
@@ -0,0 +1,20 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. +// +// This file defines utility functions for working with strings. + +#ifndef BASE_STRINGS_STRING_UTIL_H_ +#define BASE_STRINGS_STRING_UTIL_H_ + +#include "base/basictypes.h" + +// Returns true if the string passed in matches the pattern. The pattern +// string can contain wildcards like * and ? +// The backslash character (\) is an escape character for * and ? +// We limit the patterns to having a max of 16 * or ? characters. +// ? matches 0 or 1 character, while * matches 0 or more characters. +bool MatchPattern(const std::string& string, + const std::string& pattern); + +#endif // BASE_STRINGS_STRING_UTIL_H_
diff --git a/src/base/strings/utf_string_conversion_utils.cc b/src/base/strings/utf_string_conversion_utils.cc new file mode 100644 index 0000000..e71605b --- /dev/null +++ b/src/base/strings/utf_string_conversion_utils.cc
@@ -0,0 +1,150 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/strings/utf_string_conversion_utils.h" + +#include "base/third_party/icu/icu_utf.h" + +namespace url { +namespace base { + +// ReadUnicodeCharacter -------------------------------------------------------- + +bool ReadUnicodeCharacter(const char* src, + int32 src_len, + int32* char_index, + uint32* code_point_out) { + // U8_NEXT expects to be able to use -1 to signal an error, so we must + // use a signed type for code_point. But this function returns false + // on error anyway, so code_point_out is unsigned. + int32 code_point; + CBU8_NEXT(src, *char_index, src_len, code_point); + *code_point_out = static_cast<uint32>(code_point); + + // The ICU macro above moves to the next char, we want to point to the last + // char consumed. + (*char_index)--; + + // Validate the decoded value. + return IsValidCodepoint(code_point); +} + +bool ReadUnicodeCharacter(const char16* src, + int32 src_len, + int32* char_index, + uint32* code_point) { + if (CBU16_IS_SURROGATE(src[*char_index])) { + if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) || + *char_index + 1 >= src_len || + !CBU16_IS_TRAIL(src[*char_index + 1])) { + // Invalid surrogate pair. + return false; + } + + // Valid surrogate pair. + *code_point = CBU16_GET_SUPPLEMENTARY(src[*char_index], + src[*char_index + 1]); + (*char_index)++; + } else { + // Not a surrogate, just one 16-bit word. + *code_point = src[*char_index]; + } + + return IsValidCodepoint(*code_point); +} + +#if defined(WCHAR_T_IS_UTF32) +bool ReadUnicodeCharacter(const wchar_t* src, + int32 src_len, + int32* char_index, + uint32* code_point) { + // Conversion is easy since the source is 32-bit. + *code_point = src[*char_index]; + + // Validate the value. + return IsValidCodepoint(*code_point); +} +#endif // defined(WCHAR_T_IS_UTF32) + +// WriteUnicodeCharacter ------------------------------------------------------- + +size_t WriteUnicodeCharacter(uint32 code_point, std::string* output) { + if (code_point <= 0x7f) { + // Fast path the common case of one byte. + output->push_back(static_cast<char>(code_point)); + return 1; + } + + + // CBU8_APPEND_UNSAFE can append up to 4 bytes. + size_t char_offset = output->length(); + size_t original_char_offset = char_offset; + output->resize(char_offset + CBU8_MAX_LENGTH); + + CBU8_APPEND_UNSAFE(&(*output)[0], char_offset, code_point); + + // CBU8_APPEND_UNSAFE will advance our pointer past the inserted character, so + // it will represent the new length of the string. + output->resize(char_offset); + return char_offset - original_char_offset; +} + +size_t WriteUnicodeCharacter(uint32 code_point, string16* output) { + if (CBU16_LENGTH(code_point) == 1) { + // Thie code point is in the Basic Multilingual Plane (BMP). + output->push_back(static_cast<char16>(code_point)); + return 1; + } + // Non-BMP characters use a double-character encoding. + size_t char_offset = output->length(); + output->resize(char_offset + CBU16_MAX_LENGTH); + CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point); + return CBU16_MAX_LENGTH; +} + +// Generalized Unicode converter ----------------------------------------------- + +template<typename CHAR> +void PrepareForUTF8Output(const CHAR* src, + size_t src_len, + std::string* output) { + output->clear(); + if (src_len == 0) + return; + if (src[0] < 0x80) { + // Assume that the entire input will be ASCII. + output->reserve(src_len); + } else { + // Assume that the entire input is non-ASCII and will have 3 bytes per char. + output->reserve(src_len * 3); + } +} + +// Instantiate versions we know callers will need. +template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*); +template void PrepareForUTF8Output(const char16*, size_t, std::string*); + +template<typename STRING> +void PrepareForUTF16Or32Output(const char* src, + size_t src_len, + STRING* output) { + output->clear(); + if (src_len == 0) + return; + if (static_cast<unsigned char>(src[0]) < 0x80) { + // Assume the input is all ASCII, which means 1:1 correspondence. + output->reserve(src_len); + } else { + // Otherwise assume that the UTF-8 sequences will have 2 bytes for each + // character. + output->reserve(src_len / 2); + } +} + +// Instantiate versions we know callers will need. +template void PrepareForUTF16Or32Output(const char*, size_t, std::wstring*); +template void PrepareForUTF16Or32Output(const char*, size_t, string16*); + +} // namespace base +} // namespace url
diff --git a/src/base/strings/utf_string_conversion_utils.h b/src/base/strings/utf_string_conversion_utils.h new file mode 100644 index 0000000..b24f03b --- /dev/null +++ b/src/base/strings/utf_string_conversion_utils.h
@@ -0,0 +1,98 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_ +#define BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_ + +// This should only be used by the various UTF string conversion files. + +#include "base/strings/string16.h" + +namespace url { +namespace base { + +inline bool IsValidCodepoint(uint32 code_point) { + // Excludes the surrogate code points ([0xD800, 0xDFFF]) and + // codepoints larger than 0x10FFFF (the highest codepoint allowed). + // Non-characters and unassigned codepoints are allowed. + return code_point < 0xD800u || + (code_point >= 0xE000u && code_point <= 0x10FFFFu); +} + +inline bool IsValidCharacter(uint32 code_point) { + // Excludes non-characters (U+FDD0..U+FDEF, and all codepoints ending in + // 0xFFFE or 0xFFFF) from the set of valid code points. + return code_point < 0xD800u || (code_point >= 0xE000u && + code_point < 0xFDD0u) || (code_point > 0xFDEFu && + code_point <= 0x10FFFFu && (code_point & 0xFFFEu) != 0xFFFEu); +} + +// ReadUnicodeCharacter -------------------------------------------------------- + +// Reads a UTF-8 stream, placing the next code point into the given output +// |*code_point|. |src| represents the entire string to read, and |*char_index| +// is the character offset within the string to start reading at. |*char_index| +// will be updated to index the last character read, such that incrementing it +// (as in a for loop) will take the reader to the next character. +// +// Returns true on success. On false, |*code_point| will be invalid. +bool ReadUnicodeCharacter(const char* src, + int32 src_len, + int32* char_index, + uint32* code_point_out); + +// Reads a UTF-16 character. The usage is the same as the 8-bit version above. +bool ReadUnicodeCharacter(const char16* src, + int32 src_len, + int32* char_index, + uint32* code_point); + +#if defined(WCHAR_T_IS_UTF32) +// Reads UTF-32 character. The usage is the same as the 8-bit version above. +bool ReadUnicodeCharacter(const wchar_t* src, + int32 src_len, + int32* char_index, + uint32* code_point); +#endif // defined(WCHAR_T_IS_UTF32) + +// WriteUnicodeCharacter ------------------------------------------------------- + +// Appends a UTF-8 character to the given 8-bit string. Returns the number of +// bytes written. +// TODO(brettw) Bug 79631: This function should not be exposed. +size_t WriteUnicodeCharacter(uint32 code_point, + std::string* output); + +// Appends the given code point as a UTF-16 character to the given 16-bit +// string. Returns the number of 16-bit values written. +size_t WriteUnicodeCharacter(uint32 code_point, string16* output); + +#if defined(WCHAR_T_IS_UTF32) +// Appends the given UTF-32 character to the given 32-bit string. Returns the +// number of 32-bit values written. +inline size_t WriteUnicodeCharacter(uint32 code_point, std::wstring* output) { + // This is the easy case, just append the character. + output->push_back(code_point); + return 1; +} +#endif // defined(WCHAR_T_IS_UTF32) + +// Generalized Unicode converter ----------------------------------------------- + +// Guesses the length of the output in UTF-8 in bytes, clears that output +// string, and reserves that amount of space. We assume that the input +// character types are unsigned, which will be true for UTF-16 and -32 on our +// systems. +template<typename CHAR> +void PrepareForUTF8Output(const CHAR* src, size_t src_len, std::string* output); + +// Prepares an output buffer (containing either UTF-16 or -32 data) given some +// UTF-8 input that will be converted to it. See PrepareForUTF8Output(). +template<typename STRING> +void PrepareForUTF16Or32Output(const char* src, size_t src_len, STRING* output); + +} // namespace base +} // namespace url + +#endif // BASE_STRINGS_UTF_STRING_CONVERSION_UTILS_H_
diff --git a/src/base/third_party/icu/LICENSE b/src/base/third_party/icu/LICENSE new file mode 100644 index 0000000..40282f4 --- /dev/null +++ b/src/base/third_party/icu/LICENSE
@@ -0,0 +1,32 @@ +ICU License - ICU 1.8.1 and later + +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1995-2009 International Business Machines Corporation and others + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, and/or sell copies of the Software, and to permit persons +to whom the Software is furnished to do so, provided that the above +copyright notice(s) and this permission notice appear in all copies of +the Software and that both the above copyright notice(s) and this +permission notice appear in supporting documentation. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY +SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER +RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF +CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN +CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + +Except as contained in this notice, the name of a copyright holder +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +of the copyright holder.
diff --git a/src/base/third_party/icu/icu_utf.cc b/src/base/third_party/icu/icu_utf.cc new file mode 100644 index 0000000..55edce1 --- /dev/null +++ b/src/base/third_party/icu/icu_utf.cc
@@ -0,0 +1,230 @@ +/* +****************************************************************************** +* +* Copyright (C) 1999-2006, International Business Machines +* Corporation and others. All Rights Reserved. +* +****************************************************************************** +* file name: utf_impl.c +* encoding: US-ASCII +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999sep13 +* created by: Markus W. Scherer +* +* This file provides implementation functions for macros in the utfXX.h +* that would otherwise be too long as macros. +*/ + +#include "base/third_party/icu/icu_utf.h" + +namespace base_icu { + +/** + * UTF8_ERROR_VALUE_1 and UTF8_ERROR_VALUE_2 are special error values for UTF-8, + * which need 1 or 2 bytes in UTF-8: + * \code + * U+0015 = NAK = Negative Acknowledge, C0 control character + * U+009f = highest C1 control character + * \endcode + * + * These are used by UTF8_..._SAFE macros so that they can return an error value + * that needs the same number of code units (bytes) as were seen by + * a macro. They should be tested with UTF_IS_ERROR() or UTF_IS_VALID(). + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define CBUTF8_ERROR_VALUE_1 0x15 + +/** + * See documentation on UTF8_ERROR_VALUE_1 for details. + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define CBUTF8_ERROR_VALUE_2 0x9f + + +/** + * Error value for all UTFs. This code point value will be set by macros with e> + * checking if an error is detected. + * + * @deprecated ICU 2.4. Obsolete, see utf_old.h. + */ +#define CBUTF_ERROR_VALUE 0xffff + +/* + * This table could be replaced on many machines by + * a few lines of assembler code using an + * "index of first 0-bit from msb" instruction and + * one or two more integer instructions. + * + * For example, on an i386, do something like + * - MOV AL, leadByte + * - NOT AL (8-bit, leave b15..b8==0..0, reverse only b7..b0) + * - MOV AH, 0 + * - BSR BX, AX (16-bit) + * - MOV AX, 6 (result) + * - JZ finish (ZF==1 if leadByte==0xff) + * - SUB AX, BX (result) + * -finish: + * (BSR: Bit Scan Reverse, scans for a 1-bit, starting from the MSB) + * + * In Unicode, all UTF-8 byte sequences with more than 4 bytes are illegal; + * lead bytes above 0xf4 are illegal. + * We keep them in this table for skipping long ISO 10646-UTF-8 sequences. + */ +const uint8 +utf8_countTrailBytes[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, 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, 1, + + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, + 3, 3, 3, /* illegal in Unicode */ + 4, 4, 4, 4, /* illegal in Unicode */ + 5, 5, /* illegal in Unicode */ + 0, 0 /* illegal bytes 0xfe and 0xff */ +}; + +static const UChar32 +utf8_minLegal[4]={ 0, 0x80, 0x800, 0x10000 }; + +static const UChar32 +utf8_errorValue[6]={ + CBUTF8_ERROR_VALUE_1, CBUTF8_ERROR_VALUE_2, CBUTF_ERROR_VALUE, 0x10ffff, + 0x3ffffff, 0x7fffffff +}; + +/* + * Handle the non-inline part of the U8_NEXT() macro and its obsolete sibling + * UTF8_NEXT_CHAR_SAFE(). + * + * The "strict" parameter controls the error behavior: + * <0 "Safe" behavior of U8_NEXT(): All illegal byte sequences yield a negative + * code point result. + * 0 Obsolete "safe" behavior of UTF8_NEXT_CHAR_SAFE(..., FALSE): + * All illegal byte sequences yield a positive code point such that this + * result code point would be encoded with the same number of bytes as + * the illegal sequence. + * >0 Obsolete "strict" behavior of UTF8_NEXT_CHAR_SAFE(..., TRUE): + * Same as the obsolete "safe" behavior, but non-characters are also treated + * like illegal sequences. + * + * The special negative (<0) value -2 is used for lenient treatment of surrogate + * code points as legal. Some implementations use this for roundtripping of + * Unicode 16-bit strings that are not well-formed UTF-16, that is, they + * contain unpaired surrogates. + * + * Note that a UBool is the same as an int8_t. + */ +UChar32 +utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool strict) { + int32 i=*pi; + uint8 count=CBU8_COUNT_TRAIL_BYTES(c); + if((i)+count<=(length)) { + uint8 trail, illegal=0; + + CBU8_MASK_LEAD_BYTE((c), count); + /* count==0 for illegally leading trail bytes and the illegal bytes 0xfe and 0xff */ + switch(count) { + /* each branch falls through to the next one */ + case 5: + case 4: + /* count>=4 is always illegal: no more than 3 trail bytes in Unicode's UTF-8 */ + illegal=1; + break; + case 3: + trail=s[(i)++]; + (c)=((c)<<6)|(trail&0x3f); + if(c<0x110) { + illegal|=(trail&0xc0)^0x80; + } else { + /* code point>0x10ffff, outside Unicode */ + illegal=1; + break; + } + FALLTHROUGH_INTENDED; + case 2: + trail=s[(i)++]; + (c)=((c)<<6)|(trail&0x3f); + illegal|=(trail&0xc0)^0x80; + FALLTHROUGH_INTENDED; + case 1: + trail=s[(i)++]; + (c)=((c)<<6)|(trail&0x3f); + illegal|=(trail&0xc0)^0x80; + break; + case 0: + if(strict>=0) { + return CBUTF8_ERROR_VALUE_1; + } else { + return CBU_SENTINEL; + } + /* no default branch to optimize switch() - all values are covered */ + } + + /* + * All the error handling should return a value + * that needs count bytes so that UTF8_GET_CHAR_SAFE() works right. + * + * Starting with Unicode 3.0.1, non-shortest forms are illegal. + * Starting with Unicode 3.2, surrogate code points must not be + * encoded in UTF-8, and there are no irregular sequences any more. + * + * U8_ macros (new in ICU 2.4) return negative values for error conditions. + */ + + /* correct sequence - all trail bytes have (b7..b6)==(10)? */ + /* illegal is also set if count>=4 */ + if(illegal || (c)<utf8_minLegal[count] || (CBU_IS_SURROGATE(c) && strict!=-2)) { + /* error handling */ + uint8 errorCount=count; + /* don't go beyond this sequence */ + i=*pi; + while(count>0 && CBU8_IS_TRAIL(s[i])) { + ++(i); + --count; + } + if(strict>=0) { + c=utf8_errorValue[errorCount-count]; + } else { + c=CBU_SENTINEL; + } + } else if((strict)>0 && CBU_IS_UNICODE_NONCHAR(c)) { + /* strict: forbid non-characters like U+fffe */ + c=utf8_errorValue[count]; + } + } else /* too few bytes left */ { + /* error handling */ + int32 i0=i; + /* don't just set (i)=(length) in case there is an illegal sequence */ + while((i)<(length) && CBU8_IS_TRAIL(s[i])) { + ++(i); + } + if(strict>=0) { + c=utf8_errorValue[i-i0]; + } else { + c=CBU_SENTINEL; + } + } + *pi=i; + return c; +} + +} // namespace base_icu
diff --git a/src/base/third_party/icu/icu_utf.h b/src/base/third_party/icu/icu_utf.h new file mode 100644 index 0000000..2b993b0 --- /dev/null +++ b/src/base/third_party/icu/icu_utf.h
@@ -0,0 +1,391 @@ +/* +******************************************************************************* +* +* Copyright (C) 1999-2004, International Business Machines +* Corporation and others. All Rights Reserved. +* +******************************************************************************* +* file name: utf.h +* encoding: US-ASCII +* tab size: 8 (not used) +* indentation:4 +* +* created on: 1999sep09 +* created by: Markus W. Scherer +*/ + +#ifndef BASE_THIRD_PARTY_ICU_ICU_UTF_H_ +#define BASE_THIRD_PARTY_ICU_ICU_UTF_H_ + +#include "base/basictypes.h" + +namespace base_icu { + +typedef int32 UChar32; +typedef uint16 UChar; +typedef int8 UBool; + +// General --------------------------------------------------------------------- +// from utf.h + +/** + * This value is intended for sentinel values for APIs that + * (take or) return single code points (UChar32). + * It is outside of the Unicode code point range 0..0x10ffff. + * + * For example, a "done" or "error" value in a new API + * could be indicated with CBU_SENTINEL. + * + * ICU APIs designed before ICU 2.4 usually define service-specific "done" + * values, mostly 0xffff. + * Those may need to be distinguished from + * actual U+ffff text contents by calling functions like + * CharacterIterator::hasNext() or UnicodeString::length(). + * + * @return -1 + * @see UChar32 + * @stable ICU 2.4 + */ +#define CBU_SENTINEL (-1) + +/** + * Is this code point a Unicode noncharacter? + * @param c 32-bit code point + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU_IS_UNICODE_NONCHAR(c) \ + ((c)>=0xfdd0 && \ + ((uint32)(c)<=0xfdef || ((c)&0xfffe)==0xfffe) && \ + (uint32)(c)<=0x10ffff) + +/** + * Is c a Unicode code point value (0..U+10ffff) + * that can be assigned a character? + * + * Code points that are not characters include: + * - single surrogate code points (U+d800..U+dfff, 2048 code points) + * - the last two code points on each plane (U+__fffe and U+__ffff, 34 code points) + * - U+fdd0..U+fdef (new with Unicode 3.1, 32 code points) + * - the highest Unicode code point value is U+10ffff + * + * This means that all code points below U+d800 are character code points, + * and that boundary is tested first for performance. + * + * @param c 32-bit code point + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU_IS_UNICODE_CHAR(c) \ + ((uint32)(c)<0xd800 || \ + ((uint32)(c)>0xdfff && \ + (uint32)(c)<=0x10ffff && \ + !CBU_IS_UNICODE_NONCHAR(c))) + +/** + * Is this code point a surrogate (U+d800..U+dfff)? + * @param c 32-bit code point + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU_IS_SURROGATE(c) (((c)&0xfffff800)==0xd800) + +/** + * Assuming c is a surrogate code point (U_IS_SURROGATE(c)), + * is it a lead surrogate? + * @param c 32-bit code point + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU_IS_SURROGATE_LEAD(c) (((c)&0x400)==0) + + +// UTF-8 macros ---------------------------------------------------------------- +// from utf8.h + +extern const uint8 utf8_countTrailBytes[256]; + +/** + * Count the trail bytes for a UTF-8 lead byte. + * @internal + */ +#define CBU8_COUNT_TRAIL_BYTES(leadByte) (base_icu::utf8_countTrailBytes[(uint8)leadByte]) + +/** + * Mask a UTF-8 lead byte, leave only the lower bits that form part of the code point value. + * @internal + */ +#define CBU8_MASK_LEAD_BYTE(leadByte, countTrailBytes) ((leadByte)&=(1<<(6-(countTrailBytes)))-1) + +/** + * Does this code unit (byte) encode a code point by itself (US-ASCII 0..0x7f)? + * @param c 8-bit code unit (byte) + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU8_IS_SINGLE(c) (((c)&0x80)==0) + +/** + * Is this code unit (byte) a UTF-8 lead byte? + * @param c 8-bit code unit (byte) + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU8_IS_LEAD(c) ((uint8)((c)-0xc0)<0x3e) + +/** + * Is this code unit (byte) a UTF-8 trail byte? + * @param c 8-bit code unit (byte) + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU8_IS_TRAIL(c) (((c)&0xc0)==0x80) + +/** + * How many code units (bytes) are used for the UTF-8 encoding + * of this Unicode code point? + * @param c 32-bit code point + * @return 1..4, or 0 if c is a surrogate or not a Unicode code point + * @stable ICU 2.4 + */ +#define CBU8_LENGTH(c) \ + ((uint32)(c)<=0x7f ? 1 : \ + ((uint32)(c)<=0x7ff ? 2 : \ + ((uint32)(c)<=0xd7ff ? 3 : \ + ((uint32)(c)<=0xdfff || (uint32)(c)>0x10ffff ? 0 : \ + ((uint32)(c)<=0xffff ? 3 : 4)\ + ) \ + ) \ + ) \ + ) + +/** + * The maximum number of UTF-8 code units (bytes) per Unicode code point (U+0000..U+10ffff). + * @return 4 + * @stable ICU 2.4 + */ +#define CBU8_MAX_LENGTH 4 + +/** + * Function for handling "next code point" with error-checking. + * @internal + */ +UChar32 utf8_nextCharSafeBody(const uint8 *s, int32 *pi, int32 length, UChar32 c, UBool strict); + +/** + * Get a code point from a string at a code point boundary offset, + * and advance the offset to the next code point boundary. + * (Post-incrementing forward iteration.) + * "Safe" macro, checks for illegal sequences and for string boundaries. + * + * The offset may point to the lead byte of a multi-byte sequence, + * in which case the macro will read the whole sequence. + * If the offset points to a trail byte or an illegal UTF-8 sequence, then + * c is set to a negative value. + * + * @param s const uint8 * string + * @param i string offset, i<length + * @param length string length + * @param c output UChar32 variable, set to <0 in case of an error + * @see CBU8_NEXT_UNSAFE + * @stable ICU 2.4 + */ +#define CBU8_NEXT(s, i, length, c) { \ + (c)=(s)[(i)++]; \ + if(((uint8)(c))>=0x80) { \ + if(CBU8_IS_LEAD(c)) { \ + (c)=base_icu::utf8_nextCharSafeBody((const uint8 *)s, &(i), (int32)(length), c, -1); \ + } else { \ + (c)=CBU_SENTINEL; \ + } \ + } \ +} + +/** + * Append a code point to a string, overwriting 1 to 4 bytes. + * The offset points to the current end of the string contents + * and is advanced (post-increment). + * "Unsafe" macro, assumes a valid code point and sufficient space in the string. + * Otherwise, the result is undefined. + * + * @param s const uint8 * string buffer + * @param i string offset + * @param c code point to append + * @see CBU8_APPEND + * @stable ICU 2.4 + */ +#define CBU8_APPEND_UNSAFE(s, i, c) { \ + if((uint32)(c)<=0x7f) { \ + (s)[(i)++]=(uint8)(c); \ + } else { \ + if((uint32)(c)<=0x7ff) { \ + (s)[(i)++]=(uint8)(((c)>>6)|0xc0); \ + } else { \ + if((uint32)(c)<=0xffff) { \ + (s)[(i)++]=(uint8)(((c)>>12)|0xe0); \ + } else { \ + (s)[(i)++]=(uint8)(((c)>>18)|0xf0); \ + (s)[(i)++]=(uint8)((((c)>>12)&0x3f)|0x80); \ + } \ + (s)[(i)++]=(uint8)((((c)>>6)&0x3f)|0x80); \ + } \ + (s)[(i)++]=(uint8)(((c)&0x3f)|0x80); \ + } \ +} + +// UTF-16 macros --------------------------------------------------------------- +// from utf16.h + +/** + * Does this code unit alone encode a code point (BMP, not a surrogate)? + * @param c 16-bit code unit + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU16_IS_SINGLE(c) !CBU_IS_SURROGATE(c) + +/** + * Is this code unit a lead surrogate (U+d800..U+dbff)? + * @param c 16-bit code unit + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU16_IS_LEAD(c) (((c)&0xfffffc00)==0xd800) + +/** + * Is this code unit a trail surrogate (U+dc00..U+dfff)? + * @param c 16-bit code unit + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU16_IS_TRAIL(c) (((c)&0xfffffc00)==0xdc00) + +/** + * Is this code unit a surrogate (U+d800..U+dfff)? + * @param c 16-bit code unit + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU16_IS_SURROGATE(c) CBU_IS_SURROGATE(c) + +/** + * Assuming c is a surrogate code point (U16_IS_SURROGATE(c)), + * is it a lead surrogate? + * @param c 16-bit code unit + * @return TRUE or FALSE + * @stable ICU 2.4 + */ +#define CBU16_IS_SURROGATE_LEAD(c) (((c)&0x400)==0) + +/** + * Helper constant for CBU16_GET_SUPPLEMENTARY. + * @internal + */ +#define CBU16_SURROGATE_OFFSET ((0xd800<<10UL)+0xdc00-0x10000) + +/** + * Get a supplementary code point value (U+10000..U+10ffff) + * from its lead and trail surrogates. + * The result is undefined if the input values are not + * lead and trail surrogates. + * + * @param lead lead surrogate (U+d800..U+dbff) + * @param trail trail surrogate (U+dc00..U+dfff) + * @return supplementary code point (U+10000..U+10ffff) + * @stable ICU 2.4 + */ +#define CBU16_GET_SUPPLEMENTARY(lead, trail) \ + (((base_icu::UChar32)(lead)<<10UL)+(base_icu::UChar32)(trail)-CBU16_SURROGATE_OFFSET) + + +/** + * Get the lead surrogate (0xd800..0xdbff) for a + * supplementary code point (0x10000..0x10ffff). + * @param supplementary 32-bit code point (U+10000..U+10ffff) + * @return lead surrogate (U+d800..U+dbff) for supplementary + * @stable ICU 2.4 + */ +#define CBU16_LEAD(supplementary) \ + (base_icu::UChar)(((supplementary)>>10)+0xd7c0) + +/** + * Get the trail surrogate (0xdc00..0xdfff) for a + * supplementary code point (0x10000..0x10ffff). + * @param supplementary 32-bit code point (U+10000..U+10ffff) + * @return trail surrogate (U+dc00..U+dfff) for supplementary + * @stable ICU 2.4 + */ +#define CBU16_TRAIL(supplementary) \ + (base_icu::UChar)(((supplementary)&0x3ff)|0xdc00) + +/** + * How many 16-bit code units are used to encode this Unicode code point? (1 or 2) + * The result is not defined if c is not a Unicode code point (U+0000..U+10ffff). + * @param c 32-bit code point + * @return 1 or 2 + * @stable ICU 2.4 + */ +#define CBU16_LENGTH(c) ((uint32)(c)<=0xffff ? 1 : 2) + +/** + * The maximum number of 16-bit code units per Unicode code point (U+0000..U+10ffff). + * @return 2 + * @stable ICU 2.4 + */ +#define CBU16_MAX_LENGTH 2 + +/** + * Get a code point from a string at a code point boundary offset, + * and advance the offset to the next code point boundary. + * (Post-incrementing forward iteration.) + * "Safe" macro, handles unpaired surrogates and checks for string boundaries. + * + * The offset may point to the lead surrogate unit + * for a supplementary code point, in which case the macro will read + * the following trail surrogate as well. + * If the offset points to a trail surrogate or + * to a single, unpaired lead surrogate, then that itself + * will be returned as the code point. + * + * @param s const UChar * string + * @param i string offset, i<length + * @param length string length + * @param c output UChar32 variable + * @stable ICU 2.4 + */ +#define CBU16_NEXT(s, i, length, c) { \ + (c)=(s)[(i)++]; \ + if(CBU16_IS_LEAD(c)) { \ + uint16 __c2; \ + if((i)<(length) && CBU16_IS_TRAIL(__c2=(s)[(i)])) { \ + ++(i); \ + (c)=CBU16_GET_SUPPLEMENTARY((c), __c2); \ + } \ + } \ +} + +/** + * Append a code point to a string, overwriting 1 or 2 code units. + * The offset points to the current end of the string contents + * and is advanced (post-increment). + * "Unsafe" macro, assumes a valid code point and sufficient space in the string. + * Otherwise, the result is undefined. + * + * @param s const UChar * string buffer + * @param i string offset + * @param c code point to append + * @see CBU16_APPEND + * @stable ICU 2.4 + */ +#define CBU16_APPEND_UNSAFE(s, i, c) { \ + if((uint32)(c)<=0xffff) { \ + (s)[(i)++]=(uint16)(c); \ + } else { \ + (s)[(i)++]=(uint16)(((c)>>10)+0xd7c0); \ + (s)[(i)++]=(uint16)(((c)&0x3ff)|0xdc00); \ + } \ +} + +} // namesapce base_icu + +#endif // BASE_THIRD_PARTY_ICU_ICU_UTF_H_
diff --git a/src/build/build_config.h b/src/build/build_config.h new file mode 100644 index 0000000..b07660d --- /dev/null +++ b/src/build/build_config.h
@@ -0,0 +1,168 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file adds defines about the platform we're currently building on. +// Operating System: +// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) / +// OS_NACL (NACL_SFI or NACL_NONSFI) / OS_NACL_SFI / OS_NACL_NONSFI +// Compiler: +// COMPILER_MSVC / COMPILER_GCC +// Processor: +// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64) +// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS + +#ifndef BUILD_BUILD_CONFIG_H_ +#define BUILD_BUILD_CONFIG_H_ + +// A set of macros to use for platform detection. +#if defined(__native_client__) +// __native_client__ must be first, so that other OS_ defines are not set. +#define OS_NACL 1 +// OS_NACL comes in two sandboxing technology flavors, SFI or Non-SFI. +// PNaCl toolchain defines __native_client_nonsfi__ macro in Non-SFI build +// mode, while it does not in SFI build mode. +#if defined(__native_client_nonsfi__) +#define OS_NACL_NONSFI +#else +#define OS_NACL_SFI +#endif +#elif defined(ANDROID) +#define OS_ANDROID 1 +#elif defined(__APPLE__) +// only include TargetConditions after testing ANDROID as some android builds +// on mac don't have this header available and it's not needed unless the target +// is really mac/ios. +#include <TargetConditionals.h> +#define OS_MACOSX 1 +#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#define OS_IOS 1 +#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE +#elif defined(__linux__) +#define OS_LINUX 1 +// include a system header to pull in features.h for glibc/uclibc macros. +#include <unistd.h> +#if defined(__GLIBC__) && !defined(__UCLIBC__) +// we really are using glibc, not uClibc pretending to be glibc +#define LIBC_GLIBC 1 +#endif +#elif defined(_WIN32) +#define OS_WIN 1 +#define TOOLKIT_VIEWS 1 +#elif defined(__FreeBSD__) +#define OS_FREEBSD 1 +#elif defined(__OpenBSD__) +#define OS_OPENBSD 1 +#elif defined(__sun) +#define OS_SOLARIS 1 +#elif defined(__QNXNTO__) +#define OS_QNX 1 +#else +#error Please add support for your platform in build/build_config.h +#endif + +#if defined(USE_OPENSSL) && defined(USE_NSS) +#error Cannot use both OpenSSL and NSS +#endif + +// For access to standard BSD features, use OS_BSD instead of a +// more specific macro. +#if defined(OS_FREEBSD) || defined(OS_OPENBSD) +#define OS_BSD 1 +#endif + +// For access to standard POSIXish features, use OS_POSIX instead of a +// more specific macro. +#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_FREEBSD) || \ + defined(OS_OPENBSD) || defined(OS_SOLARIS) || defined(OS_ANDROID) || \ + defined(OS_NACL) || defined(OS_QNX) +#define OS_POSIX 1 +#endif + +// Use tcmalloc +#if (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)) && \ + !defined(NO_TCMALLOC) +#define USE_TCMALLOC 1 +#endif + +// Compiler detection. +#if defined(__GNUC__) +#define COMPILER_GCC 1 +#elif defined(_MSC_VER) +#define COMPILER_MSVC 1 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +// Processor architecture detection. For more info on what's defined, see: +// http://msdn.microsoft.com/en-us/library/b0084kay.aspx +// http://www.agner.org/optimize/calling_conventions.pdf +// or with gcc, run: "echo | gcc -E -dM -" +#if defined(_M_X64) || defined(__x86_64__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86_64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(_M_IX86) || defined(__i386__) +#define ARCH_CPU_X86_FAMILY 1 +#define ARCH_CPU_X86 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__ARMEL__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARMEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__aarch64__) +#define ARCH_CPU_ARM_FAMILY 1 +#define ARCH_CPU_ARM64 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__pnacl__) +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#elif defined(__MIPSEL__) +#if defined(__LP64__) +#define ARCH_CPU_MIPS64_FAMILY 1 +#define ARCH_CPU_MIPS64EL 1 +#define ARCH_CPU_64_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#else +#define ARCH_CPU_MIPS_FAMILY 1 +#define ARCH_CPU_MIPSEL 1 +#define ARCH_CPU_32_BITS 1 +#define ARCH_CPU_LITTLE_ENDIAN 1 +#endif +#else +#error Please add support for your architecture in build/build_config.h +#endif + +// Type detection for wchar_t. +#if defined(OS_WIN) +#define WCHAR_T_IS_UTF16 +#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \ + defined(__WCHAR_MAX__) && \ + (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff) +#define WCHAR_T_IS_UTF32 +#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \ + defined(__WCHAR_MAX__) && \ + (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff) +// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to +// compile in this mode (in particular, Chrome doesn't). This is intended for +// other projects using base who manage their own dependencies and make sure +// short wchar works for them. +#define WCHAR_T_IS_UTF16 +#else +#error Please add support for your compiler in build/build_config.h +#endif + +#if defined(OS_ANDROID) +// The compiler thinks std::string::const_iterator and "const char*" are +// equivalent types. +#define STD_STRING_ITERATOR_IS_CHAR_POINTER +// The compiler thinks base::string16::const_iterator and "char16*" are +// equivalent types. +#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER +#endif + +#endif // BUILD_BUILD_CONFIG_H_
diff --git a/src/url/gurl.cc b/src/url/gurl.cc new file mode 100644 index 0000000..6801dda --- /dev/null +++ b/src/url/gurl.cc
@@ -0,0 +1,549 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifdef WIN32 +#include <windows.h> +#else +#include <pthread.h> +#endif + +#include <algorithm> +#include <ostream> + +#include "url/gurl.h" + +#include "base/logging.h" +#include "url/url_canon_stdstring.h" +#include "url/url_util.h" + +namespace { + +static std::string* empty_string = NULL; +static GURL* empty_gurl = NULL; + +#ifdef WIN32 + +// Returns a static reference to an empty string for returning a reference +// when there is no underlying string. +const std::string& EmptyStringForGURL() { + // Avoid static object construction/destruction on startup/shutdown. + if (!empty_string) { + // Create the string. Be careful that we don't break in the case that this + // is being called from multiple threads. Statics are not threadsafe. + std::string* new_empty_string = new std::string; + if (InterlockedCompareExchangePointer( + reinterpret_cast<PVOID*>(&empty_string), new_empty_string, NULL)) { + // The old value was non-NULL, so no replacement was done. Another + // thread did the initialization out from under us. + delete new_empty_string; + } + } + return *empty_string; +} + +#else + +static pthread_once_t empty_string_once = PTHREAD_ONCE_INIT; +static pthread_once_t empty_gurl_once = PTHREAD_ONCE_INIT; + +void EmptyStringForGURLOnce(void) { + empty_string = new std::string; +} + +const std::string& EmptyStringForGURL() { + // Avoid static object construction/destruction on startup/shutdown. + pthread_once(&empty_string_once, EmptyStringForGURLOnce); + return *empty_string; +} + +#endif // WIN32 + +} // namespace + +GURL::GURL() : is_valid_(false) { +} + +GURL::GURL(const GURL& other) + : spec_(other.spec_), + is_valid_(other.is_valid_), + parsed_(other.parsed_) { + if (other.inner_url_) + inner_url_.reset(new GURL(*other.inner_url_)); + // Valid filesystem urls should always have an inner_url_. + DCHECK(!is_valid_ || !SchemeIsFileSystem() || inner_url_); +} + +GURL::GURL(const std::string& url_string) { + InitCanonical(url_string, true); +} + +GURL::GURL(const url::base::string16& url_string) { + InitCanonical(url_string, true); +} + +GURL::GURL(const std::string& url_string, RetainWhiteSpaceSelector) { + InitCanonical(url_string, false); +} + +GURL::GURL(const char* canonical_spec, + size_t canonical_spec_len, + const url::Parsed& parsed, + bool is_valid) + : spec_(canonical_spec, canonical_spec_len), + is_valid_(is_valid), + parsed_(parsed) { + InitializeFromCanonicalSpec(); +} + +GURL::GURL(std::string canonical_spec, const url::Parsed& parsed, bool is_valid) + : is_valid_(is_valid), + parsed_(parsed) { + spec_.swap(canonical_spec); + InitializeFromCanonicalSpec(); +} + +template<typename STR> +void GURL::InitCanonical(const STR& input_spec, bool trim_path_end) { + // Reserve enough room in the output for the input, plus some extra so that + // we have room if we have to escape a few things without reallocating. + spec_.reserve(input_spec.size() + 32); + url::StdStringCanonOutput output(&spec_); + is_valid_ = url::Canonicalize( + input_spec.data(), static_cast<int>(input_spec.length()), trim_path_end, + NULL, &output, &parsed_); + + output.Complete(); // Must be done before using string. + if (is_valid_ && SchemeIsFileSystem()) { + inner_url_.reset(new GURL(spec_.data(), parsed_.Length(), + *parsed_.inner_parsed(), true)); + } +} + +void GURL::InitializeFromCanonicalSpec() { + if (is_valid_ && SchemeIsFileSystem()) { + inner_url_.reset( + new GURL(spec_.data(), parsed_.Length(), + *parsed_.inner_parsed(), true)); + } + +#ifndef NDEBUG + // For testing purposes, check that the parsed canonical URL is identical to + // what we would have produced. Skip checking for invalid URLs have no meaning + // and we can't always canonicalize then reproducabely. + if (is_valid_) { + url::Component scheme; + // We can't do this check on the inner_url of a filesystem URL, as + // canonical_spec actually points to the start of the outer URL, so we'd + // end up with infinite recursion in this constructor. + if (!url::FindAndCompareScheme(spec_.data(), spec_.length(), + url::kFileSystemScheme, &scheme) || + scheme.begin == parsed_.scheme.begin) { + // We need to retain trailing whitespace on path URLs, as the |parsed_| + // spec we originally received may legitimately contain trailing white- + // space on the path or components e.g. if the #ref has been + // removed from a "foo:hello #ref" URL (see http://crbug.com/291747). + GURL test_url(spec_, RETAIN_TRAILING_PATH_WHITEPACE); + + DCHECK(test_url.is_valid_ == is_valid_); + DCHECK(test_url.spec_ == spec_); + + DCHECK(test_url.parsed_.scheme == parsed_.scheme); + DCHECK(test_url.parsed_.username == parsed_.username); + DCHECK(test_url.parsed_.password == parsed_.password); + DCHECK(test_url.parsed_.host == parsed_.host); + DCHECK(test_url.parsed_.port == parsed_.port); + DCHECK(test_url.parsed_.path == parsed_.path); + DCHECK(test_url.parsed_.query == parsed_.query); + DCHECK(test_url.parsed_.ref == parsed_.ref); + } + } +#endif +} + +GURL::~GURL() { +} + +GURL& GURL::operator=(GURL other) { + Swap(&other); + return *this; +} + +const std::string& GURL::spec() const { + if (is_valid_ || spec_.empty()) + return spec_; + + DCHECK(false) << "Trying to get the spec of an invalid URL!"; + return EmptyStringForGURL(); +} + +bool GURL::operator==(const GURL& other) const { + return spec_ == other.spec_; +} + +bool GURL::operator!=(const GURL& other) const { + return spec_ != other.spec_; +} + +bool GURL::operator<(const GURL& other) const { + return spec_ < other.spec_; +} + +bool GURL::operator>(const GURL& other) const { + return spec_ > other.spec_; +} + +GURL GURL::Resolve(const std::string& relative) const { + return ResolveWithCharsetConverter(relative, NULL); +} +GURL GURL::Resolve(const url::base::string16& relative) const { + return ResolveWithCharsetConverter(relative, NULL); +} + +// Note: code duplicated below (it's inconvenient to use a template here). +GURL GURL::ResolveWithCharsetConverter( + const std::string& relative, + url::CharsetConverter* charset_converter) const { + // Not allowed for invalid URLs. + if (!is_valid_) + return GURL(); + + GURL result; + + // Reserve enough room in the output for the input, plus some extra so that + // we have room if we have to escape a few things without reallocating. + result.spec_.reserve(spec_.size() + 32); + url::StdStringCanonOutput output(&result.spec_); + + if (!url::ResolveRelative(spec_.data(), static_cast<int>(spec_.length()), + parsed_, relative.data(), + static_cast<int>(relative.length()), + charset_converter, &output, &result.parsed_)) { + // Error resolving, return an empty URL. + return GURL(); + } + + output.Complete(); + result.is_valid_ = true; + if (result.SchemeIsFileSystem()) { + result.inner_url_.reset( + new GURL(result.spec_.data(), result.parsed_.Length(), + *result.parsed_.inner_parsed(), true)); + } + return result; +} + +// Note: code duplicated above (it's inconvenient to use a template here). +GURL GURL::ResolveWithCharsetConverter( + const url::base::string16& relative, + url::CharsetConverter* charset_converter) const { + // Not allowed for invalid URLs. + if (!is_valid_) + return GURL(); + + GURL result; + + // Reserve enough room in the output for the input, plus some extra so that + // we have room if we have to escape a few things without reallocating. + result.spec_.reserve(spec_.size() + 32); + url::StdStringCanonOutput output(&result.spec_); + + if (!url::ResolveRelative(spec_.data(), static_cast<int>(spec_.length()), + parsed_, relative.data(), + static_cast<int>(relative.length()), + charset_converter, &output, &result.parsed_)) { + // Error resolving, return an empty URL. + return GURL(); + } + + output.Complete(); + result.is_valid_ = true; + if (result.SchemeIsFileSystem()) { + result.inner_url_.reset( + new GURL(result.spec_.data(), result.parsed_.Length(), + *result.parsed_.inner_parsed(), true)); + } + return result; +} + +// Note: code duplicated below (it's inconvenient to use a template here). +GURL GURL::ReplaceComponents( + const url::Replacements<char>& replacements) const { + GURL result; + + // Not allowed for invalid URLs. + if (!is_valid_) + return GURL(); + + // Reserve enough room in the output for the input, plus some extra so that + // we have room if we have to escape a few things without reallocating. + result.spec_.reserve(spec_.size() + 32); + url::StdStringCanonOutput output(&result.spec_); + + result.is_valid_ = url::ReplaceComponents( + spec_.data(), static_cast<int>(spec_.length()), parsed_, replacements, + NULL, &output, &result.parsed_); + + output.Complete(); + if (result.is_valid_ && result.SchemeIsFileSystem()) { + result.inner_url_.reset(new GURL(spec_.data(), result.parsed_.Length(), + *result.parsed_.inner_parsed(), true)); + } + return result; +} + +// Note: code duplicated above (it's inconvenient to use a template here). +GURL GURL::ReplaceComponents( + const url::Replacements<url::base::char16>& replacements) const { + GURL result; + + // Not allowed for invalid URLs. + if (!is_valid_) + return GURL(); + + // Reserve enough room in the output for the input, plus some extra so that + // we have room if we have to escape a few things without reallocating. + result.spec_.reserve(spec_.size() + 32); + url::StdStringCanonOutput output(&result.spec_); + + result.is_valid_ = url::ReplaceComponents( + spec_.data(), static_cast<int>(spec_.length()), parsed_, replacements, + NULL, &output, &result.parsed_); + + output.Complete(); + if (result.is_valid_ && result.SchemeIsFileSystem()) { + result.inner_url_.reset(new GURL(spec_.data(), result.parsed_.Length(), + *result.parsed_.inner_parsed(), true)); + } + return result; +} + +GURL GURL::GetOrigin() const { + // This doesn't make sense for invalid or nonstandard URLs, so return + // the empty URL + if (!is_valid_ || !IsStandard()) + return GURL(); + + if (SchemeIsFileSystem()) + return inner_url_->GetOrigin(); + + url::Replacements<char> replacements; + replacements.ClearUsername(); + replacements.ClearPassword(); + replacements.ClearPath(); + replacements.ClearQuery(); + replacements.ClearRef(); + + return ReplaceComponents(replacements); +} + +GURL GURL::GetAsReferrer() const { + if (!is_valid_ || !SchemeIsHTTPOrHTTPS()) + return GURL(); + + if (!has_ref() && !has_username() && !has_password()) + return GURL(*this); + + url::Replacements<char> replacements; + replacements.ClearRef(); + replacements.ClearUsername(); + replacements.ClearPassword(); + return ReplaceComponents(replacements); +} + +GURL GURL::GetWithEmptyPath() const { + // This doesn't make sense for invalid or nonstandard URLs, so return + // the empty URL. + if (!is_valid_ || !IsStandard()) + return GURL(); + + // We could optimize this since we know that the URL is canonical, and we are + // appending a canonical path, so avoiding re-parsing. + GURL other(*this); + if (parsed_.path.len == 0) + return other; + + // Clear everything after the path. + other.parsed_.query.reset(); + other.parsed_.ref.reset(); + + // Set the path, since the path is longer than one, we can just set the + // first character and resize. + other.spec_[other.parsed_.path.begin] = '/'; + other.parsed_.path.len = 1; + other.spec_.resize(other.parsed_.path.begin + 1); + return other; +} + +bool GURL::IsStandard() const { + return url::IsStandard(spec_.data(), parsed_.scheme); +} + +bool GURL::SchemeIs(const char* lower_ascii_scheme) const { + if (parsed_.scheme.len <= 0) + return lower_ascii_scheme == NULL; + return url::LowerCaseEqualsASCII(spec_.data() + parsed_.scheme.begin, + spec_.data() + parsed_.scheme.end(), + lower_ascii_scheme); +} + +bool GURL::SchemeIsHTTPOrHTTPS() const { + return SchemeIs(url::kHttpScheme) || SchemeIs(url::kHttpsScheme); +} + +bool GURL::SchemeIsWSOrWSS() const { + return SchemeIs(url::kWsScheme) || SchemeIs(url::kWssScheme); +} + +int GURL::IntPort() const { + if (parsed_.port.is_nonempty()) + return url::ParsePort(spec_.data(), parsed_.port); + return url::PORT_UNSPECIFIED; +} + +int GURL::EffectiveIntPort() const { + int int_port = IntPort(); + if (int_port == url::PORT_UNSPECIFIED && IsStandard()) + return url::DefaultPortForScheme(spec_.data() + parsed_.scheme.begin, + parsed_.scheme.len); + return int_port; +} + +std::string GURL::ExtractFileName() const { + url::Component file_component; + url::ExtractFileName(spec_.data(), parsed_.path, &file_component); + return ComponentString(file_component); +} + +std::string GURL::PathForRequest() const { + DCHECK(parsed_.path.len > 0) << "Canonical path for requests should be non-empty"; + if (parsed_.ref.len >= 0) { + // Clip off the reference when it exists. The reference starts after the # + // sign, so we have to subtract one to also remove it. + return std::string(spec_, parsed_.path.begin, + parsed_.ref.begin - parsed_.path.begin - 1); + } + // Compute the actual path length, rather than depending on the spec's + // terminator. If we're an inner_url, our spec continues on into our outer + // url's path/query/ref. + int path_len = parsed_.path.len; + if (parsed_.query.is_valid()) + path_len = parsed_.query.end() - parsed_.path.begin; + + return std::string(spec_, parsed_.path.begin, path_len); +} + +std::string GURL::HostNoBrackets() const { + // If host looks like an IPv6 literal, strip the square brackets. + url::Component h(parsed_.host); + if (h.len >= 2 && spec_[h.begin] == '[' && spec_[h.end() - 1] == ']') { + h.begin++; + h.len -= 2; + } + return ComponentString(h); +} + +std::string GURL::GetContent() const { + return is_valid_ ? ComponentString(parsed_.GetContent()) : std::string(); +} + +bool GURL::HostIsIPAddress() const { + if (!is_valid_ || spec_.empty()) + return false; + + url::RawCanonOutputT<char, 128> ignored_output; + url::CanonHostInfo host_info; + url::CanonicalizeIPAddress(spec_.c_str(), parsed_.host, &ignored_output, + &host_info); + return host_info.IsIPAddress(); +} + +#ifdef WIN32 + +const GURL& GURL::EmptyGURL() { + // Avoid static object construction/destruction on startup/shutdown. + if (!empty_gurl) { + // Create the string. Be careful that we don't break in the case that this + // is being called from multiple threads. + GURL* new_empty_gurl = new GURL; + if (InterlockedCompareExchangePointer( + reinterpret_cast<PVOID*>(&empty_gurl), new_empty_gurl, NULL)) { + // The old value was non-NULL, so no replacement was done. Another + // thread did the initialization out from under us. + delete new_empty_gurl; + } + } + return *empty_gurl; +} + +#else + +void EmptyGURLOnce(void) { + empty_gurl = new GURL; +} + +const GURL& GURL::EmptyGURL() { + // Avoid static object construction/destruction on startup/shutdown. + pthread_once(&empty_gurl_once, EmptyGURLOnce); + return *empty_gurl; +} + +#endif // WIN32 + +bool GURL::DomainIs(const char* lower_ascii_domain, + int domain_len) const { + // Return false if this URL is not valid or domain is empty. + if (!is_valid_ || !domain_len) + return false; + + // FileSystem URLs have empty parsed_.host, so check this first. + if (SchemeIsFileSystem() && inner_url_) + return inner_url_->DomainIs(lower_ascii_domain, domain_len); + + if (!parsed_.host.is_nonempty()) + return false; + + // Check whether the host name is end with a dot. If yes, treat it + // the same as no-dot unless the input comparison domain is end + // with dot. + const char* last_pos = spec_.data() + parsed_.host.end() - 1; + int host_len = parsed_.host.len; + if ('.' == *last_pos && '.' != lower_ascii_domain[domain_len - 1]) { + last_pos--; + host_len--; + } + + // Return false if host's length is less than domain's length. + if (host_len < domain_len) + return false; + + // Compare this url whether belong specific domain. + const char* start_pos = spec_.data() + parsed_.host.begin + + host_len - domain_len; + + if (!url::LowerCaseEqualsASCII(start_pos, + last_pos + 1, + lower_ascii_domain, + lower_ascii_domain + domain_len)) + return false; + + // Check whether host has right domain start with dot, make sure we got + // right domain range. For example www.google.com has domain + // "google.com" but www.iamnotgoogle.com does not. + if ('.' != lower_ascii_domain[0] && host_len > domain_len && + '.' != *(start_pos - 1)) + return false; + + return true; +} + +void GURL::Swap(GURL* other) { + spec_.swap(other->spec_); + std::swap(is_valid_, other->is_valid_); + std::swap(parsed_, other->parsed_); + inner_url_.swap(other->inner_url_); +} + +std::ostream& operator<<(std::ostream& out, const GURL& url) { + return out << url.possibly_invalid_spec(); +}
diff --git a/googleurl/src/gurl.h b/src/url/gurl.h similarity index 69% rename from googleurl/src/gurl.h rename to src/url/gurl.h index 29fea81..16d9a2a 100644 --- a/googleurl/src/gurl.h +++ b/src/url/gurl.h
@@ -1,55 +1,32 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#ifndef GOOGLEURL_SRC_GURL_H__ -#define GOOGLEURL_SRC_GURL_H__ +#ifndef URL_GURL_H_ +#define URL_GURL_H_ -#include <iostream> +#include <iosfwd> +#include <memory> #include <string> -#include "base/string16.h" -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_stdstring.h" -#include "googleurl/src/url_common.h" -#include "googleurl/src/url_parse.h" +#include "base/strings/string16.h" +#include "url/url_canon.h" +#include "url/url_canon_stdstring.h" +#include "url/url_constants.h" +#include "url/url_export.h" +#include "url/url_parse.h" -class GURL { +class URL_EXPORT GURL { public: - typedef url_canon::StdStringReplacements<std::string> Replacements; - typedef url_canon::StdStringReplacements<string16> ReplacementsW; + typedef url::StdStringReplacements<std::string> Replacements; + typedef url::StdStringReplacements<url::base::string16> ReplacementsW; // Creates an empty, invalid URL. - GURL_API GURL(); + GURL(); // Copy construction is relatively inexpensive, with most of the time going // to reallocating the string. It does not re-parse. - GURL_API GURL(const GURL& other); + GURL(const GURL& other); // The narrow version requires the input be UTF-8. Invalid UTF-8 input will // result in an invalid URL. @@ -58,16 +35,27 @@ // encode the query parameters. It is probably sufficient for the narrow // version to assume the query parameter encoding should be the same as the // input encoding. - GURL_API explicit GURL(const std::string& url_string - /*, output_param_encoding*/); - GURL_API explicit GURL(const string16& url_string - /*, output_param_encoding*/); + explicit GURL(const std::string& url_string /*, output_param_encoding*/); + explicit GURL(const url::base::string16& url_string /*, output_param_encoding*/); // Constructor for URLs that have already been parsed and canonicalized. This // is used for conversions from KURL, for example. The caller must supply all // information associated with the URL, which must be correct and consistent. - GURL_API GURL(const char* canonical_spec, size_t canonical_spec_len, - const url_parse::Parsed& parsed, bool is_valid); + GURL(const char* canonical_spec, + size_t canonical_spec_len, + const url::Parsed& parsed, + bool is_valid); + // Notice that we take the canonical_spec by value so that we can convert + // from WebURL without copying the string. When we call this constructor + // we pass in a temporary std::string, which lets the compiler skip the + // copy and just move the std::string into the function argument. In the + // implementation, we use swap to move the data into the GURL itself, + // which means we end up with zero copies. + GURL(std::string canonical_spec, const url::Parsed& parsed, bool is_valid); + + ~GURL(); + + GURL& operator=(GURL other); // Returns true when this object represents a valid parsed URL. When not // valid, other functions will still succeed, but you will not get canonical @@ -99,7 +87,7 @@ // Used invalid_spec() below to get the unusable spec of an invalid URL. This // separation is designed to prevent errors that may cause security problems // that could result from the mistaken use of an invalid URL. - GURL_API const std::string& spec() const; + const std::string& spec() const; // Returns the potentially invalid spec for a the URL. This spec MUST NOT be // modified or sent over the network. It is designed to be displayed in error @@ -119,22 +107,17 @@ // or may not be valid. If you are using this to index into the spec, BE // SURE YOU ARE USING possibly_invalid_spec() to get the spec, and that you // don't do anything "important" with invalid specs. - const url_parse::Parsed& parsed_for_possibly_invalid_spec() const { + const url::Parsed& parsed_for_possibly_invalid_spec() const { return parsed_; } // Defiant equality operator! - bool operator==(const GURL& other) const { - return spec_ == other.spec_; - } - bool operator!=(const GURL& other) const { - return spec_ != other.spec_; - } + bool operator==(const GURL& other) const; + bool operator!=(const GURL& other) const; // Allows GURL to used as a key in STL (for example, a std::set or std::map). - bool operator<(const GURL& other) const { - return spec_ < other.spec_; - } + bool operator<(const GURL& other) const; + bool operator>(const GURL& other) const; // Resolves a URL that's possibly relative to this object's URL, and returns // it. Absolute URLs are also handled according to the rules of URLs on web @@ -151,8 +134,8 @@ // // It is an error to resolve a URL relative to an invalid URL. The result // will be the empty URL. - GURL_API GURL Resolve(const std::string& relative) const; - GURL_API GURL Resolve(const string16& relative) const; + GURL Resolve(const std::string& relative) const; + GURL Resolve(const url::base::string16& relative) const; // Like Resolve() above but takes a character set encoder which will be used // for any query text specified in the input. The charset converter parameter @@ -161,12 +144,12 @@ // TODO(brettw): These should be replaced with versions that take something // more friendly than a raw CharsetConverter (maybe like an ICU character set // name). - GURL_API GURL ResolveWithCharsetConverter( + GURL ResolveWithCharsetConverter( const std::string& relative, - url_canon::CharsetConverter* charset_converter) const; - GURL_API GURL ResolveWithCharsetConverter( - const string16& relative, - url_canon::CharsetConverter* charset_converter) const; + url::CharsetConverter* charset_converter) const; + GURL ResolveWithCharsetConverter( + const url::base::string16& relative, + url::CharsetConverter* charset_converter) const; // Creates a new GURL by replacing the current URL's components with the // supplied versions. See the Replacements class in url_canon.h for more. @@ -177,12 +160,11 @@ // It is an error to replace components of an invalid URL. The result will // be the empty URL. // - // Note that we use the more general url_canon::Replacements type to give + // Note that we use the more general url::Replacements type to give // callers extra flexibility rather than our override. - GURL_API GURL ReplaceComponents( - const url_canon::Replacements<char>& replacements) const; - GURL_API GURL ReplaceComponents( - const url_canon::Replacements<char16>& replacements) const; + GURL ReplaceComponents(const url::Replacements<char>& replacements) const; + GURL ReplaceComponents( + const url::Replacements<url::base::char16>& replacements) const; // A helper function that is equivalent to replacing the path with a slash // and clearing out everything after that. We sometimes need to know just the @@ -193,7 +175,7 @@ // // It is an error to get an empty path on an invalid URL. The result // will be the empty URL. - GURL_API GURL GetWithEmptyPath() const; + GURL GetWithEmptyPath() const; // A helper function to return a GURL containing just the scheme, host, // and port from a URL. Equivalent to clearing any username and password, @@ -204,35 +186,63 @@ // // It is an error to get the origin of an invalid URL. The result // will be the empty URL. - GURL_API GURL GetOrigin() const; + GURL GetOrigin() const; + + // A helper function to return a GURL stripped from the elements that are not + // supposed to be sent as HTTP referrer: username, password and ref fragment. + // For invalid URLs or URLs that no valid referrers, an empty URL will be + // returned. + GURL GetAsReferrer() const; // Returns true if the scheme for the current URL is a known "standard" // scheme. Standard schemes have an authority and a path section. This - // includes file:, which some callers may want to filter out explicitly by - // calling SchemeIsFile. - GURL_API bool IsStandard() const; + // includes file: and filesystem:, which some callers may want to filter out + // explicitly by calling SchemeIsFile[System]. + bool IsStandard() const; // Returns true if the given parameter (should be lower-case ASCII to match // the canonicalized scheme) is the scheme for this URL. This call is more // efficient than getting the scheme and comparing it because no copies or // object constructions are done. - GURL_API bool SchemeIs(const char* lower_ascii_scheme) const; + bool SchemeIs(const char* lower_ascii_scheme) const; + + // Returns true if the scheme is "http" or "https". + bool SchemeIsHTTPOrHTTPS() const; + + // Returns true is the scheme is "ws" or "wss". + bool SchemeIsWSOrWSS() const; // We often need to know if this is a file URL. File URLs are "standard", but // are often treated separately by some programs. bool SchemeIsFile() const { - return SchemeIs("file"); + return SchemeIs(url::kFileScheme); + } + + // FileSystem URLs need to be treated differently in some cases. + bool SchemeIsFileSystem() const { + return SchemeIs(url::kFileSystemScheme); } // If the scheme indicates a secure connection bool SchemeIsSecure() const { - return SchemeIs("https"); + return SchemeIs(url::kHttpsScheme) || SchemeIs(url::kWssScheme) || + (SchemeIsFileSystem() && inner_url() && inner_url()->SchemeIsSecure()); } + // Returns true if the scheme is "blob". + bool SchemeIsBlob() const { + return SchemeIs(url::kBlobScheme); + } + + // The "content" of the URL is everything after the scheme (skipping the + // scheme delimiting colon). It is an error to get the origin of an invalid + // URL. The result will be an empty string. + std::string GetContent() const; + // Returns true if the hostname is an IP address. Note: this function isn't // as cheap as a simple getter because it re-parses the hostname to verify. // This currently identifies only IPv4 addresses (bug 822685). - GURL_API bool HostIsIPAddress() const; + bool HostIsIPAddress() const; // Getters for various components of the URL. The returned string will be // empty if the component is empty or is not present. @@ -298,24 +308,24 @@ // Returns a parsed version of the port. Can also be any of the special // values defined in Parsed for ExtractPort. - GURL_API int IntPort() const; + int IntPort() const; // Returns the port number of the url, or the default port number. // If the scheme has no concept of port (or unknown default) returns // PORT_UNSPECIFIED. - GURL_API int EffectiveIntPort() const; + int EffectiveIntPort() const; // Extracts the filename portion of the path and returns it. The filename // is everything after the last slash in the path. This may be empty. - GURL_API std::string ExtractFileName() const; + std::string ExtractFileName() const; // Returns the path that should be sent to the server. This is the path, // parameter, and query portions of the URL. It is guaranteed to be ASCII. - GURL_API std::string PathForRequest() const; + std::string PathForRequest() const; // Returns the host, excluding the square brackets surrounding IPv6 address // literals. This can be useful for passing to getaddrinfo(). - GURL_API std::string HostNoBrackets() const; + std::string HostNoBrackets() const; // Returns true if this URL's host matches or is in the same domain as // the given input string. For example if this URL was "www.google.com", @@ -327,7 +337,7 @@ // // If function DomainIs has parameter domain_len, which means the parameter // lower_ascii_domain does not gurantee to terminate with NULL character. - GURL_API bool DomainIs(const char* lower_ascii_domain, int domain_len) const; + bool DomainIs(const char* lower_ascii_domain, int domain_len) const; // If function DomainIs only has parameter lower_ascii_domain, which means // domain string should be terminate with NULL character. @@ -338,16 +348,35 @@ // Swaps the contents of this GURL object with the argument without doing // any memory allocations. - GURL_API void Swap(GURL* other); + void Swap(GURL* other); // Returns a reference to a singleton empty GURL. This object is for callers // who return references but don't have anything to return in some cases. // This function may be called from any thread. - GURL_API static const GURL& EmptyGURL(); + static const GURL& EmptyGURL(); + + // Returns the inner URL of a nested URL [currently only non-null for + // filesystem: URLs]. + const GURL* inner_url() const { + return inner_url_.get(); + } private: + // Variant of the string parsing constructor that allows the caller to elect + // retain trailing whitespace, if any, on the passed URL spec but only if the + // scheme is one that allows trailing whitespace. The primary use-case is + // for data: URLs. In most cases, you want to use the single parameter + // constructor above. + enum RetainWhiteSpaceSelector { RETAIN_TRAILING_PATH_WHITEPACE }; + GURL(const std::string& url_string, RetainWhiteSpaceSelector); + + template<typename STR> + void InitCanonical(const STR& input_spec, bool trim_path_end); + + void InitializeFromCanonicalSpec(); + // Returns the substring of the input identified by the given component. - std::string ComponentString(const url_parse::Component& comp) const { + std::string ComponentString(const url::Component& comp) const { if (comp.len <= 0) return std::string(); return std::string(spec_, comp.begin, comp.len); @@ -362,14 +391,15 @@ bool is_valid_; // Identified components of the canonical spec. - url_parse::Parsed parsed_; + url::Parsed parsed_; + + // Used for nested schemes [currently only filesystem:]. + std::unique_ptr<GURL> inner_url_; // TODO bug 684583: Add encoding for query params. }; // Stream operator so GURL can be used in assertion statements. -inline std::ostream& operator<<(std::ostream& out, const GURL& url) { - return out << url.possibly_invalid_spec(); -} +URL_EXPORT std::ostream& operator<<(std::ostream& out, const GURL& url); -#endif // GOOGLEURL_SRC_GURL_H__ +#endif // URL_GURL_H_
diff --git a/src/url/gurl_unittest.cc b/src/url/gurl_unittest.cc new file mode 100644 index 0000000..112ee5f --- /dev/null +++ b/src/url/gurl_unittest.cc
@@ -0,0 +1,642 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/macros.h" +#include "testing/base/public/gunit.h" +#include "url/gurl.h" +#include "url/url_canon.h" +#include "url/url_test_utils.h" + +namespace url { + +using test_utils::WStringToUTF16; +using test_utils::ConvertUTF8ToUTF16; + +namespace { + +template<typename CHAR> +void SetupReplacement( + void (Replacements<CHAR>::*func)(const CHAR*, const Component&), + Replacements<CHAR>* replacements, + const CHAR* str) { + if (str) { + Component comp; + if (str[0]) + comp.len = static_cast<int>(strlen(str)); + (replacements->*func)(str, comp); + } +} + +// Returns the canonicalized string for the given URL string for the +// GURLTest.Types test. +std::string TypesTestCase(const char* src) { + GURL gurl(src); + return gurl.possibly_invalid_spec(); +} + +} // namespace + +// Different types of URLs should be handled differently, and handed off to +// different canonicalizers. +TEST(GURLTest, Types) { + // URLs with unknown schemes should be treated as path URLs, even when they + // have things like "://". + EXPECT_EQ("something:///HOSTNAME.com/", + TypesTestCase("something:///HOSTNAME.com/")); + + // In the reverse, known schemes should always trigger standard URL handling. + EXPECT_EQ("http://hostname.com/", TypesTestCase("http:HOSTNAME.com")); + EXPECT_EQ("http://hostname.com/", TypesTestCase("http:/HOSTNAME.com")); + EXPECT_EQ("http://hostname.com/", TypesTestCase("http://HOSTNAME.com")); + EXPECT_EQ("http://hostname.com/", TypesTestCase("http:///HOSTNAME.com")); + +#ifdef WIN32 + // URLs that look like absolute Windows drive specs. + EXPECT_EQ("file:///C:/foo.txt", TypesTestCase("c:\\foo.txt")); + EXPECT_EQ("file:///Z:/foo.txt", TypesTestCase("Z|foo.txt")); + EXPECT_EQ("file://server/foo.txt", TypesTestCase("\\\\server\\foo.txt")); + EXPECT_EQ("file://server/foo.txt", TypesTestCase("//server/foo.txt")); +#endif +} + +// Test the basic creation and querying of components in a GURL. We assume +// the parser is already tested and works, so we are mostly interested if the +// object does the right thing with the results. +TEST(GURLTest, Components) { + GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); + EXPECT_TRUE(url.is_valid()); + EXPECT_TRUE(url.SchemeIs("http")); + EXPECT_FALSE(url.SchemeIsFile()); + + // This is the narrow version of the URL, which should match the wide input. + EXPECT_EQ("http://user:pass@google.com:99/foo;bar?q=a#ref", url.spec()); + + EXPECT_EQ("http", url.scheme()); + EXPECT_EQ("user", url.username()); + EXPECT_EQ("pass", url.password()); + EXPECT_EQ("google.com", url.host()); + EXPECT_EQ("99", url.port()); + EXPECT_EQ(99, url.IntPort()); + EXPECT_EQ("/foo;bar", url.path()); + EXPECT_EQ("q=a", url.query()); + EXPECT_EQ("ref", url.ref()); + + // Test parsing userinfo with special characters. + GURL url_special_pass("http://user:%40!$&'()*+,;=:@google.com:12345"); + EXPECT_TRUE(url_special_pass.is_valid()); + // GURL canonicalizes some delimiters. + EXPECT_EQ("%40!$&%27()*+,%3B%3D%3A", url_special_pass.password()); + EXPECT_EQ("google.com", url_special_pass.host()); + EXPECT_EQ("12345", url_special_pass.port()); +} + +TEST(GURLTest, Empty) { + GURL url; + EXPECT_FALSE(url.is_valid()); + EXPECT_EQ("", url.spec()); + + EXPECT_EQ("", url.scheme()); + EXPECT_EQ("", url.username()); + EXPECT_EQ("", url.password()); + EXPECT_EQ("", url.host()); + EXPECT_EQ("", url.port()); + EXPECT_EQ(PORT_UNSPECIFIED, url.IntPort()); + EXPECT_EQ("", url.path()); + EXPECT_EQ("", url.query()); + EXPECT_EQ("", url.ref()); +} + +TEST(GURLTest, Copy) { + GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); + + GURL url2(url); + EXPECT_TRUE(url2.is_valid()); + + EXPECT_EQ("http://user:pass@google.com:99/foo;bar?q=a#ref", url2.spec()); + EXPECT_EQ("http", url2.scheme()); + EXPECT_EQ("user", url2.username()); + EXPECT_EQ("pass", url2.password()); + EXPECT_EQ("google.com", url2.host()); + EXPECT_EQ("99", url2.port()); + EXPECT_EQ(99, url2.IntPort()); + EXPECT_EQ("/foo;bar", url2.path()); + EXPECT_EQ("q=a", url2.query()); + EXPECT_EQ("ref", url2.ref()); + + // Copying of invalid URL should be invalid + GURL invalid; + GURL invalid2(invalid); + EXPECT_FALSE(invalid2.is_valid()); + EXPECT_EQ("", invalid2.spec()); + EXPECT_EQ("", invalid2.scheme()); + EXPECT_EQ("", invalid2.username()); + EXPECT_EQ("", invalid2.password()); + EXPECT_EQ("", invalid2.host()); + EXPECT_EQ("", invalid2.port()); + EXPECT_EQ(PORT_UNSPECIFIED, invalid2.IntPort()); + EXPECT_EQ("", invalid2.path()); + EXPECT_EQ("", invalid2.query()); + EXPECT_EQ("", invalid2.ref()); +} + +TEST(GURLTest, Assign) { + GURL url(WStringToUTF16(L"http://user:pass@google.com:99/foo;bar?q=a#ref")); + + GURL url2; + url2 = url; + EXPECT_TRUE(url2.is_valid()); + + EXPECT_EQ("http://user:pass@google.com:99/foo;bar?q=a#ref", url2.spec()); + EXPECT_EQ("http", url2.scheme()); + EXPECT_EQ("user", url2.username()); + EXPECT_EQ("pass", url2.password()); + EXPECT_EQ("google.com", url2.host()); + EXPECT_EQ("99", url2.port()); + EXPECT_EQ(99, url2.IntPort()); + EXPECT_EQ("/foo;bar", url2.path()); + EXPECT_EQ("q=a", url2.query()); + EXPECT_EQ("ref", url2.ref()); + + // Assignment of invalid URL should be invalid + GURL invalid; + GURL invalid2; + invalid2 = invalid; + EXPECT_FALSE(invalid2.is_valid()); + EXPECT_EQ("", invalid2.spec()); + EXPECT_EQ("", invalid2.scheme()); + EXPECT_EQ("", invalid2.username()); + EXPECT_EQ("", invalid2.password()); + EXPECT_EQ("", invalid2.host()); + EXPECT_EQ("", invalid2.port()); + EXPECT_EQ(PORT_UNSPECIFIED, invalid2.IntPort()); + EXPECT_EQ("", invalid2.path()); + EXPECT_EQ("", invalid2.query()); + EXPECT_EQ("", invalid2.ref()); +} + +// This is a regression test for http://crbug.com/309975 . +TEST(GURLTest, SelfAssign) { + GURL a("filesystem:http://example.com/temporary/"); + // This should not crash. + a = a; +} + +TEST(GURLTest, CopyFileSystem) { + GURL url(WStringToUTF16(L"filesystem:https://user:pass@google.com:99/t/foo;bar?q=a#ref")); + + GURL url2(url); + EXPECT_TRUE(url2.is_valid()); + + EXPECT_EQ("filesystem:https://user:pass@google.com:99/t/foo;bar?q=a#ref", url2.spec()); + EXPECT_EQ("filesystem", url2.scheme()); + EXPECT_EQ("", url2.username()); + EXPECT_EQ("", url2.password()); + EXPECT_EQ("", url2.host()); + EXPECT_EQ("", url2.port()); + EXPECT_EQ(PORT_UNSPECIFIED, url2.IntPort()); + EXPECT_EQ("/foo;bar", url2.path()); + EXPECT_EQ("q=a", url2.query()); + EXPECT_EQ("ref", url2.ref()); + + const GURL* inner = url2.inner_url(); + ASSERT_TRUE(inner); + EXPECT_EQ("https", inner->scheme()); + EXPECT_EQ("user", inner->username()); + EXPECT_EQ("pass", inner->password()); + EXPECT_EQ("google.com", inner->host()); + EXPECT_EQ("99", inner->port()); + EXPECT_EQ(99, inner->IntPort()); + EXPECT_EQ("/t", inner->path()); + EXPECT_EQ("", inner->query()); + EXPECT_EQ("", inner->ref()); +} + +TEST(GURLTest, IsValid) { + const char* valid_cases[] = { + "http://google.com", + "unknown://google.com", + "http://user:pass@google.com", + "http://google.com:12345", + "http://google.com/path", + "http://google.com//path", + "http://google.com?k=v#fragment", + "http://user:pass@google.com:12345/path?k=v#fragment", + "http:/path", + "http:path", + "://google.com", + }; + for (size_t i = 0; i < arraysize(valid_cases); i++) { + EXPECT_TRUE(GURL(valid_cases[i]).is_valid()) + << "Case: " << valid_cases[i]; + } + + const char* invalid_cases[] = { + "http://?k=v", + "http:://google.com", + "http//google.com", + "http://google.com:12three45", + "path", + }; + for (size_t i = 0; i < arraysize(invalid_cases); i++) { + EXPECT_FALSE(GURL(invalid_cases[i]).is_valid()) + << "Case: " << invalid_cases[i]; + } +} + +TEST(GURLTest, ExtraSlashesBeforeAuthority) { + // According to RFC3986, the hier-part for URI with an authority must use only + // two slashes, GURL intentionally just ignores slashes more than 2 and parses + // the following part as an authority. + GURL url("http:///host"); + EXPECT_EQ("host", url.host()); + EXPECT_EQ("/", url.path()); +} + +// Given an invalid URL, we should still get most of the components. +TEST(GURLTest, ComponentGettersWorkEvenForInvalidURL) { + GURL url("http:google.com:foo"); + EXPECT_FALSE(url.is_valid()); + EXPECT_EQ("http://google.com:foo/", url.possibly_invalid_spec()); + + EXPECT_EQ("http", url.scheme()); + EXPECT_EQ("", url.username()); + EXPECT_EQ("", url.password()); + EXPECT_EQ("google.com", url.host()); + EXPECT_EQ("foo", url.port()); + EXPECT_EQ(PORT_INVALID, url.IntPort()); + EXPECT_EQ("/", url.path()); + EXPECT_EQ("", url.query()); + EXPECT_EQ("", url.ref()); +} + +TEST(GURLTest, Resolve) { + // The tricky cases for relative URL resolving are tested in the + // canonicalizer unit test. Here, we just test that the GURL integration + // works properly. + struct ResolveCase { + const char* base; + const char* relative; + bool expected_valid; + const char* expected; + } resolve_cases[] = { + {"http://www.google.com/", "foo.html", true, "http://www.google.com/foo.html"}, + {"http://www.google.com/", "http://images.google.com/foo.html", true, "http://images.google.com/foo.html"}, + {"http://www.google.com/blah/bloo?c#d", "../../../hello/./world.html?a#b", true, "http://www.google.com/hello/world.html?a#b"}, + {"http://www.google.com/foo#bar", "#com", true, "http://www.google.com/foo#com"}, + {"http://www.google.com/", "Https:images.google.com", true, "https://images.google.com/"}, + // A non-standard base can be replaced with a standard absolute URL. + {"data:blahblah", "http://google.com/", true, "http://google.com/"}, + {"data:blahblah", "http:google.com", true, "http://google.com/"}, + // Filesystem URLs have different paths to test. + {"filesystem:http://www.google.com/type/", "foo.html", true, "filesystem:http://www.google.com/type/foo.html"}, + {"filesystem:http://www.google.com/type/", "../foo.html", true, "filesystem:http://www.google.com/type/foo.html"}, + }; + + for (size_t i = 0; i < arraysize(resolve_cases); i++) { + // 8-bit code path. + GURL input(resolve_cases[i].base); + GURL output = input.Resolve(resolve_cases[i].relative); + EXPECT_EQ(resolve_cases[i].expected_valid, output.is_valid()) << i; + EXPECT_EQ(resolve_cases[i].expected, output.spec()) << i; + EXPECT_EQ(output.SchemeIsFileSystem(), output.inner_url() != NULL); + + // Wide code path. + GURL inputw(ConvertUTF8ToUTF16(resolve_cases[i].base)); + GURL outputw = + input.Resolve(ConvertUTF8ToUTF16(resolve_cases[i].relative)); + EXPECT_EQ(resolve_cases[i].expected_valid, outputw.is_valid()) << i; + EXPECT_EQ(resolve_cases[i].expected, outputw.spec()) << i; + EXPECT_EQ(outputw.SchemeIsFileSystem(), outputw.inner_url() != NULL); + } +} + +TEST(GURLTest, GetOrigin) { + struct TestCase { + const char* input; + const char* expected; + } cases[] = { + {"http://www.google.com", "http://www.google.com/"}, + {"javascript:window.alert(\"hello,world\");", ""}, + {"http://user:pass@www.google.com:21/blah#baz", "http://www.google.com:21/"}, + {"http://user@www.google.com", "http://www.google.com/"}, + {"http://:pass@www.google.com", "http://www.google.com/"}, + {"http://:@www.google.com", "http://www.google.com/"}, + {"filesystem:http://www.google.com/temp/foo?q#b", "http://www.google.com/"}, + {"filesystem:http://user:pass@google.com:21/blah#baz", "http://google.com:21/"}, + }; + for (size_t i = 0; i < arraysize(cases); i++) { + GURL url(cases[i].input); + GURL origin = url.GetOrigin(); + EXPECT_EQ(cases[i].expected, origin.spec()); + } +} + +TEST(GURLTest, GetAsReferrer) { + struct TestCase { + const char* input; + const char* expected; + } cases[] = { + {"http://www.google.com", "http://www.google.com/"}, + {"http://user:pass@www.google.com:21/blah#baz", "http://www.google.com:21/blah"}, + {"http://user@www.google.com", "http://www.google.com/"}, + {"http://:pass@www.google.com", "http://www.google.com/"}, + {"http://:@www.google.com", "http://www.google.com/"}, + {"http://www.google.com/temp/foo?q#b", "http://www.google.com/temp/foo?q"}, + {"not a url", ""}, + {"unknown-scheme://foo.html", ""}, + {"file:///tmp/test.html", ""}, + {"https://www.google.com", "https://www.google.com/"}, + }; + for (size_t i = 0; i < arraysize(cases); i++) { + GURL url(cases[i].input); + GURL origin = url.GetAsReferrer(); + EXPECT_EQ(cases[i].expected, origin.spec()); + } +} + +TEST(GURLTest, GetWithEmptyPath) { + struct TestCase { + const char* input; + const char* expected; + } cases[] = { + {"http://www.google.com", "http://www.google.com/"}, + {"javascript:window.alert(\"hello, world\");", ""}, + {"http://www.google.com/foo/bar.html?baz=22", "http://www.google.com/"}, + {"filesystem:http://www.google.com/temporary/bar.html?baz=22", "filesystem:http://www.google.com/temporary/"}, + {"filesystem:file:///temporary/bar.html?baz=22", "filesystem:file:///temporary/"}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + GURL url(cases[i].input); + GURL empty_path = url.GetWithEmptyPath(); + EXPECT_EQ(cases[i].expected, empty_path.spec()); + } +} + +TEST(GURLTest, Replacements) { + // The url canonicalizer replacement test will handle most of these case. + // The most important thing to do here is to check that the proper + // canonicalizer gets called based on the scheme of the input. + struct ReplaceCase { + const char* base; + const char* scheme; + const char* username; + const char* password; + const char* host; + const char* port; + const char* path; + const char* query; + const char* ref; + const char* expected; + } replace_cases[] = { + {"http://www.google.com/foo/bar.html?foo#bar", NULL, NULL, NULL, NULL, NULL, "/", "", "", "http://www.google.com/"}, + {"http://www.google.com/foo/bar.html?foo#bar", "javascript", "", "", "", "", "window.open('foo');", "", "", "javascript:window.open('foo');"}, + {"file:///C:/foo/bar.txt", "http", NULL, NULL, "www.google.com", "99", "/foo","search", "ref", "http://www.google.com:99/foo?search#ref"}, +#ifdef WIN32 + {"http://www.google.com/foo/bar.html?foo#bar", "file", "", "", "", "", "c:\\", "", "", "file:///C:/"}, +#endif + {"filesystem:http://www.google.com/foo/bar.html?foo#bar", NULL, NULL, NULL, NULL, NULL, "/", "", "", "filesystem:http://www.google.com/foo/"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + GURL url(cur.base); + GURL::Replacements repl; + SetupReplacement(&GURL::Replacements::SetScheme, &repl, cur.scheme); + SetupReplacement(&GURL::Replacements::SetUsername, &repl, cur.username); + SetupReplacement(&GURL::Replacements::SetPassword, &repl, cur.password); + SetupReplacement(&GURL::Replacements::SetHost, &repl, cur.host); + SetupReplacement(&GURL::Replacements::SetPort, &repl, cur.port); + SetupReplacement(&GURL::Replacements::SetPath, &repl, cur.path); + SetupReplacement(&GURL::Replacements::SetQuery, &repl, cur.query); + SetupReplacement(&GURL::Replacements::SetRef, &repl, cur.ref); + GURL output = url.ReplaceComponents(repl); + + EXPECT_EQ(replace_cases[i].expected, output.spec()); + EXPECT_EQ(output.SchemeIsFileSystem(), output.inner_url() != NULL); + } +} + +TEST(GURLTest, ClearFragmentOnDataUrl) { + // http://crbug.com/291747 - a data URL may legitimately have trailing + // whitespace in the spec after the ref is cleared. Test this does not trigger + // the Parsed importing validation DCHECK in GURL. + GURL url(" data: one ? two # three "); + + // By default the trailing whitespace will have been stripped. + EXPECT_EQ("data: one ? two # three", url.spec()); + GURL::Replacements repl; + repl.ClearRef(); + GURL url_no_ref = url.ReplaceComponents(repl); + + EXPECT_EQ("data: one ? two ", url_no_ref.spec()); + + // Importing a parsed url via this constructor overload will retain trailing + // whitespace. + GURL import_url(url_no_ref.spec(), + url_no_ref.parsed_for_possibly_invalid_spec(), + url_no_ref.is_valid()); + EXPECT_EQ(url_no_ref, import_url); + EXPECT_EQ(import_url.query(), " two "); +} + +TEST(GURLTest, PathForRequest) { + struct TestCase { + const char* input; + const char* expected; + const char* inner_expected; + } cases[] = { + {"http://www.google.com", "/", NULL}, + {"http://www.google.com/", "/", NULL}, + {"http://www.google.com/foo/bar.html?baz=22", "/foo/bar.html?baz=22", NULL}, + {"http://www.google.com/foo/bar.html#ref", "/foo/bar.html", NULL}, + {"http://www.google.com/foo/bar.html?query#ref", "/foo/bar.html?query", NULL}, + {"filesystem:http://www.google.com/temporary/foo/bar.html?query#ref", "/foo/bar.html?query", "/temporary"}, + {"filesystem:http://www.google.com/temporary/foo/bar.html?query", "/foo/bar.html?query", "/temporary"}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + GURL url(cases[i].input); + std::string path_request = url.PathForRequest(); + EXPECT_EQ(cases[i].expected, path_request); + EXPECT_EQ(cases[i].inner_expected == NULL, url.inner_url() == NULL); + if (url.inner_url() && cases[i].inner_expected) + EXPECT_EQ(cases[i].inner_expected, url.inner_url()->PathForRequest()); + } +} + +TEST(GURLTest, EffectiveIntPort) { + struct PortTest { + const char* spec; + int expected_int_port; + } port_tests[] = { + // http + {"http://www.google.com/", 80}, + {"http://www.google.com:80/", 80}, + {"http://www.google.com:443/", 443}, + + // https + {"https://www.google.com/", 443}, + {"https://www.google.com:443/", 443}, + {"https://www.google.com:80/", 80}, + + // ftp + {"ftp://www.google.com/", 21}, + {"ftp://www.google.com:21/", 21}, + {"ftp://www.google.com:80/", 80}, + + // gopher + {"gopher://www.google.com/", 70}, + {"gopher://www.google.com:70/", 70}, + {"gopher://www.google.com:80/", 80}, + + // file - no port + {"file://www.google.com/", PORT_UNSPECIFIED}, + {"file://www.google.com:443/", PORT_UNSPECIFIED}, + + // data - no port + {"data:www.google.com:90", PORT_UNSPECIFIED}, + {"data:www.google.com", PORT_UNSPECIFIED}, + + // filesystem - no port + {"filesystem:http://www.google.com:90/t/foo", PORT_UNSPECIFIED}, + {"filesystem:file:///t/foo", PORT_UNSPECIFIED}, + }; + + for (size_t i = 0; i < arraysize(port_tests); i++) { + GURL url(port_tests[i].spec); + EXPECT_EQ(port_tests[i].expected_int_port, url.EffectiveIntPort()); + } +} + +TEST(GURLTest, IPAddress) { + struct IPTest { + const char* spec; + bool expected_ip; + } ip_tests[] = { + {"http://www.google.com/", false}, + {"http://192.168.9.1/", true}, + {"http://192.168.9.1.2/", false}, + {"http://192.168.m.1/", false}, + {"http://2001:db8::1/", false}, + {"http://[2001:db8::1]/", true}, + {"", false}, + {"some random input!", false}, + }; + + for (size_t i = 0; i < arraysize(ip_tests); i++) { + GURL url(ip_tests[i].spec); + EXPECT_EQ(ip_tests[i].expected_ip, url.HostIsIPAddress()); + } +} + +TEST(GURLTest, HostNoBrackets) { + struct TestCase { + const char* input; + const char* expected_host; + const char* expected_plainhost; + } cases[] = { + {"http://www.google.com", "www.google.com", "www.google.com"}, + {"http://[2001:db8::1]/", "[2001:db8::1]", "2001:db8::1"}, + {"http://[::]/", "[::]", "::"}, + + // Don't require a valid URL, but don't crash either. + {"http://[]/", "[]", ""}, + {"http://[x]/", "[x]", "x"}, + {"http://[x/", "[x", "[x"}, + {"http://x]/", "x]", "x]"}, + {"http://[/", "[", "["}, + {"http://]/", "]", "]"}, + {"", "", ""}, + }; + for (size_t i = 0; i < arraysize(cases); i++) { + GURL url(cases[i].input); + EXPECT_EQ(cases[i].expected_host, url.host()); + EXPECT_EQ(cases[i].expected_plainhost, url.HostNoBrackets()); + } +} + +TEST(GURLTest, DomainIs) { + const char google_domain[] = "google.com"; + + GURL url_1("http://www.google.com:99/foo"); + EXPECT_TRUE(url_1.DomainIs(google_domain)); + + GURL url_2("http://google.com:99/foo"); + EXPECT_TRUE(url_2.DomainIs(google_domain)); + + GURL url_3("http://google.com./foo"); + EXPECT_TRUE(url_3.DomainIs(google_domain)); + + GURL url_4("http://google.com/foo"); + EXPECT_FALSE(url_4.DomainIs("google.com.")); + + GURL url_5("http://google.com./foo"); + EXPECT_TRUE(url_5.DomainIs("google.com.")); + + GURL url_6("http://www.google.com./foo"); + EXPECT_TRUE(url_6.DomainIs(".com.")); + + GURL url_7("http://www.balabala.com/foo"); + EXPECT_FALSE(url_7.DomainIs(google_domain)); + + GURL url_8("http://www.google.com.cn/foo"); + EXPECT_FALSE(url_8.DomainIs(google_domain)); + + GURL url_9("http://www.iamnotgoogle.com/foo"); + EXPECT_FALSE(url_9.DomainIs(google_domain)); + + GURL url_10("http://www.iamnotgoogle.com../foo"); + EXPECT_FALSE(url_10.DomainIs(".com")); + + GURL url_11("filesystem:http://www.google.com:99/foo/"); + EXPECT_TRUE(url_11.DomainIs(google_domain)); + + GURL url_12("filesystem:http://www.iamnotgoogle.com/foo/"); + EXPECT_FALSE(url_12.DomainIs(google_domain)); +} + +// Newlines should be stripped from inputs. +TEST(GURLTest, Newlines) { + // Constructor. + GURL url_1(" \t ht\ntp://\twww.goo\rgle.com/as\ndf \n "); + EXPECT_EQ("http://www.google.com/asdf", url_1.spec()); + + // Relative path resolver. + GURL url_2 = url_1.Resolve(" \n /fo\to\r "); + EXPECT_EQ("http://www.google.com/foo", url_2.spec()); + + // Note that newlines are NOT stripped from ReplaceComponents. +} + +TEST(GURLTest, IsStandard) { + GURL a("http:foo/bar"); + EXPECT_TRUE(a.IsStandard()); + + GURL b("foo:bar/baz"); + EXPECT_FALSE(b.IsStandard()); + + GURL c("foo://bar/baz"); + EXPECT_FALSE(c.IsStandard()); +} + +TEST(GURLTest, SchemeIsHTTPOrHTTPS) { + EXPECT_TRUE(GURL("http://bar/").SchemeIsHTTPOrHTTPS()); + EXPECT_TRUE(GURL("HTTPS://BAR").SchemeIsHTTPOrHTTPS()); + EXPECT_FALSE(GURL("ftp://bar/").SchemeIsHTTPOrHTTPS()); +} + +TEST(GURLTest, SchemeIsWSOrWSS) { + EXPECT_TRUE(GURL("WS://BAR/").SchemeIsWSOrWSS()); + EXPECT_TRUE(GURL("wss://bar/").SchemeIsWSOrWSS()); + EXPECT_FALSE(GURL("http://bar/").SchemeIsWSOrWSS()); +} + +TEST(GURLTest, SchemeIsBlob) { + EXPECT_TRUE(GURL("BLOB://BAR/").SchemeIsBlob()); + EXPECT_TRUE(GURL("blob://bar/").SchemeIsBlob()); + EXPECT_FALSE(GURL("http://bar/").SchemeIsBlob()); +} + +} // namespace url
diff --git a/src/url/origin.cc b/src/url/origin.cc new file mode 100644 index 0000000..fdb8913 --- /dev/null +++ b/src/url/origin.cc
@@ -0,0 +1,20 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "url/origin.h" + +#include "base/logging.h" +#include "base/strings/string_util.h" + +namespace url { + +Origin::Origin() : string_("null") {} + +Origin::Origin(const std::string& origin) : string_(origin) { + DCHECK(origin == "null" || MatchPattern(origin, "?*://?*")); + DCHECK_GT(origin.size(), 0u); + DCHECK(origin == "file://" || origin[origin.size() - 1] != '/'); +} + +} // namespace url
diff --git a/src/url/origin.h b/src/url/origin.h new file mode 100644 index 0000000..777e4e1 --- /dev/null +++ b/src/url/origin.h
@@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_ORIGIN_H_ +#define URL_ORIGIN_H_ + +#include <string> + +#include "url/url_export.h" + +namespace url { + +// Origin represents a Web Origin serialized to a string. +// See RFC6454 for details. +class URL_EXPORT Origin { + public: + Origin(); + explicit Origin(const std::string& origin); + + const std::string& string() const { return string_; } + + bool IsSameAs(const Origin& that) const { + return string_ == that.string_; + } + + private: + std::string string_; +}; + +} // namespace url + +#endif // URL_ORIGIN_H_
diff --git a/src/url/origin_unittest.cc b/src/url/origin_unittest.cc new file mode 100644 index 0000000..910a1cf --- /dev/null +++ b/src/url/origin_unittest.cc
@@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "testing/base/public/gunit.h" +#include "url/origin.h" + +namespace url { + +namespace { + +// Each test examines the Origin is constructed correctly without +// violating DCHECKs. +TEST(OriginTest, constructEmpty) { + Origin origin; + EXPECT_EQ("null", origin.string()); +} + +TEST(OriginTest, constructNull) { + Origin origin("null"); + EXPECT_EQ("null", origin.string()); +} + +TEST(OriginTest, constructValidOrigin) { + Origin origin("http://example.com:8080"); + EXPECT_EQ("http://example.com:8080", origin.string()); +} + +TEST(OriginTest, constructValidFileOrigin) { + Origin origin("file://"); + EXPECT_EQ("file://", origin.string()); +} + +TEST(OriginTest, constructValidOriginWithoutPort) { + Origin origin("wss://example2.com"); + EXPECT_EQ("wss://example2.com", origin.string()); +} + +} // namespace + +} // namespace url
diff --git a/googleurl/src/url_parse.cc b/src/url/third_party/mozilla/url_parse.cc similarity index 73% rename from googleurl/src/url_parse.cc rename to src/url/third_party/mozilla/url_parse.cc index a08c4da..211043c 100644 --- a/googleurl/src/url_parse.cc +++ b/src/url/third_party/mozilla/url_parse.cc
@@ -34,19 +34,21 @@ * * ***** END LICENSE BLOCK ***** */ -#include "googleurl/src/url_parse.h" +#include "url/third_party/mozilla/url_parse.h" #include <stdlib.h> #include "base/logging.h" -#include "googleurl/src/url_parse_internal.h" +#include "url/url_parse_internal.h" +#include "url/url_util.h" +#include "url/url_util_internal.h" -namespace url_parse { +namespace url { namespace { // Returns true if the given character is a valid digit to use in a port. -inline bool IsPortDigit(char16 ch) { +inline bool IsPortDigit(base::char16 ch) { return ch >= '0' && ch <= '9'; } @@ -324,7 +326,7 @@ if (DoExtractScheme(spec, spec_len, &parsed->scheme)) { after_scheme = parsed->scheme.end() + 1; // Skip past the colon. } else { - // Say there's no scheme when there is a colon. We could also say that + // Say there's no scheme when there is no colon. We could also say that // everything is the scheme. Both would produce an invalid URL, but this way // seems less wrong in more cases. parsed->scheme.reset(); @@ -333,18 +335,20 @@ DoParseAfterScheme(spec, spec_len, after_scheme, parsed); } -// Initializes a path URL which is merely a scheme followed by a path. Examples -// include "about:foo" and "javascript:alert('bar');" +#ifndef NO_FILESYSTEMURL_SUPPORT template<typename CHAR> -void DoParsePathURL(const CHAR* spec, int spec_len, Parsed* parsed) { - // Get the non-path and non-scheme parts of the URL out of the way, we never - // use them. +void DoParseFileSystemURL(const CHAR* spec, int spec_len, Parsed* parsed) { + DCHECK(spec_len >= 0); + + // Get the unused parts of the URL out of the way. parsed->username.reset(); parsed->password.reset(); parsed->host.reset(); parsed->port.reset(); - parsed->query.reset(); - parsed->ref.reset(); + parsed->path.reset(); // May use this; reset for convenience. + parsed->ref.reset(); // May use this; reset for convenience. + parsed->query.reset(); // May use this; reset for convenience. + parsed->clear_inner_parsed(); // May use this; reset for convenience. // Strip leading & trailing spaces and control characters. int begin = 0; @@ -353,28 +357,151 @@ // Handle empty specs or ones that contain only whitespace or control chars. if (begin == spec_len) { parsed->scheme.reset(); + return; + } + + int inner_start = -1; + + // Extract the scheme. We also handle the case where there is no scheme. + if (DoExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) { + // Offset the results since we gave ExtractScheme a substring. + parsed->scheme.begin += begin; + + if (parsed->scheme.end() == spec_len - 1) + return; + + inner_start = parsed->scheme.end() + 1; + } else { + // No scheme found; that's not valid for filesystem URLs. + parsed->scheme.reset(); + return; + } + + Component inner_scheme; + const CHAR* inner_spec = &spec[inner_start]; + int inner_spec_len = spec_len - inner_start; + + if (DoExtractScheme(inner_spec, inner_spec_len, &inner_scheme)) { + // Offset the results since we gave ExtractScheme a substring. + inner_scheme.begin += inner_start; + + if (inner_scheme.end() == spec_len - 1) + return; + } else { + // No scheme found; that's not valid for filesystem URLs. + // The best we can do is return "filesystem://". + return; + } + + Parsed inner_parsed; + + if (CompareSchemeComponent(spec, inner_scheme, kFileScheme)) { + // File URLs are special. + ParseFileURL(inner_spec, inner_spec_len, &inner_parsed); + } else if (CompareSchemeComponent(spec, inner_scheme, kFileSystemScheme)) { + // Filesystem URLs don't nest. + return; + } else if (IsStandard(spec, inner_scheme)) { + // All "normal" URLs. + DoParseStandardURL(inner_spec, inner_spec_len, &inner_parsed); + } else { + return; + } + + // All members of inner_parsed need to be offset by inner_start. + // If we had any scheme that supported nesting more than one level deep, + // we'd have to recurse into the inner_parsed's inner_parsed when + // adjusting by inner_start. + inner_parsed.scheme.begin += inner_start; + inner_parsed.username.begin += inner_start; + inner_parsed.password.begin += inner_start; + inner_parsed.host.begin += inner_start; + inner_parsed.port.begin += inner_start; + inner_parsed.query.begin += inner_start; + inner_parsed.ref.begin += inner_start; + inner_parsed.path.begin += inner_start; + + // Query and ref move from inner_parsed to parsed. + parsed->query = inner_parsed.query; + inner_parsed.query.reset(); + parsed->ref = inner_parsed.ref; + inner_parsed.ref.reset(); + + parsed->set_inner_parsed(inner_parsed); + if (!inner_parsed.scheme.is_valid() || !inner_parsed.path.is_valid() || + inner_parsed.inner_parsed()) { + return; + } + + // The path in inner_parsed should start with a slash, then have a filesystem + // type followed by a slash. From the first slash up to but excluding the + // second should be what it keeps; the rest goes to parsed. If the path ends + // before the second slash, it's still pretty clear what the user meant, so + // we'll let that through. + if (!IsURLSlash(spec[inner_parsed.path.begin])) { + return; + } + int inner_path_end = inner_parsed.path.begin + 1; // skip the leading slash + while (inner_path_end < spec_len && + !IsURLSlash(spec[inner_path_end])) + ++inner_path_end; + parsed->path.begin = inner_path_end; + int new_inner_path_length = inner_path_end - inner_parsed.path.begin; + parsed->path.len = inner_parsed.path.len - new_inner_path_length; + parsed->inner_parsed()->path.len = new_inner_path_length; +} +#endif + +// Initializes a path URL which is merely a scheme followed by a path. Examples +// include "about:foo" and "javascript:alert('bar');" +template<typename CHAR> +void DoParsePathURL(const CHAR* spec, int spec_len, + bool trim_path_end, + Parsed* parsed) { + // Get the non-path and non-scheme parts of the URL out of the way, we never + // use them. + parsed->username.reset(); + parsed->password.reset(); + parsed->host.reset(); + parsed->port.reset(); + parsed->path.reset(); + parsed->query.reset(); + parsed->ref.reset(); + + // Strip leading & trailing spaces and control characters. + int scheme_begin = 0; + TrimURL(spec, &scheme_begin, &spec_len, trim_path_end); + + // Handle empty specs or ones that contain only whitespace or control chars. + if (scheme_begin == spec_len) { + parsed->scheme.reset(); parsed->path.reset(); return; } + int path_begin; // Extract the scheme, with the path being everything following. We also // handle the case where there is no scheme. - if (ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) { + if (ExtractScheme(&spec[scheme_begin], spec_len - scheme_begin, + &parsed->scheme)) { // Offset the results since we gave ExtractScheme a substring. - parsed->scheme.begin += begin; - - // For compatability with the standard URL parser, we treat no path as - // -1, rather than having a length of 0 (we normally wouldn't care so - // much for these non-standard URLs). - if (parsed->scheme.end() == spec_len - 1) - parsed->path.reset(); - else - parsed->path = MakeRange(parsed->scheme.end() + 1, spec_len); + parsed->scheme.begin += scheme_begin; + path_begin = parsed->scheme.end() + 1; } else { - // No scheme found, just path. + // No scheme case. parsed->scheme.reset(); - parsed->path = MakeRange(begin, spec_len); + path_begin = scheme_begin; } + + if (path_begin == spec_len) + return; + DCHECK_LT(path_begin, spec_len); + + ParsePath(spec, + MakeRange(path_begin, spec_len), + &parsed->path, + &parsed->query, + &parsed->ref); } template<typename CHAR> @@ -496,23 +623,13 @@ return; } - // Search backwards for a parameter, which is a normally unused field in a - // URL delimited by a semicolon. We parse the parameter as part of the - // path, but here, we don't want to count it. The last semicolon is the - // parameter. The path should start with a slash, so we don't need to check - // the first one. + // Extract the filename range from the path which is between + // the last slash and the following semicolon. int file_end = path.end(); - for (int i = path.end() - 1; i > path.begin; i--) { + for (int i = path.end() - 1; i >= path.begin; i--) { if (spec[i] == ';') { file_end = i; - break; - } - } - - // Now search backwards from the filename end to the previous slash - // to find the beginning of the filename. - for (int i = file_end - 1; i >= path.begin; i--) { - if (IsURLSlash(spec[i])) { + } else if (IsURLSlash(spec[i])) { // File name is everything following this character to the end *file_name = MakeRange(i + 1, file_end); return; @@ -559,12 +676,51 @@ cur++; // Save the new query - *query = url_parse::MakeRange(cur, end); + *query = MakeRange(cur, end); return true; } } // namespace +Parsed::Parsed() : inner_parsed_(NULL) { +} + +Parsed::Parsed(const Parsed& other) : + scheme(other.scheme), + username(other.username), + password(other.password), + host(other.host), + port(other.port), + path(other.path), + query(other.query), + ref(other.ref), + inner_parsed_(NULL) { + if (other.inner_parsed_) + set_inner_parsed(*other.inner_parsed_); +} + +Parsed& Parsed::operator=(const Parsed& other) { + if (this != &other) { + scheme = other.scheme; + username = other.username; + password = other.password; + host = other.host; + port = other.port; + path = other.path; + query = other.query; + ref = other.ref; + if (other.inner_parsed_) + set_inner_parsed(*other.inner_parsed_); + else + clear_inner_parsed(); + } + return *this; +} + +Parsed::~Parsed() { + delete inner_parsed_; +} + int Parsed::Length() const { if (ref.is_valid()) return ref.end(); @@ -634,18 +790,27 @@ return cur; } +Component Parsed::GetContent() const { + const int begin = CountCharactersBefore(USERNAME, false); + const int len = Length() - begin; + // For compatability with the standard URL parser, we treat no content as + // -1, rather than having a length of 0 (we normally wouldn't care so + // much for these non-standard URLs). + return len ? Component(begin, len) : Component(); +} + bool ExtractScheme(const char* url, int url_len, Component* scheme) { return DoExtractScheme(url, url_len, scheme); } -bool ExtractScheme(const char16* url, int url_len, Component* scheme) { +bool ExtractScheme(const base::char16* url, int url_len, Component* scheme) { return DoExtractScheme(url, url_len, scheme); } // This handles everything that may be an authority terminator, including // backslash. For special backslash handling see DoParseAfterScheme. -bool IsAuthorityTerminator(char16 ch) { - return IsURLSlash(ch) || ch == '?' || ch == '#' || ch == ';'; +bool IsAuthorityTerminator(base::char16 ch) { + return IsURLSlash(ch) || ch == '?' || ch == '#'; } void ExtractFileName(const char* url, @@ -654,7 +819,7 @@ DoExtractFileName(url, path, file_name); } -void ExtractFileName(const char16* url, +void ExtractFileName(const base::char16* url, const Component& path, Component* file_name) { DoExtractFileName(url, path, file_name); @@ -667,7 +832,7 @@ return DoExtractQueryKeyValue(url, query, key, value); } -bool ExtractQueryKeyValue(const char16* url, +bool ExtractQueryKeyValue(const base::char16* url, Component* query, Component* key, Component* value) { @@ -683,7 +848,7 @@ DoParseAuthority(spec, auth, username, password, hostname, port_num); } -void ParseAuthority(const char16* spec, +void ParseAuthority(const base::char16* spec, const Component& auth, Component* username, Component* password, @@ -696,7 +861,7 @@ return DoParsePort(url, port); } -int ParsePort(const char16* url, const Component& port) { +int ParsePort(const base::char16* url, const Component& port) { return DoParsePort(url, port); } @@ -704,23 +869,49 @@ DoParseStandardURL(url, url_len, parsed); } -void ParseStandardURL(const char16* url, int url_len, Parsed* parsed) { +void ParseStandardURL(const base::char16* url, int url_len, Parsed* parsed) { DoParseStandardURL(url, url_len, parsed); } -void ParsePathURL(const char* url, int url_len, Parsed* parsed) { - DoParsePathURL(url, url_len, parsed); +void ParsePathURL(const char* url, + int url_len, + bool trim_path_end, + Parsed* parsed) { + DoParsePathURL(url, url_len, trim_path_end, parsed); } -void ParsePathURL(const char16* url, int url_len, Parsed* parsed) { - DoParsePathURL(url, url_len, parsed); +void ParsePathURL(const base::char16* url, + int url_len, + bool trim_path_end, + Parsed* parsed) { + DoParsePathURL(url, url_len, trim_path_end, parsed); +} + +void ParseFileSystemURL(const char* url, int url_len, Parsed* parsed) { +#ifndef NO_FILESYSTEMURL_SUPPORT + DoParseFileSystemURL(url, url_len, parsed); +#else + // Should not reach here if the client doesn't want to support file system + // URL. + DCHECK(false); +#endif +} + +void ParseFileSystemURL(const base::char16* url, int url_len, Parsed* parsed) { +#ifndef NO_FILESYSTEMURL_SUPPORT + DoParseFileSystemURL(url, url_len, parsed); +#else + // Should not reach here if the client doesn't want to support file system + // URL. + DCHECK(false); +#endif } void ParseMailtoURL(const char* url, int url_len, Parsed* parsed) { DoParseMailtoURL(url, url_len, parsed); } -void ParseMailtoURL(const char16* url, int url_len, Parsed* parsed) { +void ParseMailtoURL(const base::char16* url, int url_len, Parsed* parsed) { DoParseMailtoURL(url, url_len, parsed); } @@ -732,7 +923,7 @@ ParsePath(spec, path, filepath, query, ref); } -void ParsePathInternal(const char16* spec, +void ParsePathInternal(const base::char16* spec, const Component& path, Component* filepath, Component* query, @@ -747,11 +938,11 @@ DoParseAfterScheme(spec, spec_len, after_scheme, parsed); } -void ParseAfterScheme(const char16* spec, +void ParseAfterScheme(const base::char16* spec, int spec_len, int after_scheme, Parsed* parsed) { DoParseAfterScheme(spec, spec_len, after_scheme, parsed); } -} // namespace url_parse +} // namespace url
diff --git a/googleurl/src/url_parse.h b/src/url/third_party/mozilla/url_parse.h similarity index 61% rename from googleurl/src/url_parse.h rename to src/url/third_party/mozilla/url_parse.h index 134b445..71dbb78 100644 --- a/googleurl/src/url_parse.h +++ b/src/url/third_party/mozilla/url_parse.h
@@ -1,46 +1,21 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#ifndef GOOGLEURL_SRC_URL_PARSE_H__ -#define GOOGLEURL_SRC_URL_PARSE_H__ +#ifndef URL_THIRD_PARTY_MOZILLA_URL_PARSE_H_ +#define URL_THIRD_PARTY_MOZILLA_URL_PARSE_H_ #include <string> #include "base/basictypes.h" -#include "base/string16.h" -#include "googleurl/src/url_common.h" +#include "base/strings/string16.h" +#include "url/url_export.h" -namespace url_parse { +namespace url { // Deprecated, but WebKit/WebCore/platform/KURLGooglePrivate.h and // KURLGoogle.cpp still rely on this type. -typedef char16 UTF16Char; +typedef base::char16 UTF16Char; // Component ------------------------------------------------------------------ @@ -94,19 +69,19 @@ // // Typical usage would be: // -// url_parse::Parsed parsed; -// url_parse::Component scheme; -// if (!url_parse::ExtractScheme(url, url_len, &scheme)) +// Parsed parsed; +// Component scheme; +// if (!ExtractScheme(url, url_len, &scheme)) // return I_CAN_NOT_FIND_THE_SCHEME_DUDE; // // if (IsStandardScheme(url, scheme)) // Not provided by this component -// url_parseParseStandardURL(url, url_len, &parsed); +// ParseStandardURL(url, url_len, &parsed); // else if (IsFileURL(url, scheme)) // Not provided by this component -// url_parse::ParseFileURL(url, url_len, &parsed); +// ParseFileURL(url, url_len, &parsed); // else -// url_parse::ParsePathURL(url, url_len, &parsed); +// ParsePathURL(url, url_len, &parsed); // -struct Parsed { +struct URL_EXPORT Parsed { // Identifies different components. enum ComponentType { SCHEME, @@ -119,8 +94,12 @@ REF, }; - // The default constructor is sufficient for the components. - Parsed() {} + // The default constructor is sufficient for the components, but inner_parsed_ + // requires special handling. + Parsed(); + Parsed(const Parsed&); + Parsed& operator=(const Parsed&); + ~Parsed(); // Returns the length of the URL (the end of the last component). // @@ -128,7 +107,7 @@ // of the string. For example "http://": the parsed structure will only // contain an entry for the four-character scheme, and it doesn't know about // the "://". For all other last-components, it will return the real length. - GURL_API int Length() const; + int Length() const; // Returns the number of characters before the given component if it exists, // or where the component would be if it did exist. This will return the @@ -156,8 +135,7 @@ // *QUERY: 14 15 <- // *REF: 20 20 // - GURL_API int CountCharactersBefore(ComponentType type, - bool include_delimiter) const; + int CountCharactersBefore(ComponentType type, bool include_delimiter) const; // Scheme without the colon: "http://foo"/ would have a scheme of "http". // The length will be -1 if no scheme is specified ("foo.com"), or 0 if there @@ -181,10 +159,11 @@ // Port number. Component port; - // Path, this is everything following the host name. Length will be -1 if - // unspecified. This includes the preceeding slash, so the path on - // http://www.google.com/asdf" is "/asdf". As a result, it is impossible to - // have a 0 length path, it will be -1 in cases like "http://host?foo". + // Path, this is everything following the host name, stopping at the query of + // ref delimiter (if any). Length will be -1 if unspecified. This includes + // the preceeding slash, so the path on http://www.google.com/asdf" is + // "/asdf". As a result, it is impossible to have a 0 length path, it will + // be -1 in cases like "http://host?foo". // Note that we treat backslashes the same as slashes. Component path; @@ -198,6 +177,37 @@ // Length will be -1 if there is no hash sign, or 0 if there is one but // nothing follows it. Component ref; + + // The URL spec from the character after the scheme: until the end of the + // URL, regardless of the scheme. This is mostly useful for 'opaque' non- + // hierarchical schemes like data: and javascript: as a convient way to get + // the string with the scheme stripped off. + Component GetContent() const; + + // This is used for nested URL types, currently only filesystem. If you + // parse a filesystem URL, the resulting Parsed will have a nested + // inner_parsed_ to hold the parsed inner URL's component information. + // For all other url types [including the inner URL], it will be NULL. + Parsed* inner_parsed() const { + return inner_parsed_; + } + + void set_inner_parsed(const Parsed& inner_parsed) { + if (!inner_parsed_) + inner_parsed_ = new Parsed(inner_parsed); + else + *inner_parsed_ = inner_parsed; + } + + void clear_inner_parsed() { + if (inner_parsed_) { + delete inner_parsed_; + inner_parsed_ = NULL; + } + } + + private: + Parsed* inner_parsed_; // This object is owned and managed by this struct. }; // Initialization functions --------------------------------------------------- @@ -217,24 +227,46 @@ // StandardURL is for when the scheme is known to be one that has an // authority (host) like "http". This function will not handle weird ones // like "about:" and "javascript:", or do the right thing for "file:" URLs. -GURL_API void ParseStandardURL(const char* url, int url_len, Parsed* parsed); -GURL_API void ParseStandardURL(const char16* url, int url_len, Parsed* parsed); +URL_EXPORT void ParseStandardURL(const char* url, + int url_len, + Parsed* parsed); +URL_EXPORT void ParseStandardURL(const base::char16* url, + int url_len, + Parsed* parsed); // PathURL is for when the scheme is known not to have an authority (host) // section but that aren't file URLs either. The scheme is parsed, and // everything after the scheme is considered as the path. This is used for // things like "about:" and "javascript:" -GURL_API void ParsePathURL(const char* url, int url_len, Parsed* parsed); -GURL_API void ParsePathURL(const char16* url, int url_len, Parsed* parsed); +URL_EXPORT void ParsePathURL(const char* url, + int url_len, + bool trim_path_end, + Parsed* parsed); +URL_EXPORT void ParsePathURL(const base::char16* url, + int url_len, + bool trim_path_end, + Parsed* parsed); // FileURL is for file URLs. There are some special rules for interpreting // these. -GURL_API void ParseFileURL(const char* url, int url_len, Parsed* parsed); -GURL_API void ParseFileURL(const char16* url, int url_len, Parsed* parsed); +URL_EXPORT void ParseFileURL(const char* url, int url_len, Parsed* parsed); +URL_EXPORT void ParseFileURL(const base::char16* url, + int url_len, + Parsed* parsed); + +// Filesystem URLs are structured differently than other URLs. +URL_EXPORT void ParseFileSystemURL(const char* url, + int url_len, + Parsed* parsed); +URL_EXPORT void ParseFileSystemURL(const base::char16* url, + int url_len, + Parsed* parsed); // MailtoURL is for mailto: urls. They are made up scheme,path,query -GURL_API void ParseMailtoURL(const char* url, int url_len, Parsed* parsed); -GURL_API void ParseMailtoURL(const char16* url, int url_len, Parsed* parsed); +URL_EXPORT void ParseMailtoURL(const char* url, int url_len, Parsed* parsed); +URL_EXPORT void ParseMailtoURL(const base::char16* url, + int url_len, + Parsed* parsed); // Helper functions ----------------------------------------------------------- @@ -258,27 +290,31 @@ // end of the string). // // The 8-bit version requires UTF-8 encoding. -GURL_API bool ExtractScheme(const char* url, int url_len, Component* scheme); -GURL_API bool ExtractScheme(const char16* url, int url_len, Component* scheme); +URL_EXPORT bool ExtractScheme(const char* url, + int url_len, + Component* scheme); +URL_EXPORT bool ExtractScheme(const base::char16* url, + int url_len, + Component* scheme); // Returns true if ch is a character that terminates the authority segment // of a URL. -GURL_API bool IsAuthorityTerminator(char16 ch); +URL_EXPORT bool IsAuthorityTerminator(base::char16 ch); // Does a best effort parse of input |spec|, in range |auth|. If a particular // component is not found, it will be set to invalid. -GURL_API void ParseAuthority(const char* spec, - const Component& auth, - Component* username, - Component* password, - Component* hostname, - Component* port_num); -GURL_API void ParseAuthority(const char16* spec, - const Component& auth, - Component* username, - Component* password, - Component* hostname, - Component* port_num); +URL_EXPORT void ParseAuthority(const char* spec, + const Component& auth, + Component* username, + Component* password, + Component* hostname, + Component* port_num); +URL_EXPORT void ParseAuthority(const base::char16* spec, + const Component& auth, + Component* username, + Component* password, + Component* hostname, + Component* port_num); // Computes the integer port value from the given port component. The port // component should have been identified by one of the init functions on @@ -287,8 +323,8 @@ // The return value will be a positive integer between 0 and 64K, or one of // the two special values below. enum SpecialPort { PORT_UNSPECIFIED = -1, PORT_INVALID = -2 }; -GURL_API int ParsePort(const char* url, const Component& port); -GURL_API int ParsePort(const char16* url, const Component& port); +URL_EXPORT int ParsePort(const char* url, const Component& port); +URL_EXPORT int ParsePort(const base::char16* url, const Component& port); // Extracts the range of the file name in the given url. The path must // already have been computed by the parse function, and the matching URL @@ -300,12 +336,12 @@ // following the last slash. // // The 8-bit version requires UTF-8 encoding. -GURL_API void ExtractFileName(const char* url, - const Component& path, - Component* file_name); -GURL_API void ExtractFileName(const char16* url, - const Component& path, - Component* file_name); +URL_EXPORT void ExtractFileName(const char* url, + const Component& path, + Component* file_name); +URL_EXPORT void ExtractFileName(const base::char16* url, + const Component& path, + Component* file_name); // Extract the first key/value from the range defined by |*query|. Updates // |*query| to start at the end of the extracted key/value pair. This is @@ -322,15 +358,15 @@ // // If no key/value are found |*key| and |*value| will be unchanged and it will // return false. -GURL_API bool ExtractQueryKeyValue(const char* url, - Component* query, - Component* key, - Component* value); -GURL_API bool ExtractQueryKeyValue(const char16* url, - Component* query, - Component* key, - Component* value); +URL_EXPORT bool ExtractQueryKeyValue(const char* url, + Component* query, + Component* key, + Component* value); +URL_EXPORT bool ExtractQueryKeyValue(const base::char16* url, + Component* query, + Component* key, + Component* value); -} // namespace url_parse +} // namespace url -#endif // GOOGLEURL_SRC_URL_PARSE_H__ +#endif // URL_THIRD_PARTY_MOZILLA_URL_PARSE_H_
diff --git a/googleurl/src/url_canon.h b/src/url/url_canon.h similarity index 62% rename from googleurl/src/url_canon.h rename to src/url/url_canon.h index e2cfb55..89e3509 100644 --- a/googleurl/src/url_canon.h +++ b/src/url/url_canon.h
@@ -1,42 +1,18 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. -#ifndef GOOGLEURL_SRC_URL_CANON_H__ -#define GOOGLEURL_SRC_URL_CANON_H__ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#include <memory.h> +#ifndef URL_URL_CANON_H_ +#define URL_URL_CANON_H_ + #include <stdlib.h> +#include <string.h> -#include "base/string16.h" -#include "googleurl/src/url_common.h" -#include "googleurl/src/url_parse.h" +#include "base/strings/string16.h" +#include "url/url_export.h" +#include "url/url_parse.h" -namespace url_canon { +namespace url { // Canonicalizer output ------------------------------------------------------- @@ -65,13 +41,13 @@ // Accessor for returning a character at a given position. The input offset // must be in the valid range. - inline char at(int offset) const { + inline T at(int offset) const { return buffer_[offset]; } // Sets the character at the given position. The given position MUST be less // than the length(). - inline void set(int offset, int ch) { + inline void set(int offset, T ch) { buffer_[offset] = ch; } @@ -178,7 +154,7 @@ delete[] this->buffer_; } - virtual void Resize(int sz) { + void Resize(int sz) override { T* new_buf = new T[sz]; memcpy(new_buf, this->buffer_, sizeof(T) * (this->cur_len_ < sz ? this->cur_len_ : sz)); @@ -196,12 +172,12 @@ // the templates so it can also be used internally if a wide buffer is // required. typedef CanonOutputT<char> CanonOutput; -typedef CanonOutputT<char16> CanonOutputW; +typedef CanonOutputT<base::char16> CanonOutputW; template<int fixed_capacity> class RawCanonOutput : public RawCanonOutputT<char, fixed_capacity> {}; template<int fixed_capacity> -class RawCanonOutputW : public RawCanonOutputT<char16, fixed_capacity> {}; +class RawCanonOutputW : public RawCanonOutputT<base::char16, fixed_capacity> {}; // Character set converter ---------------------------------------------------- // @@ -211,7 +187,7 @@ // // Embedders will want to see the unit test for the ICU version. -class CharsetConverter { +class URL_EXPORT CharsetConverter { public: CharsetConverter() {} virtual ~CharsetConverter() {} @@ -227,7 +203,7 @@ // decimal, (such as "你") with escaping of the ampersand, number // sign, and semicolon (in the previous example it would be // "%26%2320320%3B"). This rule is based on what IE does in this situation. - virtual void ConvertFromUTF16(const char16* input, + virtual void ConvertFromUTF16(const base::char16* input, int input_len, CanonOutput* output) = 0; }; @@ -247,14 +223,16 @@ // required, the given |buffer| will be used and the returned pointer will // point to the beginning of the buffer. // -// Therefore, callers should not use the buffer, since it may actuall be empty, +// Therefore, callers should not use the buffer, since it may actually be empty, // use the computed pointer and |*output_len| instead. -GURL_API const char* RemoveURLWhitespace(const char* input, int input_len, - CanonOutputT<char>* buffer, - int* output_len); -GURL_API const char16* RemoveURLWhitespace(const char16* input, int input_len, - CanonOutputT<char16>* buffer, +URL_EXPORT const char* RemoveURLWhitespace(const char* input, int input_len, + CanonOutputT<char>* buffer, int* output_len); +URL_EXPORT const base::char16* RemoveURLWhitespace( + const base::char16* input, + int input_len, + CanonOutputT<base::char16>* buffer, + int* output_len); // IDN ------------------------------------------------------------------------ @@ -267,7 +245,9 @@ // the length of the output will be set to the length of the new host name. // // On error, returns false. The output in this case is undefined. -GURL_API bool IDNToASCII(const char16* src, int src_len, CanonOutputW* output); +URL_EXPORT bool IDNToASCII(const base::char16* src, + int src_len, + CanonOutputW* output); // Piece-by-piece canonicalizers ---------------------------------------------- // @@ -293,14 +273,14 @@ // URLs. // // The 8-bit version requires UTF-8 encoding. -GURL_API bool CanonicalizeScheme(const char* spec, - const url_parse::Component& scheme, - CanonOutput* output, - url_parse::Component* out_scheme); -GURL_API bool CanonicalizeScheme(const char16* spec, - const url_parse::Component& scheme, - CanonOutput* output, - url_parse::Component* out_scheme); +URL_EXPORT bool CanonicalizeScheme(const char* spec, + const Component& scheme, + CanonOutput* output, + Component* out_scheme); +URL_EXPORT bool CanonicalizeScheme(const base::char16* spec, + const Component& scheme, + CanonOutput* output, + Component* out_scheme); // User info: username/password. If present, this will add the delimiters so // the output will be "<username>:<password>@" or "<username>@". Empty @@ -312,21 +292,20 @@ // is legal as long as the two components don't overlap. // // The 8-bit version requires UTF-8 encoding. -GURL_API bool CanonicalizeUserInfo(const char* username_source, - const url_parse::Component& username, - const char* password_source, - const url_parse::Component& password, - CanonOutput* output, - url_parse::Component* out_username, - url_parse::Component* out_password); -GURL_API bool CanonicalizeUserInfo(const char16* username_source, - const url_parse::Component& username, - const char16* password_source, - const url_parse::Component& password, - CanonOutput* output, - url_parse::Component* out_username, - url_parse::Component* out_password); - +URL_EXPORT bool CanonicalizeUserInfo(const char* username_source, + const Component& username, + const char* password_source, + const Component& password, + CanonOutput* output, + Component* out_username, + Component* out_password); +URL_EXPORT bool CanonicalizeUserInfo(const base::char16* username_source, + const Component& username, + const base::char16* password_source, + const Component& password, + CanonOutput* output, + Component* out_username, + Component* out_password); // This structure holds detailed state exported from the IP/Host canonicalizers. // Additional fields may be added as callers require them. @@ -359,7 +338,18 @@ // Location of host within the canonicalized output. // CanonicalizeIPAddress() only sets this field if |family| is IPV4 or IPV6. // CanonicalizeHostVerbose() always sets it. - url_parse::Component out_host; + Component out_host; + + // |address| contains the parsed IP Address (if any) in its first + // AddressLength() bytes, in network order. If IsIPAddress() is false + // AddressLength() will return zero and the content of |address| is undefined. + unsigned char address[16]; + + // Convenience function to calculate the length of an IP address corresponding + // to the current IP version in |family|, if any. For use with |address|. + int AddressLength() const { + return family == IPV4 ? 4 : (family == IPV6 ? 16 : 0); + } }; @@ -367,28 +357,27 @@ // // The 8-bit version requires UTF-8 encoding. Use this version when you only // need to know whether canonicalization succeeded. -GURL_API bool CanonicalizeHost(const char* spec, - const url_parse::Component& host, - CanonOutput* output, - url_parse::Component* out_host); -GURL_API bool CanonicalizeHost(const char16* spec, - const url_parse::Component& host, - CanonOutput* output, - url_parse::Component* out_host); +URL_EXPORT bool CanonicalizeHost(const char* spec, + const Component& host, + CanonOutput* output, + Component* out_host); +URL_EXPORT bool CanonicalizeHost(const base::char16* spec, + const Component& host, + CanonOutput* output, + Component* out_host); // Extended version of CanonicalizeHost, which returns additional information. // Use this when you need to know whether the hostname was an IP address. // A successful return is indicated by host_info->family != BROKEN. See the // definition of CanonHostInfo above for details. -GURL_API void CanonicalizeHostVerbose(const char* spec, - const url_parse::Component& host, - CanonOutput* output, - CanonHostInfo* host_info); -GURL_API void CanonicalizeHostVerbose(const char16* spec, - const url_parse::Component& host, - CanonOutput* output, - CanonHostInfo* host_info); - +URL_EXPORT void CanonicalizeHostVerbose(const char* spec, + const Component& host, + CanonOutput* output, + CanonHostInfo* host_info); +URL_EXPORT void CanonicalizeHostVerbose(const base::char16* spec, + const Component& host, + CanonOutput* output, + CanonHostInfo* host_info); // IP addresses. // @@ -400,34 +389,34 @@ // This is called AUTOMATICALLY from the host canonicalizer, which ensures that // the input is unescaped and name-prepped, etc. It should not normally be // necessary or wise to call this directly. -GURL_API void CanonicalizeIPAddress(const char* spec, - const url_parse::Component& host, - CanonOutput* output, - CanonHostInfo* host_info); -GURL_API void CanonicalizeIPAddress(const char16* spec, - const url_parse::Component& host, - CanonOutput* output, - CanonHostInfo* host_info); +URL_EXPORT void CanonicalizeIPAddress(const char* spec, + const Component& host, + CanonOutput* output, + CanonHostInfo* host_info); +URL_EXPORT void CanonicalizeIPAddress(const base::char16* spec, + const Component& host, + CanonOutput* output, + CanonHostInfo* host_info); // Port: this function will add the colon for the port if a port is present. -// The caller can pass url_parse::PORT_UNSPECIFIED as the +// The caller can pass PORT_UNSPECIFIED as the // default_port_for_scheme argument if there is no default port. // // The 8-bit version requires UTF-8 encoding. -GURL_API bool CanonicalizePort(const char* spec, - const url_parse::Component& port, - int default_port_for_scheme, - CanonOutput* output, - url_parse::Component* out_port); -GURL_API bool CanonicalizePort(const char16* spec, - const url_parse::Component& port, - int default_port_for_scheme, - CanonOutput* output, - url_parse::Component* out_port); +URL_EXPORT bool CanonicalizePort(const char* spec, + const Component& port, + int default_port_for_scheme, + CanonOutput* output, + Component* out_port); +URL_EXPORT bool CanonicalizePort(const base::char16* spec, + const Component& port, + int default_port_for_scheme, + CanonOutput* output, + Component* out_port); // Returns the default port for the given canonical scheme, or PORT_UNSPECIFIED // if the scheme is unknown. -GURL_API int DefaultPortForScheme(const char* scheme, int scheme_len); +URL_EXPORT int DefaultPortForScheme(const char* scheme, int scheme_len); // Path. If the input does not begin in a slash (including if the input is // empty), we'll prepend a slash to the path to make it canonical. @@ -438,14 +427,14 @@ // an issue. Somebody giving us an 8-bit path is responsible for generating // the path that the server expects (we'll escape high-bit characters), so // if something is invalid, it's their problem. -GURL_API bool CanonicalizePath(const char* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path); -GURL_API bool CanonicalizePath(const char16* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path); +URL_EXPORT bool CanonicalizePath(const char* spec, + const Component& path, + CanonOutput* output, + Component* out_path); +URL_EXPORT bool CanonicalizePath(const base::char16* spec, + const Component& path, + CanonOutput* output, + Component* out_path); // Canonicalizes the input as a file path. This is like CanonicalizePath except // that it also handles Windows drive specs. For example, the path can begin @@ -453,14 +442,14 @@ // The string will be appended to |*output| and |*out_path| will be updated. // // The 8-bit version requires UTF-8 encoding. -GURL_API bool FileCanonicalizePath(const char* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path); -GURL_API bool FileCanonicalizePath(const char16* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path); +URL_EXPORT bool FileCanonicalizePath(const char* spec, + const Component& path, + CanonOutput* output, + Component* out_path); +URL_EXPORT bool FileCanonicalizePath(const base::char16* spec, + const Component& path, + CanonOutput* output, + Component* out_path); // Query: Prepends the ? if needed. // @@ -474,16 +463,16 @@ // if necessary, for ASCII input, no conversions are necessary. // // The converter can be NULL. In this case, the output encoding will be UTF-8. -GURL_API void CanonicalizeQuery(const char* spec, - const url_parse::Component& query, - CharsetConverter* converter, - CanonOutput* output, - url_parse::Component* out_query); -GURL_API void CanonicalizeQuery(const char16* spec, - const url_parse::Component& query, - CharsetConverter* converter, - CanonOutput* output, - url_parse::Component* out_query); +URL_EXPORT void CanonicalizeQuery(const char* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output, + Component* out_query); +URL_EXPORT void CanonicalizeQuery(const base::char16* spec, + const Component& query, + CharsetConverter* converter, + CanonOutput* output, + Component* out_query); // Ref: Prepends the # if needed. The output will be UTF-8 (this is the only // canonicalizer that does not produce ASCII output). The output is @@ -491,14 +480,14 @@ // // This function will not fail. If the input is invalid UTF-8/UTF-16, we'll use // the "Unicode replacement character" for the confusing bits and copy the rest. -GURL_API void CanonicalizeRef(const char* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path); -GURL_API void CanonicalizeRef(const char16* spec, - const url_parse::Component& path, - CanonOutput* output, - url_parse::Component* out_path); +URL_EXPORT void CanonicalizeRef(const char* spec, + const Component& path, + CanonOutput* output, + Component* out_path); +URL_EXPORT void CanonicalizeRef(const base::char16* spec, + const Component& path, + CanonOutput* output, + Component* out_path); // Full canonicalizer --------------------------------------------------------- // @@ -511,71 +500,85 @@ // The 8-bit versions require UTF-8 encoding. // Use for standard URLs with authorities and paths. -GURL_API bool CanonicalizeStandardURL(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool CanonicalizeStandardURL(const char16* spec, - int spec_len, - const url_parse::Parsed& parsed, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool CanonicalizeStandardURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool CanonicalizeStandardURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); // Use for file URLs. -GURL_API bool CanonicalizeFileURL(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool CanonicalizeFileURL(const char16* spec, - int spec_len, - const url_parse::Parsed& parsed, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool CanonicalizeFileURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool CanonicalizeFileURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); + +// Use for filesystem URLs. +URL_EXPORT bool CanonicalizeFileSystemURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool CanonicalizeFileSystemURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); // Use for path URLs such as javascript. This does not modify the path in any // way, for example, by escaping it. -GURL_API bool CanonicalizePathURL(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool CanonicalizePathURL(const char16* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool CanonicalizePathURL(const char* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool CanonicalizePathURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed); // Use for mailto URLs. This "canonicalizes" the url into a path and query // component. It does not attempt to merge "to" fields. It uses UTF-8 for // the query encoding if there is a query. This is because a mailto URL is // really intended for an external mail program, and the encoding of a page, // etc. which would influence a query encoding normally are irrelevant. -GURL_API bool CanonicalizeMailtoURL(const char* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool CanonicalizeMailtoURL(const char16* spec, - int spec_len, - const url_parse::Parsed& parsed, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool CanonicalizeMailtoURL(const char* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool CanonicalizeMailtoURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed); // Part replacer -------------------------------------------------------------- // Internal structure used for storing separate strings for each component. // The basic canonicalization functions use this structure internally so that -// component remplacement (different strings for different components) can be +// component replacement (different strings for different components) can be // treated on the same code path as regular canonicalization (the same string // for each component). // -// A url_parse::Parsed structure usually goes along with this. Those +// A Parsed structure usually goes along with this. Those // components identify offsets within these strings, so that they can all be // in the same string, or spread arbitrarily across different ones. // @@ -638,7 +641,7 @@ } // Scheme - void SetScheme(const CHAR* s, const url_parse::Component& comp) { + void SetScheme(const CHAR* s, const Component& comp) { sources_.scheme = s; components_.scheme = comp; } @@ -646,86 +649,86 @@ bool IsSchemeOverridden() const { return sources_.scheme != NULL; } // Username - void SetUsername(const CHAR* s, const url_parse::Component& comp) { + void SetUsername(const CHAR* s, const Component& comp) { sources_.username = s; components_.username = comp; } void ClearUsername() { sources_.username = Placeholder(); - components_.username = url_parse::Component(); + components_.username = Component(); } bool IsUsernameOverridden() const { return sources_.username != NULL; } // Password - void SetPassword(const CHAR* s, const url_parse::Component& comp) { + void SetPassword(const CHAR* s, const Component& comp) { sources_.password = s; components_.password = comp; } void ClearPassword() { sources_.password = Placeholder(); - components_.password = url_parse::Component(); + components_.password = Component(); } bool IsPasswordOverridden() const { return sources_.password != NULL; } // Host - void SetHost(const CHAR* s, const url_parse::Component& comp) { + void SetHost(const CHAR* s, const Component& comp) { sources_.host = s; components_.host = comp; } void ClearHost() { sources_.host = Placeholder(); - components_.host = url_parse::Component(); + components_.host = Component(); } bool IsHostOverridden() const { return sources_.host != NULL; } // Port - void SetPort(const CHAR* s, const url_parse::Component& comp) { + void SetPort(const CHAR* s, const Component& comp) { sources_.port = s; components_.port = comp; } void ClearPort() { sources_.port = Placeholder(); - components_.port = url_parse::Component(); + components_.port = Component(); } bool IsPortOverridden() const { return sources_.port != NULL; } // Path - void SetPath(const CHAR* s, const url_parse::Component& comp) { + void SetPath(const CHAR* s, const Component& comp) { sources_.path = s; components_.path = comp; } void ClearPath() { sources_.path = Placeholder(); - components_.path = url_parse::Component(); + components_.path = Component(); } bool IsPathOverridden() const { return sources_.path != NULL; } // Query - void SetQuery(const CHAR* s, const url_parse::Component& comp) { + void SetQuery(const CHAR* s, const Component& comp) { sources_.query = s; components_.query = comp; } void ClearQuery() { sources_.query = Placeholder(); - components_.query = url_parse::Component(); + components_.query = Component(); } bool IsQueryOverridden() const { return sources_.query != NULL; } // Ref - void SetRef(const CHAR* s, const url_parse::Component& comp) { + void SetRef(const CHAR* s, const Component& comp) { sources_.ref = s; components_.ref = comp; } void ClearRef() { sources_.ref = Placeholder(); - components_.ref = url_parse::Component(); + components_.ref = Component(); } bool IsRefOverridden() const { return sources_.ref != NULL; } // Getters for the itnernal data. See the variables below for how the // information is encoded. const URLComponentSource<CHAR>& sources() const { return sources_; } - const url_parse::Parsed& components() const { return components_; } + const Parsed& components() const { return components_; } private: // Returns a pointer to a static empty string that is used as a placeholder @@ -746,63 +749,80 @@ // We use a pointer to the empty string for the source when the component // should be deleted. URLComponentSource<CHAR> sources_; - url_parse::Parsed components_; + Parsed components_; }; // The base must be an 8-bit canonical URL. -GURL_API bool ReplaceStandardURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char>& replacements, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool ReplaceStandardURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool ReplaceStandardURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool ReplaceStandardURL( + const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); + +// Filesystem URLs can only have the path, query, or ref replaced. +// All other components will be ignored. +URL_EXPORT bool ReplaceFileSystemURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool ReplaceFileSystemURL( + const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); // Replacing some parts of a file URL is not permitted. Everything except // the host, path, query, and ref will be ignored. -GURL_API bool ReplaceFileURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char>& replacements, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool ReplaceFileURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool ReplaceFileURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool ReplaceFileURL(const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed); // Path URLs can only have the scheme and path replaced. All other components // will be ignored. -GURL_API bool ReplacePathURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool ReplacePathURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool ReplacePathURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool ReplacePathURL(const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CanonOutput* output, + Parsed* new_parsed); // Mailto URLs can only have the scheme, path, and query replaced. // All other components will be ignored. -GURL_API bool ReplaceMailtoURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed); -GURL_API bool ReplaceMailtoURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, - CanonOutput* output, - url_parse::Parsed* new_parsed); +URL_EXPORT bool ReplaceMailtoURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CanonOutput* output, + Parsed* new_parsed); +URL_EXPORT bool ReplaceMailtoURL(const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CanonOutput* output, + Parsed* new_parsed); // Relative URL --------------------------------------------------------------- @@ -811,26 +831,26 @@ // relative, the relevant portion of the URL will be placed into // |*relative_component| (there may have been trimmed whitespace, for example). // This value is passed to ResolveRelativeURL. If the input is not relative, -// this value is UNDEFINED (it may be changed by the functin). +// this value is UNDEFINED (it may be changed by the function). // // Returns true on success (we successfully determined the URL is relative or // not). Failure means that the combination of URLs doesn't make any sense. // // The base URL should always be canonical, therefore is ASCII. -GURL_API bool IsRelativeURL(const char* base, - const url_parse::Parsed& base_parsed, - const char* fragment, - int fragment_len, - bool is_base_hierarchical, - bool* is_relative, - url_parse::Component* relative_component); -GURL_API bool IsRelativeURL(const char* base, - const url_parse::Parsed& base_parsed, - const char16* fragment, - int fragment_len, - bool is_base_hierarchical, - bool* is_relative, - url_parse::Component* relative_component); +URL_EXPORT bool IsRelativeURL(const char* base, + const Parsed& base_parsed, + const char* fragment, + int fragment_len, + bool is_base_hierarchical, + bool* is_relative, + Component* relative_component); +URL_EXPORT bool IsRelativeURL(const char* base, + const Parsed& base_parsed, + const base::char16* fragment, + int fragment_len, + bool is_base_hierarchical, + bool* is_relative, + Component* relative_component); // Given a canonical parsed source URL, a URL fragment known to be relative, // and the identified relevant portion of the relative URL (computed by @@ -850,23 +870,23 @@ // Returns true on success. On failure, the output will be "something // reasonable" that will be consistent and valid, just probably not what // was intended by the web page author or caller. -GURL_API bool ResolveRelativeURL(const char* base_url, - const url_parse::Parsed& base_parsed, - bool base_is_file, - const char* relative_url, - const url_parse::Component& relative_component, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* out_parsed); -GURL_API bool ResolveRelativeURL(const char* base_url, - const url_parse::Parsed& base_parsed, - bool base_is_file, - const char16* relative_url, - const url_parse::Component& relative_component, - CharsetConverter* query_converter, - CanonOutput* output, - url_parse::Parsed* out_parsed); +URL_EXPORT bool ResolveRelativeURL(const char* base_url, + const Parsed& base_parsed, + bool base_is_file, + const char* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed); +URL_EXPORT bool ResolveRelativeURL(const char* base_url, + const Parsed& base_parsed, + bool base_is_file, + const base::char16* relative_url, + const Component& relative_component, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* out_parsed); -} // namespace url_canon +} // namespace url -#endif // GOOGLEURL_SRC_URL_CANON_H__ +#endif // URL_URL_CANON_H_
diff --git a/googleurl/src/url_canon_etc.cc b/src/url/url_canon_etc.cc similarity index 68% rename from googleurl/src/url_canon_etc.cc rename to src/url/url_canon_etc.cc index aea181a..7409efd 100644 --- a/googleurl/src/url_canon_etc.cc +++ b/src/url/url_canon_etc.cc
@@ -1,40 +1,15 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. // Canonicalizers for random bits that aren't big enough for their own files. #include <string.h> -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" -namespace url_canon { +namespace url { namespace { @@ -107,12 +82,12 @@ template<typename CHAR, typename UCHAR> bool DoScheme(const CHAR* spec, - const url_parse::Component& scheme, + const Component& scheme, CanonOutput* output, - url_parse::Component* out_scheme) { + Component* out_scheme) { if (scheme.len <= 0) { // Scheme is unspecified or empty, convert to empty by appending a colon. - *out_scheme = url_parse::Component(output->length(), 0); + *out_scheme = Component(output->length(), 0); output->push_back(':'); return true; } @@ -123,7 +98,7 @@ // Danger: it's important that this code does not strip any characters: it // only emits the canonical version (be it valid or escaped) of each of // the input characters. Stripping would put it out of sync with - // url_util::FindAndCompareScheme, which could cause some security checks on + // FindAndCompareScheme, which could cause some security checks on // schemes to be incorrect. bool success = true; int end = scheme.end(); @@ -171,16 +146,16 @@ // replacing components. template<typename CHAR, typename UCHAR> bool DoUserInfo(const CHAR* username_spec, - const url_parse::Component& username, + const Component& username, const CHAR* password_spec, - const url_parse::Component& password, + const Component& password, CanonOutput* output, - url_parse::Component* out_username, - url_parse::Component* out_password) { + Component* out_username, + Component* out_password) { if (username.len <= 0 && password.len <= 0) { // Common case: no user info. We strip empty username/passwords. - *out_username = url_parse::Component(); - *out_password = url_parse::Component(); + *out_username = Component(); + *out_password = Component(); return true; } @@ -202,7 +177,7 @@ CHAR_USERINFO, output); out_password->len = output->length() - out_password->begin; } else { - *out_password = url_parse::Component(); + *out_password = Component(); } output->push_back('@'); @@ -213,25 +188,21 @@ inline void WritePortInt(char* output, int output_len, int port) { _itoa_s(port, output, output_len, 10); } -inline void WritePortInt(char16* output, int output_len, int port) { - _itow_s(port, output, output_len, 10); -} // This function will prepend the colon if there will be a port. template<typename CHAR, typename UCHAR> bool DoPort(const CHAR* spec, - const url_parse::Component& port, + const Component& port, int default_port_for_scheme, CanonOutput* output, - url_parse::Component* out_port) { - int port_num = url_parse::ParsePort(spec, port); - if (port_num == url_parse::PORT_UNSPECIFIED || - port_num == default_port_for_scheme) { - *out_port = url_parse::Component(); + Component* out_port) { + int port_num = ParsePort(spec, port); + if (port_num == PORT_UNSPECIFIED || port_num == default_port_for_scheme) { + *out_port = Component(); return true; // Leave port empty. } - if (port_num == url_parse::PORT_INVALID) { + if (port_num == PORT_INVALID) { // Invalid port: We'll copy the text from the input so the user can see // what the error was, and mark the URL as invalid by returning false. output->push_back(':'); @@ -259,12 +230,12 @@ template<typename CHAR, typename UCHAR> void DoCanonicalizeRef(const CHAR* spec, - const url_parse::Component& ref, + const Component& ref, CanonOutput* output, - url_parse::Component* out_ref) { + Component* out_ref) { if (ref.len < 0) { // Common case of no ref. - *out_ref = url_parse::Component(); + *out_ref = Component(); return; } @@ -290,12 +261,11 @@ } else { // Non-ASCII characters are appended unescaped, but only when they are // valid. Invalid Unicode characters are replaced with the "invalid - // character" as IE seems to. + // character" as IE seems to (ReadUTFChar puts the unicode replacement + // character in the output on failure for us). unsigned code_point; - if (!ReadUTFChar(spec, &i, end, &code_point)) - AppendUTF8Value(kUnicodeReplacementCharacter, output); - else - AppendUTF8Value(code_point, output); + ReadUTFChar(spec, &i, end, &code_point); + AppendUTF8Value(code_point, output); } } @@ -310,87 +280,88 @@ return DoRemoveURLWhitespace(input, input_len, buffer, output_len); } -const char16* RemoveURLWhitespace(const char16* input, int input_len, - CanonOutputT<char16>* buffer, - int* output_len) { +const base::char16* RemoveURLWhitespace(const base::char16* input, + int input_len, + CanonOutputT<base::char16>* buffer, + int* output_len) { return DoRemoveURLWhitespace(input, input_len, buffer, output_len); } -char CanonicalSchemeChar(char16 ch) { +char CanonicalSchemeChar(base::char16 ch) { if (ch >= 0x80) return 0; // Non-ASCII is not supported by schemes. return kSchemeCanonical[ch]; } bool CanonicalizeScheme(const char* spec, - const url_parse::Component& scheme, + const Component& scheme, CanonOutput* output, - url_parse::Component* out_scheme) { + Component* out_scheme) { return DoScheme<char, unsigned char>(spec, scheme, output, out_scheme); } -bool CanonicalizeScheme(const char16* spec, - const url_parse::Component& scheme, +bool CanonicalizeScheme(const base::char16* spec, + const Component& scheme, CanonOutput* output, - url_parse::Component* out_scheme) { - return DoScheme<char16, char16>(spec, scheme, output, out_scheme); + Component* out_scheme) { + return DoScheme<base::char16, base::char16>(spec, scheme, output, out_scheme); } bool CanonicalizeUserInfo(const char* username_source, - const url_parse::Component& username, + const Component& username, const char* password_source, - const url_parse::Component& password, + const Component& password, CanonOutput* output, - url_parse::Component* out_username, - url_parse::Component* out_password) { + Component* out_username, + Component* out_password) { return DoUserInfo<char, unsigned char>( username_source, username, password_source, password, output, out_username, out_password); } -bool CanonicalizeUserInfo(const char16* username_source, - const url_parse::Component& username, - const char16* password_source, - const url_parse::Component& password, +bool CanonicalizeUserInfo(const base::char16* username_source, + const Component& username, + const base::char16* password_source, + const Component& password, CanonOutput* output, - url_parse::Component* out_username, - url_parse::Component* out_password) { - return DoUserInfo<char16, char16>( + Component* out_username, + Component* out_password) { + return DoUserInfo<base::char16, base::char16>( username_source, username, password_source, password, output, out_username, out_password); } bool CanonicalizePort(const char* spec, - const url_parse::Component& port, + const Component& port, int default_port_for_scheme, CanonOutput* output, - url_parse::Component* out_port) { + Component* out_port) { return DoPort<char, unsigned char>(spec, port, default_port_for_scheme, output, out_port); } -bool CanonicalizePort(const char16* spec, - const url_parse::Component& port, +bool CanonicalizePort(const base::char16* spec, + const Component& port, int default_port_for_scheme, CanonOutput* output, - url_parse::Component* out_port) { - return DoPort<char16, char16>(spec, port, default_port_for_scheme, - output, out_port); + Component* out_port) { + return DoPort<base::char16, base::char16>(spec, port, default_port_for_scheme, + output, out_port); } void CanonicalizeRef(const char* spec, - const url_parse::Component& ref, + const Component& ref, CanonOutput* output, - url_parse::Component* out_ref) { + Component* out_ref) { DoCanonicalizeRef<char, unsigned char>(spec, ref, output, out_ref); } -void CanonicalizeRef(const char16* spec, - const url_parse::Component& ref, +void CanonicalizeRef(const base::char16* spec, + const Component& ref, CanonOutput* output, - url_parse::Component* out_ref) { - DoCanonicalizeRef<char16, char16>(spec, ref, output, out_ref); + Component* out_ref) { + DoCanonicalizeRef<base::char16, base::char16>(spec, ref, output, out_ref); } -} // namespace url_canon +} // namespace url
diff --git a/src/url/url_canon_filesystemurl.cc b/src/url/url_canon_filesystemurl.cc new file mode 100644 index 0000000..18e9055 --- /dev/null +++ b/src/url/url_canon_filesystemurl.cc
@@ -0,0 +1,129 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Functions for canonicalizing "filesystem:file:" URLs. + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" +#include "url/url_util.h" +#include "url/url_util_internal.h" + +namespace url { + +namespace { + +// We use the URLComponentSource for the outer URL, as it can have replacements, +// whereas the inner_url can't, so it uses spec. +template<typename CHAR, typename UCHAR> +bool DoCanonicalizeFileSystemURL(const CHAR* spec, + const URLComponentSource<CHAR>& source, + const Parsed& parsed, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + // filesystem only uses {scheme, path, query, ref} -- clear the rest. + new_parsed->username.reset(); + new_parsed->password.reset(); + new_parsed->host.reset(); + new_parsed->port.reset(); + + const Parsed* inner_parsed = parsed.inner_parsed(); + Parsed new_inner_parsed; + + // Scheme (known, so we don't bother running it through the more + // complicated scheme canonicalizer). + new_parsed->scheme.begin = output->length(); + output->Append("filesystem:", 11); + new_parsed->scheme.len = 10; + + if (!parsed.inner_parsed() || !parsed.inner_parsed()->scheme.is_valid()) + return false; + + bool success = true; + if (CompareSchemeComponent(spec, inner_parsed->scheme, url::kFileScheme)) { + new_inner_parsed.scheme.begin = output->length(); + output->Append("file://", 7); + new_inner_parsed.scheme.len = 4; + success &= CanonicalizePath(spec, inner_parsed->path, output, + &new_inner_parsed.path); + } else if (IsStandard(spec, inner_parsed->scheme)) { + success = CanonicalizeStandardURL(spec, parsed.inner_parsed()->Length(), + *parsed.inner_parsed(), charset_converter, + output, &new_inner_parsed); + } else { + // TODO(ericu): The URL is wrong, but should we try to output more of what + // we were given? Echoing back filesystem:mailto etc. doesn't seem all that + // useful. + return false; + } + // The filesystem type must be more than just a leading slash for validity. + success &= parsed.inner_parsed()->path.len > 1; + + success &= CanonicalizePath(source.path, parsed.path, output, + &new_parsed->path); + + // Ignore failures for query/ref since the URL can probably still be loaded. + CanonicalizeQuery(source.query, parsed.query, charset_converter, + output, &new_parsed->query); + CanonicalizeRef(source.ref, parsed.ref, output, &new_parsed->ref); + if (success) + new_parsed->set_inner_parsed(new_inner_parsed); + + return success; +} + +} // namespace + +bool CanonicalizeFileSystemURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileSystemURL<char, unsigned char>( + spec, URLComponentSource<char>(spec), parsed, charset_converter, output, + new_parsed); +} + +bool CanonicalizeFileSystemURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileSystemURL<base::char16, base::char16>( + spec, URLComponentSource<base::char16>(spec), parsed, charset_converter, + output, new_parsed); +} + +bool ReplaceFileSystemURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizeFileSystemURL<char, unsigned char>( + base, source, parsed, charset_converter, output, new_parsed); +} + +bool ReplaceFileSystemURL(const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizeFileSystemURL<char, unsigned char>( + base, source, parsed, charset_converter, output, new_parsed); +} + +} // namespace url
diff --git a/src/url/url_canon_fileurl.cc b/src/url/url_canon_fileurl.cc new file mode 100644 index 0000000..6191f8f --- /dev/null +++ b/src/url/url_canon_fileurl.cc
@@ -0,0 +1,189 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Functions for canonicalizing "file:" URLs. + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" + +namespace url { + +namespace { + +#ifdef WIN32 + +// Given a pointer into the spec, this copies and canonicalizes the drive +// letter and colon to the output, if one is found. If there is not a drive +// spec, it won't do anything. The index of the next character in the input +// spec is returned (after the colon when a drive spec is found, the begin +// offset if one is not). +template<typename CHAR> +int FileDoDriveSpec(const CHAR* spec, int begin, int end, + CanonOutput* output) { + // The path could be one of several things: /foo/bar, c:/foo/bar, /c:/foo, + // (with backslashes instead of slashes as well). + int num_slashes = CountConsecutiveSlashes(spec, begin, end); + int after_slashes = begin + num_slashes; + + if (!DoesBeginWindowsDriveSpec(spec, after_slashes, end)) + return begin; // Haven't consumed any characters + + // A drive spec is the start of a path, so we need to add a slash for the + // authority terminator (typically the third slash). + output->push_back('/'); + + // DoesBeginWindowsDriveSpec will ensure that the drive letter is valid + // and that it is followed by a colon/pipe. + + // Normalize Windows drive letters to uppercase + if (spec[after_slashes] >= 'a' && spec[after_slashes] <= 'z') + output->push_back(static_cast<char>(spec[after_slashes] - 'a' + 'A')); + else + output->push_back(static_cast<char>(spec[after_slashes])); + + // Normalize the character following it to a colon rather than pipe. + output->push_back(':'); + return after_slashes + 2; +} + +#endif // WIN32 + +template<typename CHAR, typename UCHAR> +bool DoFileCanonicalizePath(const CHAR* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + // Copies and normalizes the "c:" at the beginning, if present. + out_path->begin = output->length(); + int after_drive; +#ifdef WIN32 + after_drive = FileDoDriveSpec(spec, path.begin, path.end(), output); +#else + after_drive = path.begin; +#endif + + // Copies the rest of the path, starting from the slash following the + // drive colon (if any, Windows only), or the first slash of the path. + bool success = true; + if (after_drive < path.end()) { + // Use the regular path canonicalizer to canonicalize the rest of the + // path. Give it a fake output component to write into. DoCanonicalizeFile + // will compute the full path component. + Component sub_path = MakeRange(after_drive, path.end()); + Component fake_output_path; + success = CanonicalizePath(spec, sub_path, output, &fake_output_path); + } else { + // No input path, canonicalize to a slash. + output->push_back('/'); + } + + out_path->len = output->length() - out_path->begin; + return success; +} + +template<typename CHAR, typename UCHAR> +bool DoCanonicalizeFileURL(const URLComponentSource<CHAR>& source, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + // Things we don't set in file: URLs. + new_parsed->username = Component(); + new_parsed->password = Component(); + new_parsed->port = Component(); + + // Scheme (known, so we don't bother running it through the more + // complicated scheme canonicalizer). + new_parsed->scheme.begin = output->length(); + output->Append("file://", 7); + new_parsed->scheme.len = 4; + + // Append the host. For many file URLs, this will be empty. For UNC, this + // will be present. + // TODO(brettw) This doesn't do any checking for host name validity. We + // should probably handle validity checking of UNC hosts differently than + // for regular IP hosts. + bool success = CanonicalizeHost(source.host, parsed.host, + output, &new_parsed->host); + success &= DoFileCanonicalizePath<CHAR, UCHAR>(source.path, parsed.path, + output, &new_parsed->path); + CanonicalizeQuery(source.query, parsed.query, query_converter, + output, &new_parsed->query); + + // Ignore failure for refs since the URL can probably still be loaded. + CanonicalizeRef(source.ref, parsed.ref, output, &new_parsed->ref); + + return success; +} + +} // namespace + +bool CanonicalizeFileURL(const char* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileURL<char, unsigned char>( + URLComponentSource<char>(spec), parsed, query_converter, + output, new_parsed); +} + +bool CanonicalizeFileURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeFileURL<base::char16, base::char16>( + URLComponentSource<base::char16>(spec), parsed, query_converter, + output, new_parsed); +} + +bool FileCanonicalizePath(const char* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + return DoFileCanonicalizePath<char, unsigned char>(spec, path, + output, out_path); +} + +bool FileCanonicalizePath(const base::char16* spec, + const Component& path, + CanonOutput* output, + Component* out_path) { + return DoFileCanonicalizePath<base::char16, base::char16>(spec, path, + output, out_path); +} + +bool ReplaceFileURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizeFileURL<char, unsigned char>( + source, parsed, query_converter, output, new_parsed); +} + +bool ReplaceFileURL(const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CharsetConverter* query_converter, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizeFileURL<char, unsigned char>( + source, parsed, query_converter, output, new_parsed); +} + +} // namespace url
diff --git a/googleurl/src/url_canon_host.cc b/src/url/url_canon_host.cc similarity index 83% rename from googleurl/src/url_canon_host.cc rename to src/url/url_canon_host.cc index 6642004..513248a 100644 --- a/googleurl/src/url_canon_host.cc +++ b/src/url/url_canon_host.cc
@@ -1,37 +1,12 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "base/logging.h" -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" -namespace url_canon { +namespace url { namespace { @@ -94,14 +69,16 @@ const int kTempHostBufferLen = 1024; typedef RawCanonOutputT<char, kTempHostBufferLen> StackBuffer; -typedef RawCanonOutputT<char16, kTempHostBufferLen> StackBufferW; +typedef RawCanonOutputT<base::char16, kTempHostBufferLen> StackBufferW; // Scans a host name and fills in the output flags according to what we find. // |has_non_ascii| will be true if there are any non-7-bit characters, and // |has_escaped| will be true if there is a percent sign. template<typename CHAR, typename UCHAR> -void ScanHostname(const CHAR* spec, const url_parse::Component& host, - bool* has_non_ascii, bool* has_escaped) { +void ScanHostname(const CHAR* spec, + const Component& host, + bool* has_non_ascii, + bool* has_escaped) { int end = host.end(); *has_non_ascii = false; *has_escaped = false; @@ -187,7 +164,7 @@ } // Canonicalizes a host that requires IDN conversion. Returns true on success -bool DoIDNHost(const char16* src, int src_len, CanonOutput* output) { +bool DoIDNHost(const base::char16* src, int src_len, CanonOutput* output) { // We need to escape URL before doing IDN conversion, since punicode strings // cannot be escaped after they are created. RawCanonOutputW<kTempHostBufferLen> url_escaped_host; @@ -280,7 +257,7 @@ // UTF-16 convert host to its ASCII version. The set up is already ready for // the backend, so we just pass through. The has_escaped flag should be set if // the input string requires unescaping. -bool DoComplexHost(const char16* host, int host_len, +bool DoComplexHost(const base::char16* host, int host_len, bool has_non_ascii, bool has_escaped, CanonOutput* output) { if (has_escaped) { // Yikes, we have escaped characters with wide input. The escaped @@ -312,13 +289,13 @@ template<typename CHAR, typename UCHAR> void DoHost(const CHAR* spec, - const url_parse::Component& host, + const Component& host, CanonOutput* output, CanonHostInfo* host_info) { if (host.len <= 0) { // Empty hosts don't need anything. host_info->family = CanonHostInfo::NEUTRAL; - host_info->out_host = url_parse::Component(); + host_info->out_host = Component(); return; } @@ -347,7 +324,7 @@ // should not cause an allocation. RawCanonOutput<64> canon_ip; CanonicalizeIPAddress(output->data(), - url_parse::MakeRange(output_begin, output->length()), + MakeRange(output_begin, output->length()), &canon_ip, host_info); // If we got an IPv4/IPv6 address, copy the canonical form back to the @@ -359,43 +336,43 @@ } } - host_info->out_host = url_parse::MakeRange(output_begin, output->length()); + host_info->out_host = MakeRange(output_begin, output->length()); } } // namespace bool CanonicalizeHost(const char* spec, - const url_parse::Component& host, + const Component& host, CanonOutput* output, - url_parse::Component* out_host) { + Component* out_host) { CanonHostInfo host_info; DoHost<char, unsigned char>(spec, host, output, &host_info); *out_host = host_info.out_host; return (host_info.family != CanonHostInfo::BROKEN); } -bool CanonicalizeHost(const char16* spec, - const url_parse::Component& host, +bool CanonicalizeHost(const base::char16* spec, + const Component& host, CanonOutput* output, - url_parse::Component* out_host) { + Component* out_host) { CanonHostInfo host_info; - DoHost<char16, char16>(spec, host, output, &host_info); + DoHost<base::char16, base::char16>(spec, host, output, &host_info); *out_host = host_info.out_host; return (host_info.family != CanonHostInfo::BROKEN); } void CanonicalizeHostVerbose(const char* spec, - const url_parse::Component& host, + const Component& host, CanonOutput* output, - CanonHostInfo *host_info) { + CanonHostInfo* host_info) { DoHost<char, unsigned char>(spec, host, output, host_info); } -void CanonicalizeHostVerbose(const char16* spec, - const url_parse::Component& host, +void CanonicalizeHostVerbose(const base::char16* spec, + const Component& host, CanonOutput* output, - CanonHostInfo *host_info) { - DoHost<char16, char16>(spec, host, output, host_info); + CanonHostInfo* host_info) { + DoHost<base::char16, base::char16>(spec, host, output, host_info); } -} // namespace url_canon +} // namespace url
diff --git a/src/url/url_canon_icu.cc b/src/url/url_canon_icu.cc new file mode 100644 index 0000000..743ff00 --- /dev/null +++ b/src/url/url_canon_icu.cc
@@ -0,0 +1,186 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// ICU integration functions. + +#include <stdlib.h> +#include <string.h> + +#include "base/logging.h" +#include "third_party/icu/include/unicode/ucnv.h" +#include "third_party/icu/include/unicode/ucnv_cb.h" +#include "third_party/icu/include/unicode/uidna.h" +#include "url/url_canon_icu.h" +#include "url/url_canon_internal.h" // for _itoa_s +#include "util/gtl/lazy_static_ptr.h" + +namespace url { + +namespace { + +// Called when converting a character that can not be represented, this will +// append an escaped version of the numerical character reference for that code +// point. It is of the form "Ӓ" and we will escape the non-digits to +// "%26%231234%3B". Why? This is what Netscape did back in the olden days. +void appendURLEscapedChar(const void* context, + UConverterFromUnicodeArgs* from_args, + const UChar* code_units, + int32_t length, + UChar32 code_point, + UConverterCallbackReason reason, + UErrorCode* err) { + if (reason == UCNV_UNASSIGNED) { + *err = U_ZERO_ERROR; + + const static int prefix_len = 6; + const static char prefix[prefix_len + 1] = "%26%23"; // "&#" percent-escaped + ucnv_cbFromUWriteBytes(from_args, prefix, prefix_len, 0, err); + + DCHECK(code_point < 0x110000); + char number[8]; // Max Unicode code point is 7 digits. + _itoa_s(code_point, number, 10); + int number_len = static_cast<int>(strlen(number)); + ucnv_cbFromUWriteBytes(from_args, number, number_len, 0, err); + + const static int postfix_len = 3; + const static char postfix[postfix_len + 1] = "%3B"; // ";" percent-escaped + ucnv_cbFromUWriteBytes(from_args, postfix, postfix_len, 0, err); + } +} + +// A class for scoping the installation of the invalid character callback. +class AppendHandlerInstaller { + public: + // The owner of this object must ensure that the converter is alive for the + // duration of this object's lifetime. + AppendHandlerInstaller(UConverter* converter) : converter_(converter) { + UErrorCode err = U_ZERO_ERROR; + ucnv_setFromUCallBack(converter_, appendURLEscapedChar, 0, + &old_callback_, &old_context_, &err); + } + + ~AppendHandlerInstaller() { + UErrorCode err = U_ZERO_ERROR; + ucnv_setFromUCallBack(converter_, old_callback_, old_context_, 0, 0, &err); + } + + private: + UConverter* converter_; + + UConverterFromUCallback old_callback_; + const void* old_context_; +}; + +// A wrapper to use LazyInstance<>::Leaky with ICU's UIDNA, a C pointer to +// a UTS46/IDNA 2008 handling object opened with uidna_openUTS46(). +// +// We use UTS46 with BiDiCheck to migrate from IDNA 2003 (with unassigned +// code points allowed) to IDNA 2008 with +// the backward compatibility in mind. What it does: +// +// 1. Use the up-to-date Unicode data. +// 2. Define a case folding/mapping with the up-to-date Unicode data as +// in IDNA 2003. +// 3. Use transitional mechanism for 4 deviation characters (sharp-s, +// final sigma, ZWJ and ZWNJ) for now. +// 4. Continue to allow symbols and punctuations. +// 5. Apply new BiDi check rules more permissive than the IDNA 2003 BiDI rules. +// 6. Do not apply STD3 rules +// 7. Do not allow unassigned code points. +// +// It also closely matches what IE 10 does except for the BiDi check ( +// http://goo.gl/3XBhqw ). +// See http://http://unicode.org/reports/tr46/ and references therein +// for more details. +struct UIDNAWrapper { + UIDNAWrapper() { + UErrorCode err = U_ZERO_ERROR; + // TODO(jungshik): Change options as different parties (browsers, + // registrars, search engines) converge toward a consensus. + value = uidna_openUTS46(UIDNA_CHECK_BIDI, &err); + if (U_FAILURE(err)) + value = NULL; + } + + UIDNA* value; +}; + +} // namespace + +ICUCharsetConverter::ICUCharsetConverter(UConverter* converter) + : converter_(converter) { +} + +ICUCharsetConverter::~ICUCharsetConverter() { +} + +void ICUCharsetConverter::ConvertFromUTF16(const base::char16* input, + int input_len, + CanonOutput* output) { + // Install our error handler. It will be called for character that can not + // be represented in the destination character set. + AppendHandlerInstaller handler(converter_); + + int begin_offset = output->length(); + int dest_capacity = output->capacity() - begin_offset; + output->set_length(output->length()); + + do { + UErrorCode err = U_ZERO_ERROR; + char* dest = &output->data()[begin_offset]; + int required_capacity = ucnv_fromUChars(converter_, dest, dest_capacity, + input, input_len, &err); + if (err != U_BUFFER_OVERFLOW_ERROR) { + output->set_length(begin_offset + required_capacity); + return; + } + + // Output didn't fit, expand + dest_capacity = required_capacity; + output->Resize(begin_offset + dest_capacity); + } while (true); +} + +static util::gtl::LazyStaticPtr<UIDNAWrapper> g_uidna; + +// Converts the Unicode input representing a hostname to ASCII using IDN rules. +// The output must be ASCII, but is represented as wide characters. +// +// On success, the output will be filled with the ASCII host name and it will +// return true. Unlike most other canonicalization functions, this assumes that +// the output is empty. The beginning of the host will be at offset 0, and +// the length of the output will be set to the length of the new host name. +// +// On error, this will return false. The output in this case is undefined. +// TODO(jungshik): use UTF-8/ASCII version of nameToASCII. +// Change the function signature and callers accordingly to avoid unnecessary +// conversions in our code. In addition, consider using icu::IDNA's UTF-8/ASCII +// version with StringByteSink. That way, we can avoid C wrappers and additional +// string conversion. +bool IDNToASCII(const base::char16* src, int src_len, CanonOutputW* output) { + DCHECK(output->length() == 0); // Output buffer is assumed empty. + + UIDNA* uidna = g_uidna->value; + DCHECK(uidna != NULL); + while (true) { + UErrorCode err = U_ZERO_ERROR; + UIDNAInfo info = UIDNA_INFO_INITIALIZER; + int output_length = uidna_nameToASCII(uidna, src, src_len, output->data(), + output->capacity(), &info, &err); + if (U_SUCCESS(err) && info.errors == 0) { + output->set_length(output_length); + return true; + } + + // TODO(jungshik): Look at info.errors to handle them case-by-case basis + // if necessary. + if (err != U_BUFFER_OVERFLOW_ERROR || info.errors != 0) + return false; // Unknown error, give up. + + // Not enough room in our buffer, expand. + output->Resize(output_length); + } +} + +} // namespace url
diff --git a/src/url/url_canon_icu.h b/src/url/url_canon_icu.h new file mode 100644 index 0000000..c3c1f01 --- /dev/null +++ b/src/url/url_canon_icu.h
@@ -0,0 +1,39 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_CANON_ICU_H_ +#define URL_URL_CANON_ICU_H_ + +// ICU integration functions. + +#include "url/url_canon.h" +#include "url/url_export.h" + +typedef struct UConverter UConverter; + +namespace url { + +// An implementation of CharsetConverter that implementations can use to +// interface the canonicalizer with ICU's conversion routines. +class URL_EXPORT ICUCharsetConverter : public CharsetConverter { + public: + // Constructs a converter using an already-existing ICU character set + // converter. This converter is NOT owned by this object; the lifetime must + // be managed by the creator such that it is alive as long as this is. + ICUCharsetConverter(UConverter* converter); + + ~ICUCharsetConverter() override; + + void ConvertFromUTF16(const base::char16* input, + int input_len, + CanonOutput* output) override; + + private: + // The ICU converter, not owned by this class. + UConverter* converter_; +}; + +} // namespace url + +#endif // URL_URL_CANON_ICU_H_
diff --git a/src/url/url_canon_icu_unittest.cc b/src/url/url_canon_icu_unittest.cc new file mode 100644 index 0000000..cfa4b49 --- /dev/null +++ b/src/url/url_canon_icu_unittest.cc
@@ -0,0 +1,160 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/macros.h" +#include "testing/base/public/gunit.h" +#include "third_party/icu/include/unicode/ucnv.h" +#include "url/url_canon.h" +#include "url/url_canon_icu.h" +#include "url/url_canon_stdstring.h" +#include "url/url_test_utils.h" + +namespace url { + +using test_utils::WStringToUTF16; + +namespace { + +// Wrapper around a UConverter object that managers creation and destruction. +class UConvScoper { + public: + explicit UConvScoper(const char* charset_name) { + UErrorCode err = U_ZERO_ERROR; + converter_ = ucnv_open(charset_name, &err); + } + + ~UConvScoper() { + if (converter_) + ucnv_close(converter_); + } + + // Returns the converter object, may be NULL. + UConverter* converter() const { return converter_; } + + private: + UConverter* converter_; +}; + +TEST(URLCanonIcuTest, ICUCharsetConverter) { + struct ICUCase { + const wchar_t* input; + const char* encoding; + const char* expected; + } icu_cases[] = { + // UTF-8. + {L"Hello, world", "utf-8", "Hello, world"}, + {L"\x4f60\x597d", "utf-8", "\xe4\xbd\xa0\xe5\xa5\xbd"}, + // Non-BMP UTF-8. + {L"!\xd800\xdf00!", "utf-8", "!\xf0\x90\x8c\x80!"}, + // Big5 + {L"\x4f60\x597d", "big5", "\xa7\x41\xa6\x6e"}, + // Unrepresentable character in the destination set. + {L"hello\x4f60\x06de\x597dworld", "big5", + "hello\xa7\x41%26%231758%3B\xa6\x6eworld"}, + }; + + for (size_t i = 0; i < arraysize(icu_cases); i++) { + UConvScoper conv(icu_cases[i].encoding); + ASSERT_TRUE(conv.converter() != NULL); + ICUCharsetConverter converter(conv.converter()); + + std::string str; + StdStringCanonOutput output(&str); + + base::string16 input_str(WStringToUTF16(icu_cases[i].input)); + int input_len = static_cast<int>(input_str.length()); + converter.ConvertFromUTF16(input_str.c_str(), input_len, &output); + output.Complete(); + + EXPECT_STREQ(icu_cases[i].expected, str.c_str()); + } + + // Test string sizes around the resize boundary for the output to make sure + // the converter resizes as needed. + const int static_size = 16; + UConvScoper conv("utf-8"); + ASSERT_TRUE(conv.converter()); + ICUCharsetConverter converter(conv.converter()); + for (int i = static_size - 2; i <= static_size + 2; i++) { + // Make a string with the appropriate length. + base::string16 input; + for (int ch = 0; ch < i; ch++) + input.push_back('a'); + + RawCanonOutput<static_size> output; + converter.ConvertFromUTF16(input.c_str(), static_cast<int>(input.length()), + &output); + EXPECT_EQ(input.length(), static_cast<size_t>(output.length())); + } +} + +TEST(URLCanonIcuTest, QueryWithConverter) { + struct QueryCase { + const char* input8; + const wchar_t* input16; + const char* encoding; + const char* expected; + } query_cases[] = { + // Regular ASCII case in some different encodings. + {"foo=bar", L"foo=bar", "utf-8", "?foo=bar"}, + {"foo=bar", L"foo=bar", "shift_jis", "?foo=bar"}, + {"foo=bar", L"foo=bar", "gb2312", "?foo=bar"}, + // Chinese input/output + {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "gb2312", + "?q=%C4%E3%BA%C3"}, + {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "big5", "?q=%A7A%A6n"}, + // Unencodable character in the destination character set should be + // escaped. The escape sequence unescapes to be the entity name: + // "?q=你" + {"q=Chinese\xef\xbc\xa7", L"q=Chinese\xff27", "iso-8859-1", + "?q=Chinese%26%2365319%3B"}, + }; + + for (size_t i = 0; i < arraysize(query_cases); i++) { + Component out_comp; + + UConvScoper conv(query_cases[i].encoding); + ASSERT_TRUE(!query_cases[i].encoding || conv.converter()); + ICUCharsetConverter converter(conv.converter()); + + if (query_cases[i].input8) { + int len = static_cast<int>(strlen(query_cases[i].input8)); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(query_cases[i].input8, in_comp, &converter, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + + if (query_cases[i].input16) { + base::string16 input16(WStringToUTF16(query_cases[i].input16)); + int len = static_cast<int>(input16.length()); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(input16.c_str(), in_comp, &converter, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + } + + // Extra test for input with embedded NULL; + std::string out_str; + StdStringCanonOutput output(&out_str); + Component out_comp; + CanonicalizeQuery("a \x00z\x01", Component(0, 5), NULL, &output, &out_comp); + output.Complete(); + EXPECT_EQ("?a%20%00z%01", out_str); +} + +} // namespace + +} // namespace url
diff --git a/googleurl/src/url_canon_internal.cc b/src/url/url_canon_internal.cc similarity index 61% rename from googleurl/src/url_canon_internal.cc rename to src/url/url_canon_internal.cc index 6b776bc..1554814 100644 --- a/googleurl/src/url_canon_internal.cc +++ b/src/url/url_canon_internal.cc
@@ -1,40 +1,18 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#include <cstdio> +#include "url/url_canon_internal.h" + #include <errno.h> #include <stdlib.h> + +#include <cstdio> #include <string> -#include "googleurl/src/url_canon_internal.h" +#include "base/strings/utf_string_conversion_utils.h" -namespace url_canon { +namespace url { namespace { @@ -82,12 +60,12 @@ } } -// Overrides one component, see the url_canon::Replacements structure for +// Overrides one component, see the Replacements structure for // what the various combionations of source pointer and component mean. void DoOverrideComponent(const char* override_source, - const url_parse::Component& override_component, + const Component& override_component, const char** dest, - url_parse::Component* dest_component) { + Component* dest_component) { if (override_source) { *dest = override_source; *dest_component = override_component; @@ -98,7 +76,7 @@ // not actually set the output character pointer. // // The input is converted to UTF-8 at the end of the given buffer as a temporary -// holding place. The component indentifying the portion of the buffer used in +// holding place. The component identifying the portion of the buffer used in // the |utf8_buffer| will be specified in |*dest_component|. // // This will not actually set any |dest| pointer like DoOverrideComponent @@ -106,16 +84,15 @@ // may get resized while we're overriding a subsequent component. Instead, the // caller should use the beginning of the |utf8_buffer| as the string pointer // for all components once all overrides have been prepared. -bool PrepareUTF16OverrideComponent( - const char16* override_source, - const url_parse::Component& override_component, - CanonOutput* utf8_buffer, - url_parse::Component* dest_component) { +bool PrepareUTF16OverrideComponent(const base::char16* override_source, + const Component& override_component, + CanonOutput* utf8_buffer, + Component* dest_component) { bool success = true; if (override_source) { if (!override_component.is_valid()) { // Non-"valid" component (means delete), so we need to preserve that. - *dest_component = url_parse::Component(); + *dest_component = Component(); } else { // Convert to UTF-8. dest_component->begin = utf8_buffer->length(); @@ -134,31 +111,31 @@ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00 - 0x0f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10 - 0x1f 0, // 0x20 ' ' (escape spaces in queries) - CHAR_QUERY | CHAR_USERINFO, // 0x21 ! + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x21 ! 0, // 0x22 " 0, // 0x23 # (invalid in query since it marks the ref) CHAR_QUERY | CHAR_USERINFO, // 0x24 $ CHAR_QUERY | CHAR_USERINFO, // 0x25 % CHAR_QUERY | CHAR_USERINFO, // 0x26 & - CHAR_QUERY | CHAR_USERINFO, // 0x27 ' - CHAR_QUERY | CHAR_USERINFO, // 0x28 ( - CHAR_QUERY | CHAR_USERINFO, // 0x29 ) - CHAR_QUERY | CHAR_USERINFO, // 0x2a * + 0, // 0x27 ' (Try to prevent XSS.) + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x28 ( + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x29 ) + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x2a * CHAR_QUERY | CHAR_USERINFO, // 0x2b + CHAR_QUERY | CHAR_USERINFO, // 0x2c , - CHAR_QUERY | CHAR_USERINFO, // 0x2d - - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4, // 0x2e . - CHAR_QUERY, // 0x2f / - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x30 0 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x31 1 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x32 2 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x33 3 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x34 4 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x35 5 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x36 6 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT, // 0x37 7 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC, // 0x38 8 - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC, // 0x39 9 + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x2d - + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_COMPONENT, // 0x2e . + CHAR_QUERY, // 0x2f / + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x30 0 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x31 1 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x32 2 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x33 3 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x34 4 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x35 5 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x36 6 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_OCT | CHAR_COMPONENT, // 0x37 7 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_COMPONENT, // 0x38 8 + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_DEC | CHAR_COMPONENT, // 0x39 9 CHAR_QUERY, // 0x3a : CHAR_QUERY, // 0x3b ; 0, // 0x3c < (Try to prevent certain types of XSS.) @@ -166,68 +143,68 @@ 0, // 0x3e > (Try to prevent certain types of XSS.) CHAR_QUERY, // 0x3f ? CHAR_QUERY, // 0x40 @ - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x41 A - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x42 B - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x43 C - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x44 D - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x45 E - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x46 F - CHAR_QUERY | CHAR_USERINFO, // 0x47 G - CHAR_QUERY | CHAR_USERINFO, // 0x48 H - CHAR_QUERY | CHAR_USERINFO, // 0x49 I - CHAR_QUERY | CHAR_USERINFO, // 0x4a J - CHAR_QUERY | CHAR_USERINFO, // 0x4b K - CHAR_QUERY | CHAR_USERINFO, // 0x4c L - CHAR_QUERY | CHAR_USERINFO, // 0x4d M - CHAR_QUERY | CHAR_USERINFO, // 0x4e N - CHAR_QUERY | CHAR_USERINFO, // 0x4f O - CHAR_QUERY | CHAR_USERINFO, // 0x50 P - CHAR_QUERY | CHAR_USERINFO, // 0x51 Q - CHAR_QUERY | CHAR_USERINFO, // 0x52 R - CHAR_QUERY | CHAR_USERINFO, // 0x53 S - CHAR_QUERY | CHAR_USERINFO, // 0x54 T - CHAR_QUERY | CHAR_USERINFO, // 0x55 U - CHAR_QUERY | CHAR_USERINFO, // 0x56 V - CHAR_QUERY | CHAR_USERINFO, // 0x57 W - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4, // 0x58 X - CHAR_QUERY | CHAR_USERINFO, // 0x59 Y - CHAR_QUERY | CHAR_USERINFO, // 0x5a Z + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x41 A + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x42 B + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x43 C + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x44 D + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x45 E + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x46 F + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x47 G + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x48 H + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x49 I + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x4a J + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x4b K + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x4c L + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x4d M + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x4e N + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x4f O + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x50 P + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x51 Q + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x52 R + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x53 S + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x54 T + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x55 U + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x56 V + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x57 W + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_COMPONENT, // 0x58 X + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x59 Y + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x5a Z CHAR_QUERY, // 0x5b [ CHAR_QUERY, // 0x5c '\' CHAR_QUERY, // 0x5d ] CHAR_QUERY, // 0x5e ^ - CHAR_QUERY | CHAR_USERINFO, // 0x5f _ + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x5f _ CHAR_QUERY, // 0x60 ` - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x61 a - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x62 b - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x63 c - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x64 d - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x65 e - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX, // 0x66 f - CHAR_QUERY | CHAR_USERINFO, // 0x67 g - CHAR_QUERY | CHAR_USERINFO, // 0x68 h - CHAR_QUERY | CHAR_USERINFO, // 0x69 i - CHAR_QUERY | CHAR_USERINFO, // 0x6a j - CHAR_QUERY | CHAR_USERINFO, // 0x6b k - CHAR_QUERY | CHAR_USERINFO, // 0x6c l - CHAR_QUERY | CHAR_USERINFO, // 0x6d m - CHAR_QUERY | CHAR_USERINFO, // 0x6e n - CHAR_QUERY | CHAR_USERINFO, // 0x6f o - CHAR_QUERY | CHAR_USERINFO, // 0x70 p - CHAR_QUERY | CHAR_USERINFO, // 0x71 q - CHAR_QUERY | CHAR_USERINFO, // 0x72 r - CHAR_QUERY | CHAR_USERINFO, // 0x73 s - CHAR_QUERY | CHAR_USERINFO, // 0x74 t - CHAR_QUERY | CHAR_USERINFO, // 0x75 u - CHAR_QUERY | CHAR_USERINFO, // 0x76 v - CHAR_QUERY | CHAR_USERINFO, // 0x77 w - CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4, // 0x78 x - CHAR_QUERY | CHAR_USERINFO, // 0x79 y - CHAR_QUERY | CHAR_USERINFO, // 0x7a z + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x61 a + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x62 b + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x63 c + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x64 d + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x65 e + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_HEX | CHAR_COMPONENT, // 0x66 f + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x67 g + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x68 h + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x69 i + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x6a j + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x6b k + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x6c l + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x6d m + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x6e n + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x6f o + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x70 p + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x71 q + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x72 r + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x73 s + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x74 t + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x75 u + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x76 v + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x77 w + CHAR_QUERY | CHAR_USERINFO | CHAR_IPV4 | CHAR_COMPONENT, // 0x78 x + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x79 y + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x7a z CHAR_QUERY, // 0x7b { CHAR_QUERY, // 0x7c | CHAR_QUERY, // 0x7d } - CHAR_QUERY | CHAR_USERINFO, // 0x7e ~ + CHAR_QUERY | CHAR_USERINFO | CHAR_COMPONENT, // 0x7e ~ 0, // 0x7f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x80 - 0x8f 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f @@ -255,7 +232,7 @@ 0, // 0xE0 - 0xFF }; -const char16 kUnicodeReplacementCharacter = 0xfffd; +const base::char16 kUnicodeReplacementCharacter = 0xfffd; void AppendStringOfType(const char* source, int length, SharedCharTypes type, @@ -263,10 +240,37 @@ DoAppendStringOfType<char, unsigned char>(source, length, type, output); } -void AppendStringOfType(const char16* source, int length, +void AppendStringOfType(const base::char16* source, int length, SharedCharTypes type, CanonOutput* output) { - DoAppendStringOfType<char16, char16>(source, length, type, output); + DoAppendStringOfType<base::char16, base::char16>( + source, length, type, output); +} + +bool ReadUTFChar(const char* str, int* begin, int length, + unsigned* code_point_out) { + // This depends on ints and int32s being the same thing. If they're not, it + // will fail to compile. + // TODO(mmenke): This should probably be fixed. + if (!base::ReadUnicodeCharacter(str, length, begin, code_point_out) || + !base::IsValidCharacter(*code_point_out)) { + *code_point_out = kUnicodeReplacementCharacter; + return false; + } + return true; +} + +bool ReadUTFChar(const base::char16* str, int* begin, int length, + unsigned* code_point_out) { + // This depends on ints and int32s being the same thing. If they're not, it + // will fail to compile. + // TODO(mmenke): This should probably be fixed. + if (!base::ReadUnicodeCharacter(str, length, begin, code_point_out) || + !base::IsValidCharacter(*code_point_out)) { + *code_point_out = kUnicodeReplacementCharacter; + return false; + } + return true; } void AppendInvalidNarrowString(const char* spec, int begin, int end, @@ -274,12 +278,13 @@ DoAppendInvalidNarrowString<char, unsigned char>(spec, begin, end, output); } -void AppendInvalidNarrowString(const char16* spec, int begin, int end, +void AppendInvalidNarrowString(const base::char16* spec, int begin, int end, CanonOutput* output) { - DoAppendInvalidNarrowString<char16, char16>(spec, begin, end, output); + DoAppendInvalidNarrowString<base::char16, base::char16>( + spec, begin, end, output); } -bool ConvertUTF16ToUTF8(const char16* input, int input_len, +bool ConvertUTF16ToUTF8(const base::char16* input, int input_len, CanonOutput* output) { bool success = true; for (int i = 0; i < input_len; i++) { @@ -291,7 +296,7 @@ } bool ConvertUTF8ToUTF16(const char* input, int input_len, - CanonOutputT<char16>* output) { + CanonOutputT<base::char16>* output) { bool success = true; for (int i = 0; i < input_len; i++) { unsigned code_point; @@ -304,10 +309,10 @@ void SetupOverrideComponents(const char* base, const Replacements<char>& repl, URLComponentSource<char>* source, - url_parse::Parsed* parsed) { + Parsed* parsed) { // Get the source and parsed structures of the things we are replacing. const URLComponentSource<char>& repl_source = repl.sources(); - const url_parse::Parsed& repl_parsed = repl.components(); + const Parsed& repl_parsed = repl.components(); DoOverrideComponent(repl_source.scheme, repl_parsed.scheme, &source->scheme, &parsed->scheme); @@ -333,15 +338,15 @@ } bool SetupUTF16OverrideComponents(const char* base, - const Replacements<char16>& repl, + const Replacements<base::char16>& repl, CanonOutput* utf8_buffer, URLComponentSource<char>* source, - url_parse::Parsed* parsed) { + Parsed* parsed) { bool success = true; // Get the source and parsed structures of the things we are replacing. - const URLComponentSource<char16>& repl_source = repl.sources(); - const url_parse::Parsed& repl_parsed = repl.components(); + const URLComponentSource<base::char16>& repl_source = repl.sources(); + const Parsed& repl_parsed = repl.components(); success &= PrepareUTF16OverrideComponent( repl_source.scheme, repl_parsed.scheme, @@ -402,7 +407,7 @@ return 0; } -int _itow_s(int value, char16* buffer, size_t size_in_chars, int radix) { +int _itow_s(int value, base::char16* buffer, size_t size_in_chars, int radix) { if (radix != 10) return EINVAL; @@ -416,7 +421,7 @@ } for (int i = 0; i < written; ++i) { - buffer[i] = static_cast<char16>(temp[i]); + buffer[i] = static_cast<base::char16>(temp[i]); } buffer[written] = '\0'; return 0; @@ -424,4 +429,4 @@ #endif // !WIN32 -} // namespace url_canon +} // namespace url
diff --git a/googleurl/src/url_canon_internal.h b/src/url/url_canon_internal.h similarity index 77% rename from googleurl/src/url_canon_internal.h rename to src/url/url_canon_internal.h index 4b1e45a..a66cd8d 100644 --- a/googleurl/src/url_canon_internal.h +++ b/src/url/url_canon_internal.h
@@ -1,45 +1,21 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_CANON_INTERNAL_H_ +#define URL_URL_CANON_INTERNAL_H_ // This file is intended to be included in another C++ file where the character // types are defined. This allows us to write mostly generic code, but not have // templace bloat because everything is inlined when anybody calls any of our // functions. -#ifndef GOOGLEURL_SRC_URL_CANON_INTERNAL_H__ -#define GOOGLEURL_SRC_URL_CANON_INTERNAL_H__ - #include <stdlib.h> -#include "googleurl/src/url_canon.h" +#include "base/logging.h" +#include "url/url_canon.h" -namespace url_canon { +namespace url { // Character type handling ----------------------------------------------------- @@ -47,7 +23,7 @@ // bits that are set for each 8-bit character in the kSharedCharTypeTable. enum SharedCharTypes { // Characters that do not require escaping in queries. Characters that do - // not have this flag will be escaped, see url_canon_query.cc + // not have this flag will be escaped; see url_canon_query.cc CHAR_QUERY = 1, // Valid in the username/password field. @@ -64,6 +40,10 @@ // Valid in an ASCII-representation of an octal digit. CHAR_OCT = 32, + + // Characters that do not require escaping in encodeURIComponent. Characters + // that do not have this flag will be escaped; see url_util.cc. + CHAR_COMPONENT = 64, }; // This table contains the flags in SharedCharTypes for each 8-bit character. @@ -88,19 +68,22 @@ inline bool IsHexChar(unsigned char c) { return IsCharOfType(c, CHAR_HEX); } +inline bool IsComponentChar(unsigned char c) { + return IsCharOfType(c, CHAR_COMPONENT); +} // Appends the given string to the output, escaping characters that do not // match the given |type| in SharedCharTypes. void AppendStringOfType(const char* source, int length, SharedCharTypes type, CanonOutput* output); -void AppendStringOfType(const char16* source, int length, +void AppendStringOfType(const base::char16* source, int length, SharedCharTypes type, CanonOutput* output); // Maps the hex numerical values 0x0 to 0xf to the corresponding ASCII digit // that will be used to represent it. -extern const char kHexCharLookup[0x10]; +URL_EXPORT extern const char kHexCharLookup[0x10]; // This lookup table allows fast conversion between ASCII hex letters and their // corresponding numerical value. The 8-bit range is divided up into 8 @@ -138,7 +121,7 @@ // required for relative URL resolving to test for scheme equality. // // Returns 0 if the input character is not a valid scheme character. -char CanonicalSchemeChar(char16 ch); +char CanonicalSchemeChar(base::char16 ch); // Write a single character, escaped, to the output. This always escapes: it // does no checking that thee character requires escaping. @@ -148,12 +131,12 @@ inline void AppendEscapedChar(UINCHAR ch, CanonOutputT<OUTCHAR>* output) { output->push_back('%'); - output->push_back(kHexCharLookup[ch >> 4]); + output->push_back(kHexCharLookup[(ch >> 4) & 0xf]); output->push_back(kHexCharLookup[ch & 0xf]); } // The character we'll substitute for undecodable or invalid characters. -extern const char16 kUnicodeReplacementCharacter; +extern const base::char16 kUnicodeReplacementCharacter; // UTF-8 functions ------------------------------------------------------------ @@ -165,14 +148,15 @@ // |*begin| will be updated to point to the last character consumed so it // can be incremented in a loop and will be ready for the next character. // (for a single-byte ASCII character, it will not be changed). -// -// Implementation is in url_canon_icu.cc. -bool ReadUTFChar(const char* str, int* begin, int length, - unsigned* code_point_out); +URL_EXPORT bool ReadUTFChar(const char* str, int* begin, int length, + unsigned* code_point_out); // Generic To-UTF-8 converter. This will call the given append method for each // character that should be appended, with the given output method. Wrappers // are provided below for escaped and non-escaped versions of this. +// +// The char_value must have already been checked that it's a valid Unicode +// character. template<class Output, void Appender(unsigned char, Output*)> inline void DoAppendUTF8(unsigned char_value, Output* output) { if (char_value <= 0x7f) { @@ -191,7 +175,7 @@ output); Appender(static_cast<unsigned char>(0x80 | (char_value & 0x3f)), output); - } else if (char_value <= 0x1fffff) { + } else if (char_value <= 0x10FFFF) { // Max unicode code point. // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx Appender(static_cast<unsigned char>(0xf0 | (char_value >> 18)), output); @@ -201,20 +185,9 @@ output); Appender(static_cast<unsigned char>(0x80 | (char_value & 0x3f)), output); - } else if (char_value <= 0x10FFFF) { // Max unicode code point. - // 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - Appender(static_cast<unsigned char>(0xf8 | (char_value >> 24)), - output); - Appender(static_cast<unsigned char>(0x80 | ((char_value >> 18) & 0x3f)), - output); - Appender(static_cast<unsigned char>(0x80 | ((char_value >> 12) & 0x3f)), - output); - Appender(static_cast<unsigned char>(0x80 | ((char_value >> 6) & 0x3f)), - output); - Appender(static_cast<unsigned char>(0x80 | (char_value & 0x3f)), - output); } else { - // Invalid UTF-8 character (>20 bits) + // Invalid UTF-8 character (>20 bits). + DCHECK(false); // NOTREACHED(); } } @@ -250,19 +223,17 @@ // |*begin| will be updated to point to the last character consumed so it // can be incremented in a loop and will be ready for the next character. // (for a single-16-bit-word character, it will not be changed). -// -// Implementation is in url_canon_icu.cc. -bool ReadUTFChar(const char16* str, int* begin, int length, - unsigned* code_point); +URL_EXPORT bool ReadUTFChar(const base::char16* str, int* begin, int length, + unsigned* code_point_out); // Equivalent to U16_APPEND_UNSAFE in ICU but uses our output method. inline void AppendUTF16Value(unsigned code_point, - CanonOutputT<char16>* output) { + CanonOutputT<base::char16>* output) { if (code_point > 0xffff) { - output->push_back(static_cast<char16>((code_point >> 10) + 0xd7c0)); - output->push_back(static_cast<char16>((code_point & 0x3ff) | 0xdc00)); + output->push_back(static_cast<base::char16>((code_point >> 10) + 0xd7c0)); + output->push_back(static_cast<base::char16>((code_point & 0x3ff) | 0xdc00)); } else { - output->push_back(static_cast<char16>(code_point)); + output->push_back(static_cast<base::char16>(code_point)); } } @@ -287,8 +258,8 @@ // // Assumes that ch[begin] is within range in the array, but does not assume // that any following characters are. -inline bool AppendUTF8EscapedChar(const char16* str, int* begin, int length, - CanonOutput* output) { +inline bool AppendUTF8EscapedChar(const base::char16* str, int* begin, + int length, CanonOutput* output) { // UTF-16 input. Readchar16 will handle invalid characters for us and give // us the kUnicodeReplacementCharacter, so we don't have to do special // checking after failure, just pass through the failure to the caller. @@ -322,7 +293,7 @@ inline bool Is8BitChar(char c) { return true; // this case is specialized to avoid a warning } -inline bool Is8BitChar(char16 c) { +inline bool Is8BitChar(base::char16 c) { return c <= 255; } @@ -358,7 +329,7 @@ // the escaping rules are not guaranteed! void AppendInvalidNarrowString(const char* spec, int begin, int end, CanonOutput* output); -void AppendInvalidNarrowString(const char16* spec, int begin, int end, +void AppendInvalidNarrowString(const base::char16* spec, int begin, int end, CanonOutput* output); // Misc canonicalization helpers ---------------------------------------------- @@ -371,15 +342,15 @@ // replacing the invalid characters with the "invalid character". It will // return false in the failure case, and the caller should not continue as // normal. -bool ConvertUTF16ToUTF8(const char16* input, int input_len, - CanonOutput* output); -bool ConvertUTF8ToUTF16(const char* input, int input_len, - CanonOutputT<char16>* output); +URL_EXPORT bool ConvertUTF16ToUTF8(const base::char16* input, int input_len, + CanonOutput* output); +URL_EXPORT bool ConvertUTF8ToUTF16(const char* input, int input_len, + CanonOutputT<base::char16>* output); // Converts from UTF-16 to 8-bit using the character set converter. If the // converter is NULL, this will use UTF-8. -void ConvertUTF16ToQueryEncoding(const char16* input, - const url_parse::Component& query, +void ConvertUTF16ToQueryEncoding(const base::char16* input, + const Component& query, CharsetConverter* converter, CanonOutput* output); @@ -395,7 +366,7 @@ void SetupOverrideComponents(const char* base, const Replacements<char>& repl, URLComponentSource<char>* source, - url_parse::Parsed* parsed); + Parsed* parsed); // Like the above 8-bit version, except that it additionally converts the // UTF-16 input to UTF-8 before doing the overrides. @@ -410,31 +381,33 @@ // |source| will point into this buffer, which could be invalidated if // additional data is added and the CanonOutput resizes its buffer. // -// Returns true on success. Fales means that the input was not valid UTF-16, +// Returns true on success. False means that the input was not valid UTF-16, // although we will have still done the override with "invalid characters" in // place of errors. bool SetupUTF16OverrideComponents(const char* base, - const Replacements<char16>& repl, + const Replacements<base::char16>& repl, CanonOutput* utf8_buffer, URLComponentSource<char>* source, - url_parse::Parsed* parsed); + Parsed* parsed); // Implemented in url_canon_path.cc, these are required by the relative URL // resolver as well, so we declare them here. bool CanonicalizePartialPath(const char* spec, - const url_parse::Component& path, + const Component& path, int path_begin_in_output, CanonOutput* output); -bool CanonicalizePartialPath(const char16* spec, - const url_parse::Component& path, +bool CanonicalizePartialPath(const base::char16* spec, + const Component& path, int path_begin_in_output, CanonOutput* output); #ifndef WIN32 // Implementations of Windows' int-to-string conversions -int _itoa_s(int value, char* buffer, size_t size_in_chars, int radix); -int _itow_s(int value, char16* buffer, size_t size_in_chars, int radix); +URL_EXPORT int _itoa_s(int value, char* buffer, size_t size_in_chars, + int radix); +URL_EXPORT int _itow_s(int value, base::char16* buffer, size_t size_in_chars, + int radix); // Secure template overloads for these functions template<size_t N> @@ -443,7 +416,7 @@ } template<size_t N> -inline int _itow_s(int value, char16 (&buffer)[N], int radix) { +inline int _itow_s(int value, base::char16 (&buffer)[N], int radix) { return _itow_s(value, buffer, N, radix); } @@ -455,6 +428,6 @@ #endif // WIN32 -} // namespace url_canon +} // namespace url -#endif // GOOGLEURL_SRC_URL_CANON_INTERNAL_H__ +#endif // URL_URL_CANON_INTERNAL_H_
diff --git a/googleurl/src/url_canon_internal_file.h b/src/url/url_canon_internal_file.h similarity index 70% rename from googleurl/src/url_canon_internal_file.h rename to src/url/url_canon_internal_file.h index 63a9c5b..6903098 100644 --- a/googleurl/src/url_canon_internal_file.h +++ b/src/url/url_canon_internal_file.h
@@ -1,31 +1,9 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_CANON_INTERNAL_FILE_H_ +#define URL_URL_CANON_INTERNAL_FILE_H_ // As with url_canon_internal.h, this file is intended to be included in // another C++ file where the template types are defined. This allows the @@ -36,13 +14,11 @@ // *** This file must be included after url_canon_internal as we depend on some // functions in it. *** -#ifndef GOOGLEURL_SRC_URL_CANON_INTERNAL_FILE_H__ -#define GOOGLEURL_SRC_URL_CANON_INTERNAL_FILE_H__ -#include "googleurl/src/url_file.h" -#include "googleurl/src/url_parse_internal.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" -using namespace url_canon; +namespace url { // Given a pointer into the spec, this copies and canonicalizes the drive // letter and colon to the output, if one is found. If there is not a drive @@ -90,11 +66,11 @@ // path. We supply it with the path following the slashes. It won't prepend // a slash because it assumes any nonempty path already starts with one. // We explicitly filter out calls with no path here to prevent that case. - ParsedURL::Component sub_path(after_slashes, end - after_slashes); + ParsedComponent sub_path(after_slashes, end - after_slashes); if (sub_path.len > 0) { // Give it a fake output component to write into. DoCanonicalizeFile will // compute the full path component. - ParsedURL::Component fake_output_path; + ParsedComponent fake_output_path; URLCanonInternal<CHAR, UCHAR>::DoPath( spec, sub_path, output, &fake_output_path); } @@ -106,9 +82,9 @@ CanonOutput* output, ParsedURL* new_parsed) { // Things we don't set in file: URLs. - new_parsed->username = ParsedURL::Component(0, -1); - new_parsed->password = ParsedURL::Component(0, -1); - new_parsed->port = ParsedURL::Component(0, -1); + new_parsed->username = ParsedComponent(0, -1); + new_parsed->password = ParsedComponent(0, -1); + new_parsed->port = ParsedComponent(0, -1); // Scheme (known, so we don't bother running it through the more // complicated scheme canonicalizer). @@ -154,4 +130,6 @@ return success; } -#endif // GOOGLEURL_SRC_URL_CANON_INTERNAL_FILE_H__ +} // namespace url + +#endif // URL_URL_CANON_INTERNAL_FILE_H_
diff --git a/googleurl/src/url_canon_ip.cc b/src/url/url_canon_ip.cc similarity index 78% rename from googleurl/src/url_canon_ip.cc rename to src/url/url_canon_ip.cc index 86f7c9c..45f95de 100644 --- a/googleurl/src/url_canon_ip.cc +++ b/src/url/url_canon_ip.cc
@@ -1,41 +1,16 @@ -// Copyright 2009, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#include "googleurl/src/url_canon_ip.h" +#include "url/url_canon_ip.h" #include <stdlib.h> #include "base/basictypes.h" #include "base/logging.h" -#include "googleurl/src/url_canon_internal.h" +#include "url/url_canon_internal.h" -namespace url_canon { +namespace url { namespace { @@ -56,8 +31,8 @@ template<typename CHAR, typename UCHAR> bool DoFindIPv4Components(const CHAR* spec, - const url_parse::Component& host, - url_parse::Component components[4]) { + const Component& host, + Component components[4]) { if (!host.is_nonempty()) return false; @@ -68,8 +43,7 @@ if (i >= end || spec[i] == '.') { // Found the end of the current component. int component_len = i - cur_component_begin; - components[cur_component] = - url_parse::Component(cur_component_begin, component_len); + components[cur_component] = Component(cur_component_begin, component_len); // The next component starts after the dot. cur_component_begin = i + 1; @@ -101,7 +75,7 @@ // Fill in any unused components. while (cur_component < 4) - components[cur_component++] = url_parse::Component(); + components[cur_component++] = Component(); return true; } @@ -116,10 +90,9 @@ // out any input that is greater than 7 bits. The components are assumed // to be non-empty. template<typename CHAR> -CanonHostInfo::Family IPv4ComponentToNumber( - const CHAR* spec, - const url_parse::Component& component, - uint32* number) { +CanonHostInfo::Family IPv4ComponentToNumber(const CHAR* spec, + const Component& component, + uint32* number) { // Figure out the base SharedCharTypes base; int base_prefix_len = 0; // Size of the prefix for this base. @@ -181,34 +154,14 @@ return CanonHostInfo::IPV4; } -// Writes the given address (with each character representing one dotted -// part of an IPv4 address) to the output, and updating |*out_host| to -// identify the added portion. -void AppendIPv4Address(const unsigned char address[4], - CanonOutput* output, - url_parse::Component* out_host) { - out_host->begin = output->length(); - for (int i = 0; i < 4; i++) { - char str[16]; - _itoa_s(address[i], str, 10); - - for (int ch = 0; str[ch] != 0; ch++) - output->push_back(str[ch]); - - if (i != 3) - output->push_back('.'); - } - out_host->len = output->length() - out_host->begin; -} - // See declaration of IPv4AddressToNumber for documentation. template<typename CHAR> CanonHostInfo::Family DoIPv4AddressToNumber(const CHAR* spec, - const url_parse::Component& host, + const Component& host, unsigned char address[4], int* num_ipv4_components) { // The identified components. Not all may exist. - url_parse::Component components[4]; + Component components[4]; if (!FindIPv4Components(spec, host, components)) return CanonHostInfo::NEUTRAL; @@ -216,19 +169,30 @@ // |existing_components| will be valid. uint32 component_values[4]; int existing_components = 0; + + // Set to true if one or more components are BROKEN. BROKEN is only + // returned if all components are IPV4 or BROKEN, so, for example, + // 12345678912345.de returns NEUTRAL rather than broken. + bool broken = false; for (int i = 0; i < 4; i++) { if (components[i].len <= 0) continue; CanonHostInfo::Family family = IPv4ComponentToNumber( spec, components[i], &component_values[existing_components]); - // Stop if we hit an invalid non-empty component. - if (family != CanonHostInfo::IPV4) + if (family == CanonHostInfo::BROKEN) { + broken = true; + } else if (family != CanonHostInfo::IPV4) { + // Stop if we hit a non-BROKEN invalid non-empty component. return family; + } existing_components++; } + if (broken) + return CanonHostInfo::BROKEN; + // Use that sequence of numbers to fill out the 4-component IP address. // First, process all components but the last, while making sure each fits @@ -240,7 +204,15 @@ } // Next, consume the last component to fill in the remaining bytes. + // Work around a gcc 4.9 bug. crbug.com/392872 +#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || __GNUC__ > 4) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Warray-bounds" +#endif uint32 last_value = component_values[existing_components - 1]; +#if ((__GNUC__ == 4 && __GNUC_MINOR__ >= 9) || __GNUC__ > 4) +#pragma GCC diagnostic pop +#endif for (int i = 3; i >= existing_components - 1; i--) { address[i] = static_cast<unsigned char>(last_value); last_value >>= 8; @@ -261,17 +233,18 @@ // is NEUTRAL, and we could use a second opinion. template<typename CHAR, typename UCHAR> bool DoCanonicalizeIPv4Address(const CHAR* spec, - const url_parse::Component& host, + const Component& host, CanonOutput* output, CanonHostInfo* host_info) { - unsigned char address[4]; host_info->family = IPv4AddressToNumber( - spec, host, address, &host_info->num_ipv4_components); + spec, host, host_info->address, &host_info->num_ipv4_components); switch (host_info->family) { case CanonHostInfo::IPV4: // Definitely an IPv4 address. - AppendIPv4Address(address, output, &host_info->out_host); + host_info->out_host.begin = output->length(); + AppendIPv4Address(host_info->address, output); + host_info->out_host.len = output->length() - host_info->out_host.begin; return true; case CanonHostInfo::BROKEN: // Definitely broken. @@ -328,7 +301,7 @@ } // There can be up to 8 hex components (colon separated) in the literal. - url_parse::Component hex_components[8]; + Component hex_components[8]; // The count of hex components present. Ranges from [0,8]. int num_hex_components; @@ -338,16 +311,14 @@ int index_of_contraction; // The range of characters which are an IPv4 literal. - url_parse::Component ipv4_component; + Component ipv4_component; }; // Parse the IPv6 input string. If parsing succeeded returns true and fills // |parsed| with the information. If parsing failed (because the input is // invalid) returns false. template<typename CHAR, typename UCHAR> -bool DoParseIPv6(const CHAR* spec, - const url_parse::Component& host, - IPv6Parsed* parsed) { +bool DoParseIPv6(const CHAR* spec, const Component& host, IPv6Parsed* parsed) { // Zero-out the info. parsed->reset(); @@ -392,7 +363,7 @@ return false; parsed->hex_components[parsed->num_hex_components++] = - url_parse::Component(cur_component_begin, component_len); + Component(cur_component_begin, component_len); } } @@ -423,8 +394,8 @@ // Since IPv4 address can only appear at the end, assume the rest // of the string is an IPv4 address. (We will parse this separately // later). - parsed->ipv4_component = url_parse::Component( - cur_component_begin, end - cur_component_begin); + parsed->ipv4_component = + Component(cur_component_begin, end - cur_component_begin); break; } else { // The character was neither a hex digit, nor an IPv4 character. @@ -473,8 +444,7 @@ // already verified that each character in the string was a hex digit, and // that there were no more than 4 characters. template<typename CHAR> -uint16 IPv6HexComponentToNumber(const CHAR* spec, - const url_parse::Component& component) { +uint16 IPv6HexComponentToNumber(const CHAR* spec, const Component& component) { DCHECK(component.len <= 4); // Copy the hex string into a C-string. @@ -492,7 +462,7 @@ // true on success. False means that the input was not a valid IPv6 address. template<typename CHAR, typename UCHAR> bool DoIPv6AddressToNumber(const CHAR* spec, - const url_parse::Component& host, + const Component& host, unsigned char address[16]) { // Make sure the component is bounded by '[' and ']'. int end = host.end(); @@ -500,7 +470,7 @@ return false; // Exclude the square brackets. - url_parse::Component ipv6_comp(host.begin + 1, host.len - 2); + Component ipv6_comp(host.begin + 1, host.len - 2); // Parse the IPv6 address -- identify where all the colon separated hex // components are, the "::" contraction, and the embedded IPv4 address. @@ -538,18 +508,6 @@ // If there was an IPv4 section, convert it into a 32-bit number and append // it to |address|. if (ipv6_parsed.ipv4_component.is_valid()) { - // We only allow the embedded IPv4 syntax to be used for "compat" and - // "mapped" formats: - // "mapped" ==> 0:0:0:0:0:ffff:<IPv4-literal> - // "compat" ==> 0:0:0:0:0:0000:<IPv4-literal> - for (int j = 0; j < 10; ++j) { - if (address[j] != 0) - return false; - } - if (!((address[10] == 0 && address[11] == 0) || - (address[10] == 0xFF && address[11] == 0xFF))) - return false; - // Append the 32-bit number to |address|. int ignored_num_ipv4_components; if (CanonHostInfo::IPV4 != @@ -567,12 +525,12 @@ // range into |contraction_range|. The run of zeros must be at least 16 bits, // and if there is a tie the first is chosen. void ChooseIPv6ContractionRange(const unsigned char address[16], - url_parse::Component* contraction_range) { + Component* contraction_range) { // The longest run of zeros in |address| seen so far. - url_parse::Component max_range; + Component max_range; // The current run of zeros in |address| being iterated over. - url_parse::Component cur_range; + Component cur_range; for (int i = 0; i < 16; i += 2) { // Test for 16 bits worth of zero. @@ -581,7 +539,7 @@ if (is_zero) { // Add the zero to the current range (or start a new one). if (!cur_range.is_valid()) - cur_range = url_parse::Component(i, 0); + cur_range = Component(i, 0); cur_range.len += 2; } @@ -601,12 +559,11 @@ // is NEUTRAL, and we could use a second opinion. template<typename CHAR, typename UCHAR> bool DoCanonicalizeIPv6Address(const CHAR* spec, - const url_parse::Component& host, + const Component& host, CanonOutput* output, CanonHostInfo* host_info) { // Turn the IP address into a 128 bit number. - unsigned char address[16]; - if (!IPv6AddressToNumber(spec, host, address)) { + if (!IPv6AddressToNumber(spec, host, host_info->address)) { // If it's not an IPv6 address, scan for characters that should *only* // exist in an IPv6 address. for (int i = host.begin; i < host.end(); i++) { @@ -626,12 +583,35 @@ host_info->out_host.begin = output->length(); output->push_back('['); + AppendIPv6Address(host_info->address, output); + output->push_back(']'); + host_info->out_host.len = output->length() - host_info->out_host.begin; - // We will now output the address according to the rules in: + host_info->family = CanonHostInfo::IPV6; + return true; +} + +} // namespace + +void AppendIPv4Address(const unsigned char address[4], CanonOutput* output) { + for (int i = 0; i < 4; i++) { + char str[16]; + _itoa_s(address[i], str, 10); + + for (int ch = 0; str[ch] != 0; ch++) + output->push_back(str[ch]); + + if (i != 3) + output->push_back('.'); + } +} + +void AppendIPv6Address(const unsigned char address[16], CanonOutput* output) { + // We will output the address according to the rules in: // http://tools.ietf.org/html/draft-kawamura-ipv6-text-representation-01#section-4 // Start by finding where to place the "::" contraction (if any). - url_parse::Component contraction_range; + Component contraction_range; ChooseIPv6ContractionRange(address, &contraction_range); for (int i = 0; i <= 14;) { @@ -660,30 +640,23 @@ output->push_back(':'); } } - - output->push_back(']'); - host_info->out_host.len = output->length() - host_info->out_host.begin; - - host_info->family = CanonHostInfo::IPV6; - return true; } -} // namespace - bool FindIPv4Components(const char* spec, - const url_parse::Component& host, - url_parse::Component components[4]) { + const Component& host, + Component components[4]) { return DoFindIPv4Components<char, unsigned char>(spec, host, components); } -bool FindIPv4Components(const char16* spec, - const url_parse::Component& host, - url_parse::Component components[4]) { - return DoFindIPv4Components<char16, char16>(spec, host, components); +bool FindIPv4Components(const base::char16* spec, + const Component& host, + Component components[4]) { + return DoFindIPv4Components<base::char16, base::char16>( + spec, host, components); } void CanonicalizeIPAddress(const char* spec, - const url_parse::Component& host, + const Component& host, CanonOutput* output, CanonHostInfo* host_info) { if (DoCanonicalizeIPv4Address<char, unsigned char>( @@ -694,44 +667,43 @@ return; } -void CanonicalizeIPAddress(const char16* spec, - const url_parse::Component& host, +void CanonicalizeIPAddress(const base::char16* spec, + const Component& host, CanonOutput* output, CanonHostInfo* host_info) { - if (DoCanonicalizeIPv4Address<char16, char16>( + if (DoCanonicalizeIPv4Address<base::char16, base::char16>( spec, host, output, host_info)) return; - if (DoCanonicalizeIPv6Address<char16, char16>( + if (DoCanonicalizeIPv6Address<base::char16, base::char16>( spec, host, output, host_info)) return; } CanonHostInfo::Family IPv4AddressToNumber(const char* spec, - const url_parse::Component& host, + const Component& host, unsigned char address[4], int* num_ipv4_components) { return DoIPv4AddressToNumber<char>(spec, host, address, num_ipv4_components); } -CanonHostInfo::Family IPv4AddressToNumber(const char16* spec, - const url_parse::Component& host, +CanonHostInfo::Family IPv4AddressToNumber(const base::char16* spec, + const Component& host, unsigned char address[4], int* num_ipv4_components) { - return DoIPv4AddressToNumber<char16>( + return DoIPv4AddressToNumber<base::char16>( spec, host, address, num_ipv4_components); } bool IPv6AddressToNumber(const char* spec, - const url_parse::Component& host, + const Component& host, unsigned char address[16]) { return DoIPv6AddressToNumber<char, unsigned char>(spec, host, address); } -bool IPv6AddressToNumber(const char16* spec, - const url_parse::Component& host, +bool IPv6AddressToNumber(const base::char16* spec, + const Component& host, unsigned char address[16]) { - return DoIPv6AddressToNumber<char16, char16>(spec, host, address); + return DoIPv6AddressToNumber<base::char16, base::char16>(spec, host, address); } - -} // namespace url_canon +} // namespace url
diff --git a/src/url/url_canon_ip.h b/src/url/url_canon_ip.h new file mode 100644 index 0000000..19ecfdb --- /dev/null +++ b/src/url/url_canon_ip.h
@@ -0,0 +1,82 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_CANON_IP_H_ +#define URL_URL_CANON_IP_H_ + +#include "base/strings/string16.h" +#include "url/url_canon.h" +#include "url/url_export.h" +#include "url/url_parse.h" + +namespace url { + +// Writes the given IPv4 address to |output|. +URL_EXPORT void AppendIPv4Address(const unsigned char address[4], + CanonOutput* output); + +// Writes the given IPv6 address to |output|. +URL_EXPORT void AppendIPv6Address(const unsigned char address[16], + CanonOutput* output); + +// Searches the host name for the portions of the IPv4 address. On success, +// each component will be placed into |components| and it will return true. +// It will return false if the host can not be separated as an IPv4 address +// or if there are any non-7-bit characters or other characters that can not +// be in an IP address. (This is important so we fail as early as possible for +// common non-IP hostnames.) +// +// Not all components may exist. If there are only 3 components, for example, +// the last one will have a length of -1 or 0 to indicate it does not exist. +// +// Note that many platform's inet_addr will ignore everything after a space +// in certain curcumstances if the stuff before the space looks like an IP +// address. IE6 is included in this. We do NOT handle this case. In many cases, +// the browser's canonicalization will get run before this which converts +// spaces to %20 (in the case of IE7) or rejects them (in the case of +// Mozilla), so this code path never gets hit. Our host canonicalization will +// notice these spaces and escape them, which will make IP address finding +// fail. This seems like better behavior than stripping after a space. +URL_EXPORT bool FindIPv4Components(const char* spec, + const Component& host, + Component components[4]); +URL_EXPORT bool FindIPv4Components(const base::char16* spec, + const Component& host, + Component components[4]); + +// Converts an IPv4 address to a 32-bit number (network byte order). +// +// Possible return values: +// IPV4 - IPv4 address was successfully parsed. +// BROKEN - Input was formatted like an IPv4 address, but overflow occurred +// during parsing. +// NEUTRAL - Input couldn't possibly be interpreted as an IPv4 address. +// It might be an IPv6 address, or a hostname. +// +// On success, |num_ipv4_components| will be populated with the number of +// components in the IPv4 address. +URL_EXPORT CanonHostInfo::Family IPv4AddressToNumber(const char* spec, + const Component& host, + unsigned char address[4], + int* num_ipv4_components); +URL_EXPORT CanonHostInfo::Family IPv4AddressToNumber(const base::char16* spec, + const Component& host, + unsigned char address[4], + int* num_ipv4_components); + +// Converts an IPv6 address to a 128-bit number (network byte order), returning +// true on success. False means that the input was not a valid IPv6 address. +// +// NOTE that |host| is expected to be surrounded by square brackets. +// i.e. "[::1]" rather than "::1". +URL_EXPORT bool IPv6AddressToNumber(const char* spec, + const Component& host, + unsigned char address[16]); +URL_EXPORT bool IPv6AddressToNumber(const base::char16* spec, + const Component& host, + unsigned char address[16]); + +} // namespace url + +#endif // URL_URL_CANON_IP_H_
diff --git a/src/url/url_canon_mailtourl.cc b/src/url/url_canon_mailtourl.cc new file mode 100644 index 0000000..7c48b95 --- /dev/null +++ b/src/url/url_canon_mailtourl.cc
@@ -0,0 +1,110 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Functions for canonicalizing "mailto:" URLs. + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" + +namespace url { + +namespace { + +template <typename CHAR, typename UCHAR> +bool DoCanonicalizeMailtoURL(const URLComponentSource<CHAR>& source, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + // mailto: only uses {scheme, path, query} -- clear the rest. + new_parsed->username = Component(); + new_parsed->password = Component(); + new_parsed->host = Component(); + new_parsed->port = Component(); + new_parsed->ref = Component(); + + // Scheme (known, so we don't bother running it through the more + // complicated scheme canonicalizer). + new_parsed->scheme.begin = output->length(); + output->Append("mailto:", 7); + new_parsed->scheme.len = 6; + + bool success = true; + + // Path + if (parsed.path.is_valid()) { + new_parsed->path.begin = output->length(); + + // Copy the path using path URL's more lax escaping rules. + // We convert to UTF-8 and escape non-ASCII, but leave all + // ASCII characters alone. + int end = parsed.path.end(); + for (int i = parsed.path.begin; i < end; ++i) { + UCHAR uch = static_cast<UCHAR>(source.path[i]); + if (uch < 0x20 || uch >= 0x80) + success &= AppendUTF8EscapedChar(source.path, &i, end, output); + else + output->push_back(static_cast<char>(uch)); + } + + new_parsed->path.len = output->length() - new_parsed->path.begin; + } else { + // No path at all + new_parsed->path.reset(); + } + + // Query -- always use the default utf8 charset converter. + CanonicalizeQuery(source.query, parsed.query, NULL, + output, &new_parsed->query); + + return success; +} + +} // namespace + +bool CanonicalizeMailtoURL(const char* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeMailtoURL<char, unsigned char>( + URLComponentSource<char>(spec), parsed, output, new_parsed); +} + +bool CanonicalizeMailtoURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizeMailtoURL<base::char16, base::char16>( + URLComponentSource<base::char16>(spec), parsed, output, new_parsed); +} + +bool ReplaceMailtoURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizeMailtoURL<char, unsigned char>( + source, parsed, output, new_parsed); +} + +bool ReplaceMailtoURL(const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizeMailtoURL<char, unsigned char>( + source, parsed, output, new_parsed); +} + +} // namespace url
diff --git a/googleurl/src/url_canon_path.cc b/src/url/url_canon_path.cc similarity index 83% rename from googleurl/src/url_canon_path.cc rename to src/url/url_canon_path.cc index df97aad..ceff689 100644 --- a/googleurl/src/url_canon_path.cc +++ b/src/url/url_canon_path.cc
@@ -1,39 +1,13 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. -// Canonicalization functions for the paths of URLs. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "base/logging.h" -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" -#include "googleurl/src/url_parse_internal.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_parse_internal.h" -namespace url_canon { +namespace url { namespace { @@ -131,7 +105,7 @@ *consumed_len = 0; return DIRECTORY_CUR; } - if (url_parse::IsURLSlash(spec[after_dot])) { + if (IsURLSlash(spec[after_dot])) { // Single dot followed by a slash. *consumed_len = 1; // Consume the slash return DIRECTORY_CUR; @@ -145,7 +119,7 @@ *consumed_len = second_dot_len; return DIRECTORY_UP; } - if (url_parse::IsURLSlash(spec[after_second_dot])) { + if (IsURLSlash(spec[after_second_dot])) { // Double dot followed by a slash. *consumed_len = second_dot_len + 1; return DIRECTORY_UP; @@ -203,7 +177,7 @@ // it would be correct for most systems. template<typename CHAR, typename UCHAR> bool DoPartialPath(const CHAR* spec, - const url_parse::Component& path, + const Component& path, int path_begin_in_output, CanonOutput* output) { int end = path.end(); @@ -321,60 +295,59 @@ template<typename CHAR, typename UCHAR> bool DoPath(const CHAR* spec, - const url_parse::Component& path, + const Component& path, CanonOutput* output, - url_parse::Component* out_path) { + Component* out_path) { bool success = true; + out_path->begin = output->length(); if (path.len > 0) { - out_path->begin = output->length(); - // Write out an initial slash if the input has none. If we just parse a URL // and then canonicalize it, it will of course have a slash already. This // check is for the replacement and relative URL resolving cases of file // URLs. - if (!url_parse::IsURLSlash(spec[path.begin])) + if (!IsURLSlash(spec[path.begin])) output->push_back('/'); success = DoPartialPath<CHAR, UCHAR>(spec, path, out_path->begin, output); - out_path->len = output->length() - out_path->begin; } else { // No input, canonical path is a slash. output->push_back('/'); - *out_path = url_parse::Component(); } + out_path->len = output->length() - out_path->begin; return success; } } // namespace bool CanonicalizePath(const char* spec, - const url_parse::Component& path, + const Component& path, CanonOutput* output, - url_parse::Component* out_path) { + Component* out_path) { return DoPath<char, unsigned char>(spec, path, output, out_path); } -bool CanonicalizePath(const char16* spec, - const url_parse::Component& path, +bool CanonicalizePath(const base::char16* spec, + const Component& path, CanonOutput* output, - url_parse::Component* out_path) { - return DoPath<char16, char16>(spec, path, output, out_path); + Component* out_path) { + return DoPath<base::char16, base::char16>(spec, path, output, out_path); } bool CanonicalizePartialPath(const char* spec, - const url_parse::Component& path, + const Component& path, int path_begin_in_output, CanonOutput* output) { return DoPartialPath<char, unsigned char>(spec, path, path_begin_in_output, output); } -bool CanonicalizePartialPath(const char16* spec, - const url_parse::Component& path, +bool CanonicalizePartialPath(const base::char16* spec, + const Component& path, int path_begin_in_output, CanonOutput* output) { - return DoPartialPath<char16, char16>(spec, path, path_begin_in_output, - output); + return DoPartialPath<base::char16, base::char16>(spec, path, + path_begin_in_output, + output); } -} // namespace url_canon +} // namespace url
diff --git a/src/url/url_canon_pathurl.cc b/src/url/url_canon_pathurl.cc new file mode 100644 index 0000000..0d23ccb --- /dev/null +++ b/src/url/url_canon_pathurl.cc
@@ -0,0 +1,121 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// Functions for canonicalizing "path" URLs. Not to be confused with the path +// of a URL, these are URLs that have no authority section, only a path. For +// example, "javascript:" and "data:". + +#include "url/url_canon.h" +#include "url/url_canon_internal.h" + +namespace url { + +namespace { + +// Canonicalize the given |component| from |source| into |output| and +// |new_component|. If |separator| is non-zero, it is pre-pended to |ouput| +// prior to the canonicalized component; i.e. for the '?' or '#' characters. +template<typename CHAR, typename UCHAR> +bool DoCanonicalizePathComponent(const CHAR* source, + const Component& component, + char separator, + CanonOutput* output, + Component* new_component) { + bool success = true; + if (component.is_valid()) { + if (separator) + output->push_back(separator); + // Copy the path using path URL's more lax escaping rules (think for + // javascript:). We convert to UTF-8 and escape non-ASCII, but leave all + // ASCII characters alone. This helps readability of JavaStript. + new_component->begin = output->length(); + int end = component.end(); + for (int i = component.begin; i < end; i++) { + UCHAR uch = static_cast<UCHAR>(source[i]); + if (uch < 0x20 || uch >= 0x80) + success &= AppendUTF8EscapedChar(source, &i, end, output); + else + output->push_back(static_cast<char>(uch)); + } + new_component->len = output->length() - new_component->begin; + } else { + // Empty part. + new_component->reset(); + } + return success; +} + +template <typename CHAR, typename UCHAR> +bool DoCanonicalizePathURL(const URLComponentSource<CHAR>& source, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + // Scheme: this will append the colon. + bool success = CanonicalizeScheme(source.scheme, parsed.scheme, + output, &new_parsed->scheme); + + // We assume there's no authority for path URLs. Note that hosts should never + // have -1 length. + new_parsed->username.reset(); + new_parsed->password.reset(); + new_parsed->host.reset(); + new_parsed->port.reset(); + // We allow path URLs to have the path, query and fragment components, but we + // will canonicalize each of the via the weaker path URL rules. + success &= DoCanonicalizePathComponent<CHAR, UCHAR>( + source.path, parsed.path, '\0', output, &new_parsed->path); + success &= DoCanonicalizePathComponent<CHAR, UCHAR>( + source.query, parsed.query, '?', output, &new_parsed->query); + success &= DoCanonicalizePathComponent<CHAR, UCHAR>( + source.ref, parsed.ref, '#', output, &new_parsed->ref); + + return success; +} + +} // namespace + +bool CanonicalizePathURL(const char* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizePathURL<char, unsigned char>( + URLComponentSource<char>(spec), parsed, output, new_parsed); +} + +bool CanonicalizePathURL(const base::char16* spec, + int spec_len, + const Parsed& parsed, + CanonOutput* output, + Parsed* new_parsed) { + return DoCanonicalizePathURL<base::char16, base::char16>( + URLComponentSource<base::char16>(spec), parsed, output, new_parsed); +} + +bool ReplacePathURL(const char* base, + const Parsed& base_parsed, + const Replacements<char>& replacements, + CanonOutput* output, + Parsed* new_parsed) { + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupOverrideComponents(base, replacements, &source, &parsed); + return DoCanonicalizePathURL<char, unsigned char>( + source, parsed, output, new_parsed); +} + +bool ReplacePathURL(const char* base, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, + CanonOutput* output, + Parsed* new_parsed) { + RawCanonOutput<1024> utf8; + URLComponentSource<char> source(base); + Parsed parsed(base_parsed); + SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); + return DoCanonicalizePathURL<char, unsigned char>( + source, parsed, output, new_parsed); +} + +} // namespace url
diff --git a/googleurl/src/url_canon_query.cc b/src/url/url_canon_query.cc similarity index 66% rename from googleurl/src/url_canon_query.cc rename to src/url/url_canon_query.cc index cee8774..5494ddf 100644 --- a/googleurl/src/url_canon_query.cc +++ b/src/url/url_canon_query.cc
@@ -1,34 +1,9 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" // Query canonicalization in IE // ---------------------------- @@ -60,14 +35,14 @@ // replace all invalid sequences (including invalid UTF-16 sequences, which IE // doesn't) with the "invalid character," and we will escape it. -namespace url_canon { +namespace url { namespace { // Returns true if the characters starting at |begin| and going until |end| // (non-inclusive) are all representable in 7-bits. template<typename CHAR, typename UCHAR> -bool IsAllASCII(const CHAR* spec, const url_parse::Component& query) { +bool IsAllASCII(const CHAR* spec, const Component& query) { int end = query.end(); for (int i = query.begin; i < end; i++) { if (static_cast<UCHAR>(spec[i]) >= 0x80) @@ -94,7 +69,7 @@ // Runs the converter on the given UTF-8 input. Since the converter expects // UTF-16, we have to convert first. The converter must be non-NULL. void RunConverter(const char* spec, - const url_parse::Component& query, + const Component& query, CharsetConverter* converter, CanonOutput* output) { // This function will replace any misencoded values with the invalid @@ -107,8 +82,8 @@ // Runs the converter with the given UTF-16 input. We don't have to do // anything, but this overriddden function allows us to use the same code // for both UTF-8 and UTF-16 input. -void RunConverter(const char16* spec, - const url_parse::Component& query, +void RunConverter(const base::char16* spec, + const Component& query, CharsetConverter* converter, CanonOutput* output) { converter->ConvertFromUTF16(&spec[query.begin], query.len, output); @@ -116,7 +91,7 @@ template<typename CHAR, typename UCHAR> void DoConvertToQueryEncoding(const CHAR* spec, - const url_parse::Component& query, + const Component& query, CharsetConverter* converter, CanonOutput* output) { if (IsAllASCII<CHAR, UCHAR>(spec, query)) { @@ -141,12 +116,12 @@ template<typename CHAR, typename UCHAR> void DoCanonicalizeQuery(const CHAR* spec, - const url_parse::Component& query, + const Component& query, CharsetConverter* converter, CanonOutput* output, - url_parse::Component* out_query) { + Component* out_query) { if (query.len < 0) { - *out_query = url_parse::Component(); + *out_query = Component(); return; } @@ -161,29 +136,29 @@ } // namespace void CanonicalizeQuery(const char* spec, - const url_parse::Component& query, + const Component& query, CharsetConverter* converter, CanonOutput* output, - url_parse::Component* out_query) { + Component* out_query) { DoCanonicalizeQuery<char, unsigned char>(spec, query, converter, output, out_query); } -void CanonicalizeQuery(const char16* spec, - const url_parse::Component& query, +void CanonicalizeQuery(const base::char16* spec, + const Component& query, CharsetConverter* converter, CanonOutput* output, - url_parse::Component* out_query) { - DoCanonicalizeQuery<char16, char16>(spec, query, converter, - output, out_query); + Component* out_query) { + DoCanonicalizeQuery<base::char16, base::char16>(spec, query, converter, + output, out_query); } -void ConvertUTF16ToQueryEncoding(const char16* input, - const url_parse::Component& query, +void ConvertUTF16ToQueryEncoding(const base::char16* input, + const Component& query, CharsetConverter* converter, CanonOutput* output) { - DoConvertToQueryEncoding<char16, char16>(input, query, - converter, output); + DoConvertToQueryEncoding<base::char16, base::char16>(input, query, + converter, output); } -} // namespace url_canon +} // namespace url
diff --git a/googleurl/src/url_canon_relative.cc b/src/url/url_canon_relative.cc similarity index 73% rename from googleurl/src/url_canon_relative.cc rename to src/url/url_canon_relative.cc index 6bcc72f..9436245 100644 --- a/googleurl/src/url_canon_relative.cc +++ b/src/url/url_canon_relative.cc
@@ -1,41 +1,18 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. // Canonicalizer functions for working with and resolving relative URLs. #include "base/logging.h" -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" -#include "googleurl/src/url_file.h" -#include "googleurl/src/url_parse_internal.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_constants.h" +#include "url/url_file.h" +#include "url/url_parse_internal.h" +#include "url/url_util_internal.h" -namespace url_canon { +namespace url { namespace { @@ -50,9 +27,9 @@ // The base URL should always be canonical, therefore is ASCII. template<typename CHAR> bool AreSchemesEqual(const char* base, - const url_parse::Component& base_scheme, + const Component& base_scheme, const CHAR* cmp, - const url_parse::Component& cmp_scheme) { + const Component& cmp_scheme) { if (base_scheme.len != cmp_scheme.len) return false; for (int i = 0; i < base_scheme.len; i++) { @@ -76,8 +53,8 @@ int spec_len) { if (start_offset >= spec_len) return false; - return url_parse::IsURLSlash(spec[start_offset]) && - url_parse::DoesBeginWindowsDriveSpec(spec, start_offset + 1, spec_len); + return IsURLSlash(spec[start_offset]) && + DoesBeginWindowsDriveSpec(spec, start_offset + 1, spec_len); } #endif // WIN32 @@ -85,20 +62,20 @@ // See IsRelativeURL in the header file for usage. template<typename CHAR> bool DoIsRelativeURL(const char* base, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, const CHAR* url, int url_len, bool is_base_hierarchical, bool* is_relative, - url_parse::Component* relative_component) { + Component* relative_component) { *is_relative = false; // So we can default later to not relative. // Trim whitespace and construct a new range for the substring. int begin = 0; - url_parse::TrimURL(url, &begin, &url_len); + TrimURL(url, &begin, &url_len); if (begin >= url_len) { // Empty URLs are relative, but do nothing. - *relative_component = url_parse::Component(begin, 0); + *relative_component = Component(begin, 0); *is_relative = true; return true; } @@ -115,8 +92,8 @@ // // We require strict backslashes when detecting UNC since two forward // shashes should be treated a a relative URL with a hostname. - if (url_parse::DoesBeginWindowsDriveSpec(url, begin, url_len) || - url_parse::DoesBeginUNCPath(url, begin, url_len, true)) + if (DoesBeginWindowsDriveSpec(url, begin, url_len) || + DoesBeginUNCPath(url, begin, url_len, true)) return true; #endif // WIN32 @@ -124,13 +101,19 @@ // BUT: Just because we have a scheme, doesn't make it absolute. // "http:foo.html" is a relative URL with path "foo.html". If the scheme is // empty, we treat it as relative (":foo") like IE does. - url_parse::Component scheme; - if (!url_parse::ExtractScheme(url, url_len, &scheme) || scheme.len == 0) { - // Don't allow relative URLs if the base scheme doesn't support it. - if (!is_base_hierarchical) + Component scheme; + const bool scheme_is_empty = + !ExtractScheme(url, url_len, &scheme) || scheme.len == 0; + if (scheme_is_empty) { + if (url[begin] == '#') { + // |url| is a bare fragement (e.g. "#foo"). This can be resolved against + // any base. Fall-through. + } else if (!is_base_hierarchical) { + // Don't allow relative URLs if the base scheme doesn't support it. return false; + } - *relative_component = url_parse::MakeRange(begin, url_len); + *relative_component = MakeRange(begin, url_len); *is_relative = true; return true; } @@ -139,7 +122,11 @@ int scheme_end = scheme.end(); for (int i = scheme.begin; i < scheme_end; i++) { if (!CanonicalSchemeChar(url[i])) { - *relative_component = url_parse::MakeRange(begin, url_len); + if (!is_base_hierarchical) { + // Don't allow relative URLs if the base scheme doesn't support it. + return false; + } + *relative_component = MakeRange(begin, url_len); *is_relative = true; return true; } @@ -155,18 +142,23 @@ if (!is_base_hierarchical) return true; + int colon_offset = scheme.end(); + + // If it's a filesystem URL, the only valid way to make it relative is not to + // supply a scheme. There's no equivalent to e.g. http:index.html. + if (CompareSchemeComponent(url, scheme, kFileSystemScheme)) + return true; + // ExtractScheme guarantees that the colon immediately follows what it // considers to be the scheme. CountConsecutiveSlashes will handle the // case where the begin offset is the end of the input. - int colon_offset = scheme.end(); - int num_slashes = url_parse::CountConsecutiveSlashes(url, colon_offset + 1, - url_len); + int num_slashes = CountConsecutiveSlashes(url, colon_offset + 1, url_len); if (num_slashes == 0 || num_slashes == 1) { // No slashes means it's a relative path like "http:foo.html". One slash // is an absolute path. "http:/home/foo.html" *is_relative = true; - *relative_component = url_parse::MakeRange(colon_offset + 1, url_len); + *relative_component = MakeRange(colon_offset + 1, url_len); return true; } @@ -205,12 +197,12 @@ // source should already be canonical, we don't have to do anything special, // and the input is ASCII. void CopyOneComponent(const char* source, - const url_parse::Component& source_component, + const Component& source_component, CanonOutput* output, - url_parse::Component* output_component) { + Component* output_component) { if (source_component.len < 0) { // This component is not present. - *output_component = url_parse::Component(); + *output_component = Component(); return; } @@ -245,8 +237,7 @@ // If the relative begins with a drive spec, don't do anything. The existing // drive spec in the base will be replaced. - if (url_parse::DoesBeginWindowsDriveSpec(relative_url, - path_start, relative_url_len)) { + if (DoesBeginWindowsDriveSpec(relative_url, path_start, relative_url_len)) { return base_path_begin; // Relative URL path is "C:/foo" } @@ -272,23 +263,19 @@ // the input is a relative path or less (qyuery or ref). template<typename CHAR> bool DoResolveRelativePath(const char* base_url, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, bool base_is_file, const CHAR* relative_url, - const url_parse::Component& relative_component, + const Component& relative_component, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* out_parsed) { + Parsed* out_parsed) { bool success = true; // We know the authority section didn't change, copy it to the output. We // also know we have a path so can copy up to there. - url_parse::Component path, query, ref; - url_parse::ParsePathInternal(relative_url, - relative_component, - &path, - &query, - &ref); + Component path, query, ref; + ParsePathInternal(relative_url, relative_component, &path, &query, &ref); // Canonical URLs always have a path, so we can use that offset. output->Append(base_url, base_parsed.path.begin); @@ -313,7 +300,7 @@ } #endif // WIN32 - if (url_parse::IsURLSlash(relative_url[path.begin])) { + if (IsURLSlash(relative_url[path.begin])) { // Easy case: the path is an absolute path on the server, so we can // just replace everything from the path on with the new versions. // Since the input should be canonical hierarchical URL, we should @@ -329,7 +316,7 @@ output); success &= CanonicalizePartialPath(relative_url, path, path_begin, output); - out_parsed->path = url_parse::MakeRange(path_begin, output->length()); + out_parsed->path = MakeRange(path_begin, output->length()); // Copy the rest of the stuff after the path from the relative path. } @@ -340,8 +327,7 @@ CanonicalizeRef(relative_url, ref, output, &out_parsed->ref); // Fix the path beginning to add back the "C:" we may have written above. - out_parsed->path = url_parse::MakeRange(true_path_begin, - out_parsed->path.end()); + out_parsed->path = MakeRange(true_path_begin, out_parsed->path.end()); return success; } @@ -381,18 +367,17 @@ // should be kept from the original URL is the scheme. template<typename CHAR> bool DoResolveRelativeHost(const char* base_url, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, const CHAR* relative_url, - const url_parse::Component& relative_component, + const Component& relative_component, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* out_parsed) { + Parsed* out_parsed) { // Parse the relative URL, just like we would for anything following a // scheme. - url_parse::Parsed relative_parsed; // Everything but the scheme is valid. - url_parse::ParseAfterScheme(&relative_url[relative_component.begin], - relative_component.len, relative_component.begin, - &relative_parsed); + Parsed relative_parsed; // Everything but the scheme is valid. + ParseAfterScheme(relative_url, relative_component.end(), + relative_component.begin, &relative_parsed); // Now we can just use the replacement function to replace all the necessary // parts of the old URL with the new one. @@ -413,16 +398,16 @@ // include: "//hostname/path", "/c:/foo", and "//hostname/c:/foo". template<typename CHAR> bool DoResolveAbsoluteFile(const CHAR* relative_url, - const url_parse::Component& relative_component, + const Component& relative_component, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* out_parsed) { + Parsed* out_parsed) { // Parse the file URL. The file URl parsing function uses the same logic // as we do for determining if the file is absolute, in which case it will // not bother to look for a scheme. - url_parse::Parsed relative_parsed; - url_parse::ParseFileURL(&relative_url[relative_component.begin], - relative_component.len, &relative_parsed); + Parsed relative_parsed; + ParseFileURL(&relative_url[relative_component.begin], relative_component.len, + &relative_parsed); return CanonicalizeFileURL(&relative_url[relative_component.begin], relative_component.len, relative_parsed, @@ -432,13 +417,13 @@ // TODO(brettw) treat two slashes as root like Mozilla for FTP? template<typename CHAR> bool DoResolveRelativeURL(const char* base_url, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, bool base_is_file, const CHAR* relative_url, - const url_parse::Component& relative_component, + const Component& relative_component, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* out_parsed) { + Parsed* out_parsed) { // Starting point for our output parsed. We'll fix what we change. *out_parsed = base_parsed; @@ -465,7 +450,7 @@ return true; } - int num_slashes = url_parse::CountConsecutiveSlashes( + int num_slashes = CountConsecutiveSlashes( relative_url, relative_component.begin, relative_component.end()); #ifdef WIN32 @@ -481,24 +466,27 @@ // be setting the path. // // This assumes the absolute path resolver handles absolute URLs like this - // properly. url_util::DoCanonicalize does this. + // properly. DoCanonicalize does this. int after_slashes = relative_component.begin + num_slashes; - if (url_parse::DoesBeginUNCPath(relative_url, relative_component.begin, - relative_component.end(), !base_is_file) || + if (DoesBeginUNCPath(relative_url, relative_component.begin, + relative_component.end(), !base_is_file) || ((num_slashes == 0 || base_is_file) && - url_parse::DoesBeginWindowsDriveSpec(relative_url, after_slashes, - relative_component.end()))) { + DoesBeginWindowsDriveSpec( + relative_url, after_slashes, relative_component.end()))) { return DoResolveAbsoluteFile(relative_url, relative_component, query_converter, output, out_parsed); } #else // Other platforms need explicit handling for file: URLs with multiple // slashes because the generic scheme parsing always extracts a host, but a - // file: URL only has a host if it has exactly 2 slashes. This also - // handles the special case where the URL is only slashes, since that - // doesn't have a host part either. + // file: URL only has a host if it has exactly 2 slashes. Even if it does + // have a host, we want to use the special host detection logic for file + // URLs provided by DoResolveAbsoluteFile(), as opposed to the generic host + // detection logic, for consistency with parsing file URLs from scratch. + // This also handles the special case where the URL is only slashes, + // since that doesn't have a host part either. if (base_is_file && - (num_slashes > 2 || num_slashes == relative_component.len)) { + (num_slashes >= 2 || num_slashes == relative_component.len)) { return DoResolveAbsoluteFile(relative_url, relative_component, query_converter, output, out_parsed); } @@ -520,53 +508,53 @@ } // namespace bool IsRelativeURL(const char* base, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, const char* fragment, int fragment_len, bool is_base_hierarchical, bool* is_relative, - url_parse::Component* relative_component) { + Component* relative_component) { return DoIsRelativeURL<char>( base, base_parsed, fragment, fragment_len, is_base_hierarchical, is_relative, relative_component); } bool IsRelativeURL(const char* base, - const url_parse::Parsed& base_parsed, - const char16* fragment, + const Parsed& base_parsed, + const base::char16* fragment, int fragment_len, bool is_base_hierarchical, bool* is_relative, - url_parse::Component* relative_component) { - return DoIsRelativeURL<char16>( + Component* relative_component) { + return DoIsRelativeURL<base::char16>( base, base_parsed, fragment, fragment_len, is_base_hierarchical, is_relative, relative_component); } bool ResolveRelativeURL(const char* base_url, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, bool base_is_file, const char* relative_url, - const url_parse::Component& relative_component, + const Component& relative_component, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* out_parsed) { + Parsed* out_parsed) { return DoResolveRelativeURL<char>( base_url, base_parsed, base_is_file, relative_url, relative_component, query_converter, output, out_parsed); } bool ResolveRelativeURL(const char* base_url, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, bool base_is_file, - const char16* relative_url, - const url_parse::Component& relative_component, + const base::char16* relative_url, + const Component& relative_component, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* out_parsed) { - return DoResolveRelativeURL<char16>( + Parsed* out_parsed) { + return DoResolveRelativeURL<base::char16>( base_url, base_parsed, base_is_file, relative_url, relative_component, query_converter, output, out_parsed); } -} // namespace url_canon +} // namespace url
diff --git a/src/url/url_canon_stdstring.cc b/src/url/url_canon_stdstring.cc new file mode 100644 index 0000000..366a2e0 --- /dev/null +++ b/src/url/url_canon_stdstring.cc
@@ -0,0 +1,32 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "url/url_canon_stdstring.h" + +namespace url { + +StdStringCanonOutput::StdStringCanonOutput(std::string* str) + : CanonOutput(), str_(str) { + cur_len_ = static_cast<int>(str_->size()); // Append to existing data. + str_->resize(str_->capacity()); + buffer_ = str_->empty() ? NULL : &(*str_)[0]; + buffer_len_ = static_cast<int>(str_->size()); +} + +StdStringCanonOutput::~StdStringCanonOutput() { + // Nothing to do, we don't own the string. +} + +void StdStringCanonOutput::Complete() { + str_->resize(cur_len_); + buffer_len_ = cur_len_; +} + +void StdStringCanonOutput::Resize(int sz) { + str_->resize(sz); + buffer_ = str_->empty() ? NULL : &(*str_)[0]; + buffer_len_ = sz; +} + +} // namespace url
diff --git a/src/url/url_canon_stdstring.h b/src/url/url_canon_stdstring.h new file mode 100644 index 0000000..f8a847d --- /dev/null +++ b/src/url/url_canon_stdstring.h
@@ -0,0 +1,85 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_CANON_STDSTRING_H_ +#define URL_URL_CANON_STDSTRING_H_ + +// This header file defines a canonicalizer output method class for STL +// strings. Because the canonicalizer tries not to be dependent on the STL, +// we have segregated it here. + +#include <string> + +#include "url/url_canon.h" +#include "url/url_export.h" + +namespace url { + +// Write into a std::string given in the constructor. This object does not own +// the string itself, and the user must ensure that the string stays alive +// throughout the lifetime of this object. +// +// The given string will be appended to; any existing data in the string will +// be preserved. The caller should reserve() the amount of data in the string +// they expect to be written. We will resize if necessary, but that's slow. +// +// Note that when canonicalization is complete, the string will likely have +// unused space at the end because we make the string very big to start out +// with (by |initial_size|). This ends up being important because resize +// operations are slow, and because the base class needs to write directly +// into the buffer. +// +// Therefore, the user should call Complete() before using the string that +// this class wrote into. +class URL_EXPORT StdStringCanonOutput : public CanonOutput { + public: + StdStringCanonOutput(std::string* str); + ~StdStringCanonOutput() override; + + // Must be called after writing has completed but before the string is used. + void Complete(); + + void Resize(int sz) override; + + protected: + std::string* str_; +}; + +// An extension of the Replacements class that allows the setters to use +// standard strings. +// +// The strings passed as arguments are not copied and must remain valid until +// this class goes out of scope. +template<typename STR> +class StdStringReplacements : public Replacements<typename STR::value_type> { + public: + void SetSchemeStr(const STR& s) { + this->SetScheme(s.data(), Component(0, static_cast<int>(s.length()))); + } + void SetUsernameStr(const STR& s) { + this->SetUsername(s.data(), Component(0, static_cast<int>(s.length()))); + } + void SetPasswordStr(const STR& s) { + this->SetPassword(s.data(), Component(0, static_cast<int>(s.length()))); + } + void SetHostStr(const STR& s) { + this->SetHost(s.data(), Component(0, static_cast<int>(s.length()))); + } + void SetPortStr(const STR& s) { + this->SetPort(s.data(), Component(0, static_cast<int>(s.length()))); + } + void SetPathStr(const STR& s) { + this->SetPath(s.data(), Component(0, static_cast<int>(s.length()))); + } + void SetQueryStr(const STR& s) { + this->SetQuery(s.data(), Component(0, static_cast<int>(s.length()))); + } + void SetRefStr(const STR& s) { + this->SetRef(s.data(), Component(0, static_cast<int>(s.length()))); + } +}; + +} // namespace url + +#endif // URL_URL_CANON_STDSTRING_H_
diff --git a/googleurl/src/url_canon_stdurl.cc b/src/url/url_canon_stdurl.cc similarity index 64% rename from googleurl/src/url_canon_stdurl.cc rename to src/url/url_canon_stdurl.cc index 1e21a14..7a61de8 100644 --- a/googleurl/src/url_canon_stdurl.cc +++ b/src/url/url_canon_stdurl.cc
@@ -1,48 +1,24 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. // Functions to canonicalize "standard" URLs, which are ones that have an // authority section including a host name. -#include "googleurl/src/url_canon.h" -#include "googleurl/src/url_canon_internal.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_constants.h" -namespace url_canon { +namespace url { namespace { template<typename CHAR, typename UCHAR> bool DoCanonicalizeStandardURL(const URLComponentSource<CHAR>& source, - const url_parse::Parsed& parsed, + const Parsed& parsed, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* new_parsed) { + Parsed* new_parsed) { // Scheme: this will append the colon. bool success = CanonicalizeScheme(source.scheme, parsed.scheme, output, &new_parsed->scheme); @@ -97,7 +73,7 @@ // When we have an empty path, make up a path when we have an authority // or something following the path. The only time we allow an empty // output path is when there is nothing else. - new_parsed->path = url_parse::Component(output->length(), 1); + new_parsed->path = Component(output->length(), 1); output->push_back('/'); } else { // No path at all @@ -120,28 +96,28 @@ // Returns the default port for the given canonical scheme, or PORT_UNSPECIFIED // if the scheme is unknown. int DefaultPortForScheme(const char* scheme, int scheme_len) { - int default_port = url_parse::PORT_UNSPECIFIED; + int default_port = PORT_UNSPECIFIED; switch (scheme_len) { case 4: - if (!strncmp(scheme, "http", scheme_len)) + if (!strncmp(scheme, kHttpScheme, scheme_len)) default_port = 80; break; case 5: - if (!strncmp(scheme, "https", scheme_len)) + if (!strncmp(scheme, kHttpsScheme, scheme_len)) default_port = 443; break; case 3: - if (!strncmp(scheme, "ftp", scheme_len)) + if (!strncmp(scheme, kFtpScheme, scheme_len)) default_port = 21; - else if (!strncmp(scheme, "wss", scheme_len)) + else if (!strncmp(scheme, kWssScheme, scheme_len)) default_port = 443; break; case 6: - if (!strncmp(scheme, "gopher", scheme_len)) + if (!strncmp(scheme, kGopherScheme, scheme_len)) default_port = 70; break; case 2: - if (!strncmp(scheme, "ws", scheme_len)) + if (!strncmp(scheme, kWsScheme, scheme_len)) default_port = 80; break; } @@ -150,23 +126,23 @@ bool CanonicalizeStandardURL(const char* spec, int spec_len, - const url_parse::Parsed& parsed, + const Parsed& parsed, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* new_parsed) { + Parsed* new_parsed) { return DoCanonicalizeStandardURL<char, unsigned char>( URLComponentSource<char>(spec), parsed, query_converter, output, new_parsed); } -bool CanonicalizeStandardURL(const char16* spec, +bool CanonicalizeStandardURL(const base::char16* spec, int spec_len, - const url_parse::Parsed& parsed, + const Parsed& parsed, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* new_parsed) { - return DoCanonicalizeStandardURL<char16, char16>( - URLComponentSource<char16>(spec), parsed, query_converter, + Parsed* new_parsed) { + return DoCanonicalizeStandardURL<base::char16, base::char16>( + URLComponentSource<base::char16>(spec), parsed, query_converter, output, new_parsed); } @@ -180,13 +156,13 @@ // You would also need to update DoReplaceComponents in url_util.cc which // relies on this re-checking everything (see the comment there for why). bool ReplaceStandardURL(const char* base, - const url_parse::Parsed& base_parsed, + const Parsed& base_parsed, const Replacements<char>& replacements, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* new_parsed) { + Parsed* new_parsed) { URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); + Parsed parsed(base_parsed); SetupOverrideComponents(base, replacements, &source, &parsed); return DoCanonicalizeStandardURL<char, unsigned char>( source, parsed, query_converter, output, new_parsed); @@ -195,17 +171,17 @@ // For 16-bit replacements, we turn all the replacements into UTF-8 so the // regular codepath can be used. bool ReplaceStandardURL(const char* base, - const url_parse::Parsed& base_parsed, - const Replacements<char16>& replacements, + const Parsed& base_parsed, + const Replacements<base::char16>& replacements, CharsetConverter* query_converter, CanonOutput* output, - url_parse::Parsed* new_parsed) { + Parsed* new_parsed) { RawCanonOutput<1024> utf8; URLComponentSource<char> source(base); - url_parse::Parsed parsed(base_parsed); + Parsed parsed(base_parsed); SetupUTF16OverrideComponents(base, replacements, &utf8, &source, &parsed); return DoCanonicalizeStandardURL<char, unsigned char>( source, parsed, query_converter, output, new_parsed); } -} // namespace url_canon +} // namespace url
diff --git a/src/url/url_canon_unittest.cc b/src/url/url_canon_unittest.cc new file mode 100644 index 0000000..1917cc9 --- /dev/null +++ b/src/url/url_canon_unittest.cc
@@ -0,0 +1,2131 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <errno.h> + +#include "base/macros.h" +#include "testing/base/public/gunit.h" +#include "url/url_canon.h" +#include "url/url_canon_internal.h" +#include "url/url_canon_stdstring.h" +#include "url/url_parse.h" +#include "url/url_test_utils.h" + +namespace url { + +using test_utils::WStringToUTF16; +using test_utils::ConvertUTF8ToUTF16; +using test_utils::ConvertUTF16ToUTF8; + +namespace { + +struct ComponentCase { + const char* input; + const char* expected; + Component expected_component; + bool expected_success; +}; + +// ComponentCase but with dual 8-bit/16-bit input. Generally, the unit tests +// treat each input as optional, and will only try processing if non-NULL. +// The output is always 8-bit. +struct DualComponentCase { + const char* input8; + const wchar_t* input16; + const char* expected; + Component expected_component; + bool expected_success; +}; + +// Test cases for CanonicalizeIPAddress(). The inputs are identical to +// DualComponentCase, but the output has extra CanonHostInfo fields. +struct IPAddressCase { + const char* input8; + const wchar_t* input16; + const char* expected; + Component expected_component; + + // CanonHostInfo fields, for verbose output. + CanonHostInfo::Family expected_family; + int expected_num_ipv4_components; + const char* expected_address_hex; // Two hex chars per IP address byte. +}; + +std::string BytesToHexString(unsigned char bytes[16], int length) { + EXPECT_TRUE(length == 0 || length == 4 || length == 16) + << "Bad IP address length: " << length; + std::string result; + for (int i = 0; i < length; ++i) { + result.push_back(kHexCharLookup[(bytes[i] >> 4) & 0xf]); + result.push_back(kHexCharLookup[bytes[i] & 0xf]); + } + return result; +} + +struct ReplaceCase { + const char* base; + const char* scheme; + const char* username; + const char* password; + const char* host; + const char* port; + const char* path; + const char* query; + const char* ref; + const char* expected; +}; + +// Magic string used in the replacements code that tells SetupReplComp to +// call the clear function. +const char kDeleteComp[] = "|"; + +// Sets up a replacement for a single component. This is given pointers to +// the set and clear function for the component being replaced, and will +// either set the component (if it exists) or clear it (if the replacement +// string matches kDeleteComp). +// +// This template is currently used only for the 8-bit case, and the strlen +// causes it to fail in other cases. It is left a template in case we have +// tests for wide replacements. +template<typename CHAR> +void SetupReplComp( + void (Replacements<CHAR>::*set)(const CHAR*, const Component&), + void (Replacements<CHAR>::*clear)(), + Replacements<CHAR>* rep, + const CHAR* str) { + if (str && str[0] == kDeleteComp[0]) { + (rep->*clear)(); + } else if (str) { + (rep->*set)(str, Component(0, static_cast<int>(strlen(str)))); + } +} + +} // namespace + +TEST(URLCanonTest, DoAppendUTF8) { + struct UTF8Case { + unsigned input; + const char* output; + } utf_cases[] = { + // Valid code points. + {0x24, "\x24"}, + {0xA2, "\xC2\xA2"}, + {0x20AC, "\xE2\x82\xAC"}, + {0x24B62, "\xF0\xA4\xAD\xA2"}, + {0x10FFFF, "\xF4\x8F\xBF\xBF"}, + }; + std::string out_str; + for (size_t i = 0; i < arraysize(utf_cases); i++) { + out_str.clear(); + StdStringCanonOutput output(&out_str); + AppendUTF8Value(utf_cases[i].input, &output); + output.Complete(); + EXPECT_EQ(utf_cases[i].output, out_str); + } +} + +#if defined(GTEST_HAS_DEATH_TEST) +// TODO(mattm): Can't run this in debug mode for now, since the DCHECK will +// cause the Chromium stacktrace dialog to appear and hang the test. +// See http://crbug.com/49580. +#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON) +#define MAYBE_DoAppendUTF8Invalid DoAppendUTF8Invalid +#else +#define MAYBE_DoAppendUTF8Invalid DISABLED_DoAppendUTF8Invalid +#endif +TEST(URLCanonTest, MAYBE_DoAppendUTF8Invalid) { + std::string out_str; + StdStringCanonOutput output(&out_str); + // Invalid code point (too large). + ASSERT_DEBUG_DEATH({ + AppendUTF8Value(0x110000, &output); + output.Complete(); + EXPECT_EQ("", out_str); + }, ""); +} +#endif // defined(GTEST_HAS_DEATH_TEST) + +TEST(URLCanonTest, UTF) { + // Low-level test that we handle reading, canonicalization, and writing + // UTF-8/UTF-16 strings properly. + struct UTFCase { + const char* input8; + const wchar_t* input16; + bool expected_success; + const char* output; + } utf_cases[] = { + // Valid canonical input should get passed through & escaped. + {"\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d", true, "%E4%BD%A0%E5%A5%BD"}, + // Test a characer that takes > 16 bits (U+10300 = old italic letter A) + {"\xF0\x90\x8C\x80", L"\xd800\xdf00", true, "%F0%90%8C%80"}, + // Non-shortest-form UTF-8 are invalid. The bad char should be replaced + // with the invalid character (EF BF DB in UTF-8). + {"\xf0\x84\xbd\xa0\xe5\xa5\xbd", NULL, false, "%EF%BF%BD%E5%A5%BD"}, + // Invalid UTF-8 sequences should be marked as invalid (the first + // sequence is truncated). + {"\xe4\xa0\xe5\xa5\xbd", L"\xd800\x597d", false, "%EF%BF%BD%E5%A5%BD"}, + // Character going off the end. + {"\xe4\xbd\xa0\xe5\xa5", L"\x4f60\xd800", false, "%E4%BD%A0%EF%BF%BD"}, + // ...same with low surrogates with no high surrogate. + {"\xed\xb0\x80", L"\xdc00", false, "%EF%BF%BD"}, + // Test a UTF-8 encoded surrogate value is marked as invalid. + // ED A0 80 = U+D800 + {"\xed\xa0\x80", NULL, false, "%EF%BF%BD"}, + }; + + std::string out_str; + for (size_t i = 0; i < arraysize(utf_cases); i++) { + if (utf_cases[i].input8) { + out_str.clear(); + StdStringCanonOutput output(&out_str); + + int input_len = static_cast<int>(strlen(utf_cases[i].input8)); + bool success = true; + for (int ch = 0; ch < input_len; ch++) { + success &= AppendUTF8EscapedChar(utf_cases[i].input8, &ch, input_len, + &output); + } + output.Complete(); + EXPECT_EQ(utf_cases[i].expected_success, success); + EXPECT_EQ(std::string(utf_cases[i].output), out_str); + } + if (utf_cases[i].input16) { + out_str.clear(); + StdStringCanonOutput output(&out_str); + + base::string16 input_str(WStringToUTF16(utf_cases[i].input16)); + int input_len = static_cast<int>(input_str.length()); + bool success = true; + for (int ch = 0; ch < input_len; ch++) { + success &= AppendUTF8EscapedChar(input_str.c_str(), &ch, input_len, + &output); + } + output.Complete(); + EXPECT_EQ(utf_cases[i].expected_success, success); + EXPECT_EQ(std::string(utf_cases[i].output), out_str); + } + + if (utf_cases[i].input8 && utf_cases[i].input16 && + utf_cases[i].expected_success) { + // Check that the UTF-8 and UTF-16 inputs are equivalent. + + // UTF-16 -> UTF-8 + std::string input8_str(utf_cases[i].input8); + base::string16 input16_str(WStringToUTF16(utf_cases[i].input16)); + EXPECT_EQ(input8_str, ConvertUTF16ToUTF8(input16_str)); + + // UTF-8 -> UTF-16 + EXPECT_EQ(input16_str, ConvertUTF8ToUTF16(input8_str)); + } + } +} + +TEST(URLCanonTest, Scheme) { + // Here, we're mostly testing that unusual characters are handled properly. + // The canonicalizer doesn't do any parsing or whitespace detection. It will + // also do its best on error, and will escape funny sequences (these won't be + // valid schemes and it will return error). + // + // Note that the canonicalizer will append a colon to the output to separate + // out the rest of the URL, which is not present in the input. We check, + // however, that the output range includes everything but the colon. + ComponentCase scheme_cases[] = { + {"http", "http:", Component(0, 4), true}, + {"HTTP", "http:", Component(0, 4), true}, + {" HTTP ", "%20http%20:", Component(0, 10), false}, + {"htt: ", "htt%3A%20:", Component(0, 9), false}, + {"\xe4\xbd\xa0\xe5\xa5\xbdhttp", "%E4%BD%A0%E5%A5%BDhttp:", Component(0, 22), false}, + // Don't re-escape something already escaped. Note that it will + // "canonicalize" the 'A' to 'a', but that's OK. + {"ht%3Atp", "ht%3atp:", Component(0, 7), false}, + }; + + std::string out_str; + + for (size_t i = 0; i < arraysize(scheme_cases); i++) { + int url_len = static_cast<int>(strlen(scheme_cases[i].input)); + Component in_comp(0, url_len); + Component out_comp; + + out_str.clear(); + StdStringCanonOutput output1(&out_str); + bool success = CanonicalizeScheme(scheme_cases[i].input, in_comp, &output1, + &out_comp); + output1.Complete(); + + EXPECT_EQ(scheme_cases[i].expected_success, success); + EXPECT_EQ(std::string(scheme_cases[i].expected), out_str); + EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len); + + // Now try the wide version + out_str.clear(); + StdStringCanonOutput output2(&out_str); + + base::string16 wide_input(ConvertUTF8ToUTF16(scheme_cases[i].input)); + in_comp.len = static_cast<int>(wide_input.length()); + success = CanonicalizeScheme(wide_input.c_str(), in_comp, &output2, + &out_comp); + output2.Complete(); + + EXPECT_EQ(scheme_cases[i].expected_success, success); + EXPECT_EQ(std::string(scheme_cases[i].expected), out_str); + EXPECT_EQ(scheme_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(scheme_cases[i].expected_component.len, out_comp.len); + } + + // Test the case where the scheme is declared nonexistant, it should be + // converted into an empty scheme. + Component out_comp; + out_str.clear(); + StdStringCanonOutput output(&out_str); + + EXPECT_TRUE(CanonicalizeScheme("", Component(0, -1), &output, &out_comp)); + output.Complete(); + + EXPECT_EQ(std::string(":"), out_str); + EXPECT_EQ(0, out_comp.begin); + EXPECT_EQ(0, out_comp.len); +} + +TEST(URLCanonTest, Host) { + IPAddressCase host_cases[] = { + // Basic canonicalization, uppercase should be converted to lowercase. + {"GoOgLe.CoM", L"GoOgLe.CoM", "google.com", Component(0, 10), CanonHostInfo::NEUTRAL, -1, ""}, + // Spaces and some other characters should be escaped. + {"Goo%20 goo%7C|.com", L"Goo%20 goo%7C|.com", "goo%20%20goo%7C%7C.com", Component(0, 22), CanonHostInfo::NEUTRAL, -1, ""}, + // Exciting different types of spaces! + {NULL, L"GOO\x00a0\x3000goo.com", "goo%20%20goo.com", Component(0, 16), CanonHostInfo::NEUTRAL, -1, ""}, + // Other types of space (no-break, zero-width, zero-width-no-break) are + // name-prepped away to nothing. + {NULL, L"GOO\x200b\x2060\xfeffgoo.com", "googoo.com", Component(0, 10), CanonHostInfo::NEUTRAL, -1, ""}, + // Ideographic full stop (full-width period for Chinese, etc.) should be + // treated as a dot. + {NULL, L"www.foo\x3002" L"bar.com", "www.foo.bar.com", Component(0, 15), CanonHostInfo::NEUTRAL, -1, ""}, + // Invalid unicode characters should fail... + // ...In wide input, ICU will barf and we'll end up with the input as + // escaped UTF-8 (the invalid character should be replaced with the + // replacement character). + {"\xef\xb7\x90zyx.com", L"\xfdd0zyx.com", "%EF%BF%BDzyx.com", Component(0, 16), CanonHostInfo::BROKEN, -1, ""}, + // ...This is the same as previous but with with escaped. + {"%ef%b7%90zyx.com", L"%ef%b7%90zyx.com", "%EF%BF%BDzyx.com", Component(0, 16), CanonHostInfo::BROKEN, -1, ""}, + // Test name prepping, fullwidth input should be converted to ASCII and NOT + // IDN-ized. This is "Go" in fullwidth UTF-8/UTF-16. + {"\xef\xbc\xa7\xef\xbd\x8f.com", L"\xff27\xff4f.com", "go.com", Component(0, 6), CanonHostInfo::NEUTRAL, -1, ""}, + // Test that fullwidth escaped values are properly name-prepped, + // then converted or rejected. + // ...%41 in fullwidth = 'A' (also as escaped UTF-8 input) + {"\xef\xbc\x85\xef\xbc\x94\xef\xbc\x91.com", L"\xff05\xff14\xff11.com", "a.com", Component(0, 5), CanonHostInfo::NEUTRAL, -1, ""}, + {"%ef%bc%85%ef%bc%94%ef%bc%91.com", L"%ef%bc%85%ef%bc%94%ef%bc%91.com", "a.com", Component(0, 5), CanonHostInfo::NEUTRAL, -1, ""}, + // ...%00 in fullwidth should fail (also as escaped UTF-8 input) + {"\xef\xbc\x85\xef\xbc\x90\xef\xbc\x90.com", L"\xff05\xff10\xff10.com", "%00.com", Component(0, 7), CanonHostInfo::BROKEN, -1, ""}, + {"%ef%bc%85%ef%bc%90%ef%bc%90.com", L"%ef%bc%85%ef%bc%90%ef%bc%90.com", "%00.com", Component(0, 7), CanonHostInfo::BROKEN, -1, ""}, + // Basic IDN support, UTF-8 and UTF-16 input should be converted to IDN + {"\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", L"\x4f60\x597d\x4f60\x597d", "xn--6qqa088eba", Component(0, 14), CanonHostInfo::NEUTRAL, -1, ""}, + // See http://unicode.org/cldr/utility/idna.jsp for other + // examples/experiments and http://goo.gl/7yG11o + // for the full list of characters handled differently by + // IDNA 2003, UTS 46 (http://unicode.org/reports/tr46/ ) and IDNA 2008. + + // 4 Deviation characters are mapped/ignored in UTS 46 transitional + // mechansm. UTS 46, table 4 row (g). + // Sharp-s is mapped to 'ss' in UTS 46 and IDNA 2003. + // Otherwise, it'd be "xn--fuball-cta.de". + {"fu\xc3\x9f" "ball.de", L"fu\x00df" L"ball.de", "fussball.de", + Component(0, 11), CanonHostInfo::NEUTRAL, -1, ""}, + // Final-sigma (U+03C3) is mapped to regular sigma (U+03C2). + // Otherwise, it'd be "xn--wxaijb9b". + {"\xcf\x83\xcf\x8c\xce\xbb\xce\xbf\xcf\x82", L"\x3c3\x3cc\x3bb\x3bf\x3c2", + "xn--wxaikc6b", Component(0, 12), + CanonHostInfo::NEUTRAL, -1, ""}, + // ZWNJ (U+200C) and ZWJ (U+200D) are mapped away in UTS 46 transitional + // handling as well as in IDNA 2003. + {"a\xe2\x80\x8c" "b\xe2\x80\x8d" "c", L"a\x200c" L"b\x200d" L"c", "abc", + Component(0, 3), CanonHostInfo::NEUTRAL, -1, ""}, + // ZWJ between Devanagari characters is still mapped away in UTS 46 + // transitional handling. IDNA 2008 would give xn--11bo0mv54g. + {"\xe0\xa4\x95\xe0\xa5\x8d\xe2\x80\x8d\xe0\xa4\x9c", + L"\x915\x94d\x200d\x91c", "xn--11bo0m", + Component(0, 10), CanonHostInfo::NEUTRAL, -1, ""}, + // Fullwidth exclamation mark is disallowed. UTS 46, table 4, row (b) + // However, we do allow this at the moment because we don't use + // STD3 rules and canonicalize full-width ASCII to ASCII. + {"wow\xef\xbc\x81", L"wow\xff01", "wow%21", + Component(0, 6), CanonHostInfo::NEUTRAL, -1, ""}, + // U+2132 (turned capital F) is disallowed. UTS 46, table 4, row (c) + // Allowed in IDNA 2003, but the mapping changed after Unicode 3.2 + {"\xe2\x84\xb2oo", L"\x2132oo", "%E2%84%B2oo", + Component(0, 11), CanonHostInfo::BROKEN, -1, ""}, + // U+2F868 (CJK Comp) is disallowed. UTS 46, table 4, row (d) + // Allowed in IDNA 2003, but the mapping changed after Unicode 3.2 + {"\xf0\xaf\xa1\xa8\xe5\xa7\xbb.cn", L"\xd87e\xdc68\x59fb.cn", + "%F0%AF%A1%A8%E5%A7%BB.cn", + Component(0, 24), CanonHostInfo::BROKEN, -1, ""}, + // Maps uppercase letters to lower case letters. UTS 46 table 4 row (e) + {"M\xc3\x9cNCHEN", L"M\xdcNCHEN", "xn--mnchen-3ya", + Component(0, 14), CanonHostInfo::NEUTRAL, -1, ""}, + // Symbol/punctuations are allowed in IDNA 2003/UTS46. + // Not allowed in IDNA 2008. UTS 46 table 4 row (f). + {"\xe2\x99\xa5ny.us", L"\x2665ny.us", "xn--ny-s0x.us", + Component(0, 13), CanonHostInfo::NEUTRAL, -1, ""}, + // U+11013 is new in Unicode 6.0 and is allowed. UTS 46 table 4, row (h) + // We used to allow it because we passed through unassigned code points. + {"\xf0\x91\x80\x93.com", L"\xd804\xdc13.com", "xn--n00d.com", + Component(0, 12), CanonHostInfo::NEUTRAL, -1, ""}, + // U+0602 is disallowed in UTS46/IDNA 2008. UTS 46 table 4, row(i) + // Used to be allowed in INDA 2003. + {"\xd8\x82.eg", L"\x602.eg", "%D8%82.eg", + Component(0, 9), CanonHostInfo::BROKEN, -1, ""}, + // U+20B7 is new in Unicode 5.2 (not a part of IDNA 2003 based + // on Unicode 3.2). We did allow it in the past because we let unassigned + // code point pass. We continue to allow it even though it's a + // "punctuation and symbol" blocked in IDNA 2008. + // UTS 46 table 4, row (j) + {"\xe2\x82\xb7.com", L"\x20b7.com", "xn--wzg.com", + Component(0, 11), CanonHostInfo::NEUTRAL, -1, ""}, + // Maps uppercase letters to lower case letters. + // In IDNA 2003, it's allowed without case-folding + // ( xn--bc-7cb.com ) because it's not defined in Unicode 3.2 + // (added in Unicode 4.1). UTS 46 table 4 row (k) + {"bc\xc8\xba.com", L"bc\x23a.com", "xn--bc-is1a.com", + Component(0, 15), CanonHostInfo::NEUTRAL, -1, ""}, + // BiDi check test + // "Divehi" in Divehi (Thaana script) ends with BidiClass=NSM. + // Disallowed in IDNA 2003 but now allowed in UTS 46/IDNA 2008. + {"\xde\x8b\xde\xa8\xde\x88\xde\xac\xde\x80\xde\xa8", + L"\x78b\x7a8\x788\x7ac\x780\x7a8", "xn--hqbpi0jcw", + Component(0, 13), CanonHostInfo::NEUTRAL, -1, ""}, + // Disallowed in both IDNA 2003 and 2008 with BiDi check. + // Labels starting with a RTL character cannot end with a LTR character. + {"\xd8\xac\xd8\xa7\xd8\xb1xyz", L"\x62c\x627\x631xyz", + "%D8%AC%D8%A7%D8%B1xyz", Component(0, 21), + CanonHostInfo::BROKEN, -1, ""}, + // Labels starting with a RTL character can end with BC=EN (European + // number). Disallowed in IDNA 2003 but now allowed. + {"\xd8\xac\xd8\xa7\xd8\xb1" "2", L"\x62c\x627\x631" L"2", + "xn--2-ymcov", Component(0, 11), + CanonHostInfo::NEUTRAL, -1, ""}, + // Labels starting with a RTL character cannot have "L" characters + // even if it ends with an BC=EN. Disallowed in both IDNA 2003/2008. + {"\xd8\xac\xd8\xa7\xd8\xb1xy2", L"\x62c\x627\x631xy2", + "%D8%AC%D8%A7%D8%B1xy2", Component(0, 21), + CanonHostInfo::BROKEN, -1, ""}, + // Labels starting with a RTL character can end with BC=AN (Arabic number) + // Disallowed in IDNA 2003, but now allowed. + {"\xd8\xac\xd8\xa7\xd8\xb1\xd9\xa2", L"\x62c\x627\x631\x662", + "xn--mgbjq0r", Component(0, 11), + CanonHostInfo::NEUTRAL, -1, ""}, + // Labels starting with a RTL character cannot have "L" characters + // even if it ends with an BC=AN (Arabic number). + // Disallowed in both IDNA 2003/2008. + {"\xd8\xac\xd8\xa7\xd8\xb1xy\xd9\xa2", L"\x62c\x627\x631xy\x662", + "%D8%AC%D8%A7%D8%B1xy%D9%A2", Component(0, 26), + CanonHostInfo::BROKEN, -1, ""}, + // Labels starting with a RTL character cannot mix BC=EN and BC=AN + {"\xd8\xac\xd8\xa7\xd8\xb1xy2\xd9\xa2", L"\x62c\x627\x631xy2\x662", + "%D8%AC%D8%A7%D8%B1xy2%D9%A2", Component(0, 27), + CanonHostInfo::BROKEN, -1, ""}, + // As of Unicode 6.2, U+20CF is not assigned. We do not allow it. + {"\xe2\x83\x8f.com", L"\x20cf.com", "%E2%83%8F.com", + Component(0, 13), CanonHostInfo::BROKEN, -1, ""}, + // U+0080 is not allowed. + {"\xc2\x80.com", L"\x80.com", "%C2%80.com", + Component(0, 10), CanonHostInfo::BROKEN, -1, ""}, + // Mixed UTF-8 and escaped UTF-8 (narrow case) and UTF-16 and escaped + // Mixed UTF-8 and escaped UTF-8 (narrow case) and UTF-16 and escaped + // UTF-8 (wide case). The output should be equivalent to the true wide + // character input above). + {"%E4%BD%A0%E5%A5%BD\xe4\xbd\xa0\xe5\xa5\xbd", + L"%E4%BD%A0%E5%A5%BD\x4f60\x597d", "xn--6qqa088eba", + Component(0, 14), CanonHostInfo::NEUTRAL, -1, ""}, + // Invalid escaped characters should fail and the percents should be + // escaped. + {"%zz%66%a", L"%zz%66%a", "%25zzf%25a", Component(0, 10), + CanonHostInfo::BROKEN, -1, ""}, + // If we get an invalid character that has been escaped. + {"%25", L"%25", "%25", Component(0, 3), + CanonHostInfo::BROKEN, -1, ""}, + {"hello%00", L"hello%00", "hello%00", Component(0, 8), + CanonHostInfo::BROKEN, -1, ""}, + // Escaped numbers should be treated like IP addresses if they are. + {"%30%78%63%30%2e%30%32%35%30.01", L"%30%78%63%30%2e%30%32%35%30.01", + "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, + "C0A80001"}, + {"%30%78%63%30%2e%30%32%35%30.01%2e", L"%30%78%63%30%2e%30%32%35%30.01%2e", + "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, + "C0A80001"}, + // Invalid escaping should trigger the regular host error handling. + {"%3g%78%63%30%2e%30%32%35%30%2E.01", L"%3g%78%63%30%2e%30%32%35%30%2E.01", "%253gxc0.0250..01", Component(0, 17), CanonHostInfo::BROKEN, -1, ""}, + // Something that isn't exactly an IP should get treated as a host and + // spaces escaped. + {"192.168.0.1 hello", L"192.168.0.1 hello", "192.168.0.1%20hello", Component(0, 19), CanonHostInfo::NEUTRAL, -1, ""}, + // Fullwidth and escaped UTF-8 fullwidth should still be treated as IP. + // These are "0Xc0.0250.01" in fullwidth. + {"\xef\xbc\x90%Ef%bc\xb8%ef%Bd%83\xef\xbc\x90%EF%BC%8E\xef\xbc\x90\xef\xbc\x92\xef\xbc\x95\xef\xbc\x90\xef\xbc%8E\xef\xbc\x90\xef\xbc\x91", L"\xff10\xff38\xff43\xff10\xff0e\xff10\xff12\xff15\xff10\xff0e\xff10\xff11", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, "C0A80001"}, + // Broken IP addresses get marked as such. + {"192.168.0.257", L"192.168.0.257", "192.168.0.257", Component(0, 13), CanonHostInfo::BROKEN, -1, ""}, + {"[google.com]", L"[google.com]", "[google.com]", Component(0, 12), CanonHostInfo::BROKEN, -1, ""}, + // Cyrillic letter followed by '(' should return punycode for '(' escaped + // before punycode string was created. I.e. + // if '(' is escaped after punycode is created we would get xn--%28-8tb + // (incorrect). + {"\xd1\x82(", L"\x0442(", "xn--%28-7ed", Component(0, 11), + CanonHostInfo::NEUTRAL, -1, ""}, + // Address with all hexidecimal characters with leading number of 1<<32 + // or greater and should return NEUTRAL rather than BROKEN if not all + // components are numbers. + {"12345678912345.de", L"12345678912345.de", "12345678912345.de", Component(0, 17), CanonHostInfo::NEUTRAL, -1, ""}, + {"1.12345678912345.de", L"1.12345678912345.de", "1.12345678912345.de", Component(0, 19), CanonHostInfo::NEUTRAL, -1, ""}, + {"12345678912345.12345678912345.de", L"12345678912345.12345678912345.de", "12345678912345.12345678912345.de", Component(0, 32), CanonHostInfo::NEUTRAL, -1, ""}, + {"1.2.0xB3A73CE5B59.de", L"1.2.0xB3A73CE5B59.de", "1.2.0xb3a73ce5b59.de", Component(0, 20), CanonHostInfo::NEUTRAL, -1, ""}, + {"12345678912345.0xde", L"12345678912345.0xde", "12345678912345.0xde", Component(0, 19), CanonHostInfo::BROKEN, -1, ""}, + }; + + // CanonicalizeHost() non-verbose. + std::string out_str; + for (size_t i = 0; i < arraysize(host_cases); i++) { + // Narrow version. + if (host_cases[i].input8) { + int host_len = static_cast<int>(strlen(host_cases[i].input8)); + Component in_comp(0, host_len); + Component out_comp; + + out_str.clear(); + StdStringCanonOutput output(&out_str); + + bool success = CanonicalizeHost(host_cases[i].input8, in_comp, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN, + success) << "for input: " << host_cases[i].input8; + EXPECT_EQ(std::string(host_cases[i].expected), out_str) << + "for input: " << host_cases[i].input8; + EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin) << + "for input: " << host_cases[i].input8; + EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len) << + "for input: " << host_cases[i].input8; + } + + // Wide version. + if (host_cases[i].input16) { + base::string16 input16(WStringToUTF16(host_cases[i].input16)); + int host_len = static_cast<int>(input16.length()); + Component in_comp(0, host_len); + Component out_comp; + + out_str.clear(); + StdStringCanonOutput output(&out_str); + + bool success = CanonicalizeHost(input16.c_str(), in_comp, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family != CanonHostInfo::BROKEN, + success); + EXPECT_EQ(std::string(host_cases[i].expected), out_str); + EXPECT_EQ(host_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(host_cases[i].expected_component.len, out_comp.len); + } + } + + // CanonicalizeHostVerbose() + for (size_t i = 0; i < arraysize(host_cases); i++) { + // Narrow version. + if (host_cases[i].input8) { + int host_len = static_cast<int>(strlen(host_cases[i].input8)); + Component in_comp(0, host_len); + + out_str.clear(); + StdStringCanonOutput output(&out_str); + CanonHostInfo host_info; + + CanonicalizeHostVerbose(host_cases[i].input8, in_comp, &output, + &host_info); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(host_cases[i].expected), out_str); + EXPECT_EQ(host_cases[i].expected_component.begin, + host_info.out_host.begin); + EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(std::string(host_cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_cases[i].expected_family == CanonHostInfo::IPV4) { + EXPECT_EQ(host_cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + } + + // Wide version. + if (host_cases[i].input16) { + base::string16 input16(WStringToUTF16(host_cases[i].input16)); + int host_len = static_cast<int>(input16.length()); + Component in_comp(0, host_len); + + out_str.clear(); + StdStringCanonOutput output(&out_str); + CanonHostInfo host_info; + + CanonicalizeHostVerbose(input16.c_str(), in_comp, &output, &host_info); + output.Complete(); + + EXPECT_EQ(host_cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(host_cases[i].expected), out_str); + EXPECT_EQ(host_cases[i].expected_component.begin, + host_info.out_host.begin); + EXPECT_EQ(host_cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(std::string(host_cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_cases[i].expected_family == CanonHostInfo::IPV4) { + EXPECT_EQ(host_cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + } + } +} + +TEST(URLCanonTest, IPv4) { + IPAddressCase cases[] = { + // Empty is not an IP address. + {"", L"", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {".", L".", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Regular IP addresses in different bases. + {"192.168.0.1", L"192.168.0.1", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + {"0300.0250.00.01", L"0300.0250.00.01", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + {"0xC0.0Xa8.0x0.0x1", L"0xC0.0Xa8.0x0.0x1", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + // Non-IP addresses due to invalid characters. + {"192.168.9.com", L"192.168.9.com", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Invalid characters for the base should be rejected. + {"19a.168.0.1", L"19a.168.0.1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"0308.0250.00.01", L"0308.0250.00.01", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"0xCG.0xA8.0x0.0x1", L"0xCG.0xA8.0x0.0x1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // If there are not enough components, the last one should fill them out. + {"192", L"192", "0.0.0.192", Component(0, 9), CanonHostInfo::IPV4, 1, "000000C0"}, + {"0xC0a80001", L"0xC0a80001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 1, "C0A80001"}, + {"030052000001", L"030052000001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 1, "C0A80001"}, + {"000030052000001", L"000030052000001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 1, "C0A80001"}, + {"192.168", L"192.168", "192.0.0.168", Component(0, 11), CanonHostInfo::IPV4, 2, "C00000A8"}, + {"192.0x00A80001", L"192.0x000A80001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 2, "C0A80001"}, + {"0xc0.052000001", L"0xc0.052000001", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 2, "C0A80001"}, + {"192.168.1", L"192.168.1", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, "C0A80001"}, + // Too many components means not an IP address. + {"192.168.0.0.1", L"192.168.0.0.1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // We allow a single trailing dot. + {"192.168.0.1.", L"192.168.0.1.", "192.168.0.1", Component(0, 11), CanonHostInfo::IPV4, 4, "C0A80001"}, + {"192.168.0.1. hello", L"192.168.0.1. hello", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"192.168.0.1..", L"192.168.0.1..", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Two dots in a row means not an IP address. + {"192.168..1", L"192.168..1", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Any numerical overflow should be marked as BROKEN. + {"0x100.0", L"0x100.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0x100.0.0", L"0x100.0.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0x100.0.0.0", L"0x100.0.0.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0x100.0.0", L"0.0x100.0.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0.0x100.0", L"0.0.0x100.0", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0.0.0x100", L"0.0.0.0x100", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0.0x10000", L"0.0.0x10000", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0.0x1000000", L"0.0x1000000", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0x100000000", L"0x100000000", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Repeat the previous tests, minus 1, to verify boundaries. + {"0xFF.0", L"0xFF.0", "255.0.0.0", Component(0, 9), CanonHostInfo::IPV4, 2, "FF000000"}, + {"0xFF.0.0", L"0xFF.0.0", "255.0.0.0", Component(0, 9), CanonHostInfo::IPV4, 3, "FF000000"}, + {"0xFF.0.0.0", L"0xFF.0.0.0", "255.0.0.0", Component(0, 9), CanonHostInfo::IPV4, 4, "FF000000"}, + {"0.0xFF.0.0", L"0.0xFF.0.0", "0.255.0.0", Component(0, 9), CanonHostInfo::IPV4, 4, "00FF0000"}, + {"0.0.0xFF.0", L"0.0.0xFF.0", "0.0.255.0", Component(0, 9), CanonHostInfo::IPV4, 4, "0000FF00"}, + {"0.0.0.0xFF", L"0.0.0.0xFF", "0.0.0.255", Component(0, 9), CanonHostInfo::IPV4, 4, "000000FF"}, + {"0.0.0xFFFF", L"0.0.0xFFFF", "0.0.255.255", Component(0, 11), CanonHostInfo::IPV4, 3, "0000FFFF"}, + {"0.0xFFFFFF", L"0.0xFFFFFF", "0.255.255.255", Component(0, 13), CanonHostInfo::IPV4, 2, "00FFFFFF"}, + {"0xFFFFFFFF", L"0xFFFFFFFF", "255.255.255.255", Component(0, 15), CanonHostInfo::IPV4, 1, "FFFFFFFF"}, + // Old trunctations tests. They're all "BROKEN" now. + {"276.256.0xf1a2.077777", L"276.256.0xf1a2.077777", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"192.168.0.257", L"192.168.0.257", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"192.168.0xa20001", L"192.168.0xa20001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"192.015052000001", L"192.015052000001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"0X12C0a80001", L"0X12C0a80001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"276.1.2", L"276.1.2", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Spaces should be rejected. + {"192.168.0.1 hello", L"192.168.0.1 hello", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Very large numbers. + {"0000000000000300.0x00000000000000fF.00000000000000001", L"0000000000000300.0x00000000000000fF.00000000000000001", "192.255.0.1", Component(0, 11), CanonHostInfo::IPV4, 3, "C0FF0001"}, + {"0000000000000300.0xffffffffFFFFFFFF.3022415481470977", L"0000000000000300.0xffffffffFFFFFFFF.3022415481470977", "", Component(0, 11), CanonHostInfo::BROKEN, -1, ""}, + // A number has no length limit, but long numbers can still overflow. + {"00000000000000000001", L"00000000000000000001", "0.0.0.1", Component(0, 7), CanonHostInfo::IPV4, 1, "00000001"}, + {"0000000000000000100000000000000001", L"0000000000000000100000000000000001", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // If a long component is non-numeric, it's a hostname, *not* a broken IP. + {"0.0.0.000000000000000000z", L"0.0.0.000000000000000000z", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + {"0.0.0.100000000000000000z", L"0.0.0.100000000000000000z", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Truncation of all zeros should still result in 0. + {"0.00.0x.0x0", L"0.00.0x.0x0", "0.0.0.0", Component(0, 7), CanonHostInfo::IPV4, 4, "00000000"}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + // 8-bit version. + Component component(0, static_cast<int>(strlen(cases[i].input8))); + + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + CanonHostInfo host_info; + CanonicalizeIPAddress(cases[i].input8, component, &output1, &host_info); + output1.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_info.family == CanonHostInfo::IPV4) { + EXPECT_STREQ(cases[i].expected, out_str1.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + + // 16-bit version. + base::string16 input16(WStringToUTF16(cases[i].input16)); + component = Component(0, static_cast<int>(input16.length())); + + std::string out_str2; + StdStringCanonOutput output2(&out_str2); + CanonicalizeIPAddress(input16.c_str(), component, &output2, &host_info); + output2.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_info.family == CanonHostInfo::IPV4) { + EXPECT_STREQ(cases[i].expected, out_str2.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + EXPECT_EQ(cases[i].expected_num_ipv4_components, + host_info.num_ipv4_components); + } + } +} + +TEST(URLCanonTest, IPv6) { + IPAddressCase cases[] = { + // Empty is not an IP address. + {"", L"", "", Component(), CanonHostInfo::NEUTRAL, -1, ""}, + // Non-IPs with [:] characters are marked BROKEN. + {":", L":", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[", L"[", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:", L"[:", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"]", L"]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {":]", L":]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[]", L"[]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:]", L"[:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Regular IP address is invalid without bounding '[' and ']'. + {"2001:db8::1", L"2001:db8::1", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[2001:db8::1", L"[2001:db8::1", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"2001:db8::1]", L"2001:db8::1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Regular IP addresses. + {"[::]", L"[::]", "[::]", Component(0,4), CanonHostInfo::IPV6, -1, "00000000000000000000000000000000"}, + {"[::1]", L"[::1]", "[::1]", Component(0,5), CanonHostInfo::IPV6, -1, "00000000000000000000000000000001"}, + {"[1::]", L"[1::]", "[1::]", Component(0,5), CanonHostInfo::IPV6, -1, "00010000000000000000000000000000"}, + + // Leading zeros should be stripped. + {"[000:01:02:003:004:5:6:007]", L"[000:01:02:003:004:5:6:007]", "[0:1:2:3:4:5:6:7]", Component(0,17), CanonHostInfo::IPV6, -1, "00000001000200030004000500060007"}, + + // Upper case letters should be lowercased. + {"[A:b:c:DE:fF:0:1:aC]", L"[A:b:c:DE:fF:0:1:aC]", "[a:b:c:de:ff:0:1:ac]", Component(0,20), CanonHostInfo::IPV6, -1, "000A000B000C00DE00FF0000000100AC"}, + + // The same address can be written with different contractions, but should + // get canonicalized to the same thing. + {"[1:0:0:2::3:0]", L"[1:0:0:2::3:0]", "[1::2:0:0:3:0]", Component(0,14), CanonHostInfo::IPV6, -1, "00010000000000020000000000030000"}, + {"[1::2:0:0:3:0]", L"[1::2:0:0:3:0]", "[1::2:0:0:3:0]", Component(0,14), CanonHostInfo::IPV6, -1, "00010000000000020000000000030000"}, + + // Addresses with embedded IPv4. + {"[::192.168.0.1]", L"[::192.168.0.1]", "[::c0a8:1]", Component(0,10), CanonHostInfo::IPV6, -1, "000000000000000000000000C0A80001"}, + {"[::ffff:192.168.0.1]", L"[::ffff:192.168.0.1]", "[::ffff:c0a8:1]", Component(0,15), CanonHostInfo::IPV6, -1, "00000000000000000000FFFFC0A80001"}, + {"[::eeee:192.168.0.1]", L"[::eeee:192.168.0.1]", "[::eeee:c0a8:1]", Component(0, 15), CanonHostInfo::IPV6, -1, "00000000000000000000EEEEC0A80001"}, + {"[2001::192.168.0.1]", L"[2001::192.168.0.1]", "[2001::c0a8:1]", Component(0, 14), CanonHostInfo::IPV6, -1, "200100000000000000000000C0A80001"}, + {"[1:2:192.168.0.1:5:6]", L"[1:2:192.168.0.1:5:6]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // IPv4 with last component missing. + {"[::ffff:192.1.2]", L"[::ffff:192.1.2]", "[::ffff:c001:2]", Component(0,15), CanonHostInfo::IPV6, -1, "00000000000000000000FFFFC0010002"}, + + // IPv4 using hex. + // TODO(eroman): Should this format be disallowed? + {"[::ffff:0xC0.0Xa8.0x0.0x1]", L"[::ffff:0xC0.0Xa8.0x0.0x1]", "[::ffff:c0a8:1]", Component(0,15), CanonHostInfo::IPV6, -1, "00000000000000000000FFFFC0A80001"}, + + // There may be zeros surrounding the "::" contraction. + {"[0:0::0:0:8]", L"[0:0::0:0:8]", "[::8]", Component(0,5), CanonHostInfo::IPV6, -1, "00000000000000000000000000000008"}, + + {"[2001:db8::1]", L"[2001:db8::1]", "[2001:db8::1]", Component(0,13), CanonHostInfo::IPV6, -1, "20010DB8000000000000000000000001"}, + + // Can only have one "::" contraction in an IPv6 string literal. + {"[2001::db8::1]", L"[2001::db8::1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // No more than 2 consecutive ':'s. + {"[2001:db8:::1]", L"[2001:db8:::1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:::]", L"[:::]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Non-IP addresses due to invalid characters. + {"[2001::.com]", L"[2001::.com]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // If there are not enough components, the last one should fill them out. + // ... omitted at this time ... + // Too many components means not an IP address. Similarly with too few if using IPv4 compat or mapped addresses. + {"[::192.168.0.0.1]", L"[::192.168.0.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[::ffff:192.168.0.0.1]", L"[::ffff:192.168.0.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1:2:3:4:5:6:7:8:9]", L"[1:2:3:4:5:6:7:8:9]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Too many bits (even though 8 comonents, the last one holds 32 bits). + {"[0:0:0:0:0:0:0:192.168.0.1]", L"[0:0:0:0:0:0:0:192.168.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Too many bits specified -- the contraction would have to be zero-length + // to not exceed 128 bits. + {"[1:2:3:4:5:6::192.168.0.1]", L"[1:2:3:4:5:6::192.168.0.1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // The contraction is for 16 bits of zero. + {"[1:2:3:4:5:6::8]", L"[1:2:3:4:5:6::8]", "[1:2:3:4:5:6:0:8]", Component(0,17), CanonHostInfo::IPV6, -1, "00010002000300040005000600000008"}, + + // Cannot have a trailing colon. + {"[1:2:3:4:5:6:7:8:]", L"[1:2:3:4:5:6:7:8:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1:2:3:4:5:6:192.168.0.1:]", L"[1:2:3:4:5:6:192.168.0.1:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Cannot have negative numbers. + {"[-1:2:3:4:5:6:7:8]", L"[-1:2:3:4:5:6:7:8]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Scope ID -- the URL may contain an optional ["%" <scope_id>] section. + // The scope_id should be included in the canonicalized URL, and is an + // unsigned decimal number. + + // Invalid because no ID was given after the percent. + + // Don't allow scope-id + {"[1::%1]", L"[1::%1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1::%eth0]", L"[1::%eth0]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[1::%]", L"[1::%]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[%]", L"[%]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[::%:]", L"[::%:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // Don't allow leading or trailing colons. + {"[:0:0::0:0:8]", L"[:0:0::0:0:8]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[0:0::0:0:8:]", L"[0:0::0:0:8:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + {"[:0:0::0:0:8:]", L"[:0:0::0:0:8:]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + + // We allow a single trailing dot. + // ... omitted at this time ... + // Two dots in a row means not an IP address. + {"[::192.168..1]", L"[::192.168..1]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + // Any non-first components get truncated to one byte. + // ... omitted at this time ... + // Spaces should be rejected. + {"[::1 hello]", L"[::1 hello]", "", Component(), CanonHostInfo::BROKEN, -1, ""}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + // 8-bit version. + Component component(0, static_cast<int>(strlen(cases[i].input8))); + + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + CanonHostInfo host_info; + CanonicalizeIPAddress(cases[i].input8, component, &output1, &host_info); + output1.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())) << "iter " << i << " host " << cases[i].input8; + if (host_info.family == CanonHostInfo::IPV6) { + EXPECT_STREQ(cases[i].expected, out_str1.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, + host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + } + + // 16-bit version. + base::string16 input16(WStringToUTF16(cases[i].input16)); + component = Component(0, static_cast<int>(input16.length())); + + std::string out_str2; + StdStringCanonOutput output2(&out_str2); + CanonicalizeIPAddress(input16.c_str(), component, &output2, &host_info); + output2.Complete(); + + EXPECT_EQ(cases[i].expected_family, host_info.family); + EXPECT_EQ(std::string(cases[i].expected_address_hex), + BytesToHexString(host_info.address, host_info.AddressLength())); + if (host_info.family == CanonHostInfo::IPV6) { + EXPECT_STREQ(cases[i].expected, out_str2.c_str()); + EXPECT_EQ(cases[i].expected_component.begin, host_info.out_host.begin); + EXPECT_EQ(cases[i].expected_component.len, host_info.out_host.len); + } + } +} + +TEST(URLCanonTest, IPEmpty) { + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + CanonHostInfo host_info; + + // This tests tests. + const char spec[] = "192.168.0.1"; + CanonicalizeIPAddress(spec, Component(), &output1, &host_info); + EXPECT_FALSE(host_info.IsIPAddress()); + + CanonicalizeIPAddress(spec, Component(0, 0), &output1, &host_info); + EXPECT_FALSE(host_info.IsIPAddress()); +} + +TEST(URLCanonTest, UserInfo) { + // Note that the canonicalizer should escape and treat empty components as + // not being there. + + // We actually parse a full input URL so we can get the initial components. + struct UserComponentCase { + const char* input; + const char* expected; + Component expected_username; + Component expected_password; + bool expected_success; + } user_info_cases[] = { + {"http://user:pass@host.com/", "user:pass@", Component(0, 4), Component(5, 4), true}, + {"http://@host.com/", "", Component(0, -1), Component(0, -1), true}, + {"http://:@host.com/", "", Component(0, -1), Component(0, -1), true}, + {"http://foo:@host.com/", "foo@", Component(0, 3), Component(0, -1), true}, + {"http://:foo@host.com/", ":foo@", Component(0, 0), Component(1, 3), true}, + {"http://^ :$\t@host.com/", "%5E%20:$%09@", Component(0, 6), Component(7, 4), true}, + {"http://user:pass@/", "user:pass@", Component(0, 4), Component(5, 4), true}, + {"http://%2540:bar@domain.com/", "%2540:bar@", Component(0, 5), Component(6, 3), true }, + + // IE7 compatability: old versions allowed backslashes in usernames, but + // IE7 does not. We disallow it as well. + {"ftp://me\\mydomain:pass@foo.com/", "", Component(0, -1), Component(0, -1), true}, + }; + + for (size_t i = 0; i < arraysize(user_info_cases); i++) { + int url_len = static_cast<int>(strlen(user_info_cases[i].input)); + Parsed parsed; + ParseStandardURL(user_info_cases[i].input, url_len, &parsed); + Component out_user, out_pass; + std::string out_str; + StdStringCanonOutput output1(&out_str); + + bool success = CanonicalizeUserInfo(user_info_cases[i].input, + parsed.username, + user_info_cases[i].input, + parsed.password, + &output1, + &out_user, + &out_pass); + output1.Complete(); + + EXPECT_EQ(user_info_cases[i].expected_success, success); + EXPECT_EQ(std::string(user_info_cases[i].expected), out_str); + EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin); + EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len); + EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin); + EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len); + + // Now try the wide version + out_str.clear(); + StdStringCanonOutput output2(&out_str); + base::string16 wide_input(ConvertUTF8ToUTF16(user_info_cases[i].input)); + success = CanonicalizeUserInfo(wide_input.c_str(), + parsed.username, + wide_input.c_str(), + parsed.password, + &output2, + &out_user, + &out_pass); + output2.Complete(); + + EXPECT_EQ(user_info_cases[i].expected_success, success); + EXPECT_EQ(std::string(user_info_cases[i].expected), out_str); + EXPECT_EQ(user_info_cases[i].expected_username.begin, out_user.begin); + EXPECT_EQ(user_info_cases[i].expected_username.len, out_user.len); + EXPECT_EQ(user_info_cases[i].expected_password.begin, out_pass.begin); + EXPECT_EQ(user_info_cases[i].expected_password.len, out_pass.len); + } +} + +TEST(URLCanonTest, Port) { + // We only need to test that the number gets properly put into the output + // buffer. The parser unit tests will test scanning the number correctly. + // + // Note that the CanonicalizePort will always prepend a colon to the output + // to separate it from the colon that it assumes preceeds it. + struct PortCase { + const char* input; + int default_port; + const char* expected; + Component expected_component; + bool expected_success; + } port_cases[] = { + // Invalid input should be copied w/ failure. + {"as df", 80, ":as%20df", Component(1, 7), false}, + {"-2", 80, ":-2", Component(1, 2), false}, + // Default port should be omitted. + {"80", 80, "", Component(0, -1), true}, + {"8080", 80, ":8080", Component(1, 4), true}, + // PORT_UNSPECIFIED should mean always keep the port. + {"80", PORT_UNSPECIFIED, ":80", Component(1, 2), true}, + }; + + for (size_t i = 0; i < arraysize(port_cases); i++) { + int url_len = static_cast<int>(strlen(port_cases[i].input)); + Component in_comp(0, url_len); + Component out_comp; + std::string out_str; + StdStringCanonOutput output1(&out_str); + bool success = CanonicalizePort(port_cases[i].input, + in_comp, + port_cases[i].default_port, + &output1, + &out_comp); + output1.Complete(); + + EXPECT_EQ(port_cases[i].expected_success, success); + EXPECT_EQ(std::string(port_cases[i].expected), out_str); + EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len); + + // Now try the wide version + out_str.clear(); + StdStringCanonOutput output2(&out_str); + base::string16 wide_input(ConvertUTF8ToUTF16(port_cases[i].input)); + success = CanonicalizePort(wide_input.c_str(), + in_comp, + port_cases[i].default_port, + &output2, + &out_comp); + output2.Complete(); + + EXPECT_EQ(port_cases[i].expected_success, success); + EXPECT_EQ(std::string(port_cases[i].expected), out_str); + EXPECT_EQ(port_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(port_cases[i].expected_component.len, out_comp.len); + } +} + +TEST(URLCanonTest, Path) { + DualComponentCase path_cases[] = { + // ----- path collapsing tests ----- + {"/././foo", L"/././foo", "/foo", Component(0, 4), true}, + {"/./.foo", L"/./.foo", "/.foo", Component(0, 5), true}, + {"/foo/.", L"/foo/.", "/foo/", Component(0, 5), true}, + {"/foo/./", L"/foo/./", "/foo/", Component(0, 5), true}, + // double dots followed by a slash or the end of the string count + {"/foo/bar/..", L"/foo/bar/..", "/foo/", Component(0, 5), true}, + {"/foo/bar/../", L"/foo/bar/../", "/foo/", Component(0, 5), true}, + // don't count double dots when they aren't followed by a slash + {"/foo/..bar", L"/foo/..bar", "/foo/..bar", Component(0, 10), true}, + // some in the middle + {"/foo/bar/../ton", L"/foo/bar/../ton", "/foo/ton", Component(0, 8), true}, + {"/foo/bar/../ton/../../a", L"/foo/bar/../ton/../../a", "/a", Component(0, 2), true}, + // we should not be able to go above the root + {"/foo/../../..", L"/foo/../../..", "/", Component(0, 1), true}, + {"/foo/../../../ton", L"/foo/../../../ton", "/ton", Component(0, 4), true}, + // escaped dots should be unescaped and treated the same as dots + {"/foo/%2e", L"/foo/%2e", "/foo/", Component(0, 5), true}, + {"/foo/%2e%2", L"/foo/%2e%2", "/foo/.%2", Component(0, 8), true}, + {"/foo/%2e./%2e%2e/.%2e/%2e.bar", L"/foo/%2e./%2e%2e/.%2e/%2e.bar", "/..bar", Component(0, 6), true}, + // Multiple slashes in a row should be preserved and treated like empty + // directory names. + {"////../..", L"////../..", "//", Component(0, 2), true}, + + // ----- escaping tests ----- + {"/foo", L"/foo", "/foo", Component(0, 4), true}, + // Valid escape sequence + {"/%20foo", L"/%20foo", "/%20foo", Component(0, 7), true}, + // Invalid escape sequence we should pass through unchanged. + {"/foo%", L"/foo%", "/foo%", Component(0, 5), true}, + {"/foo%2", L"/foo%2", "/foo%2", Component(0, 6), true}, + // Invalid escape sequence: bad characters should be treated the same as + // the sourrounding text, not as escaped (in this case, UTF-8). + {"/foo%2zbar", L"/foo%2zbar", "/foo%2zbar", Component(0, 10), true}, + {"/foo%2\xc2\xa9zbar", NULL, "/foo%2%C2%A9zbar", Component(0, 16), true}, + {NULL, L"/foo%2\xc2\xa9zbar", "/foo%2%C3%82%C2%A9zbar", Component(0, 22), true}, + // Regular characters that are escaped should be unescaped + {"/foo%41%7a", L"/foo%41%7a", "/fooAz", Component(0, 6), true}, + // Funny characters that are unescaped should be escaped + {"/foo\x09\x91%91", NULL, "/foo%09%91%91", Component(0, 13), true}, + {NULL, L"/foo\x09\x91%91", "/foo%09%C2%91%91", Component(0, 16), true}, + // Invalid characters that are escaped should cause a failure. + {"/foo%00%51", L"/foo%00%51", "/foo%00Q", Component(0, 8), false}, + // Some characters should be passed through unchanged regardless of esc. + {"/(%28:%3A%29)", L"/(%28:%3A%29)", "/(%28:%3A%29)", Component(0, 13), true}, + // Characters that are properly escaped should not have the case changed + // of hex letters. + {"/%3A%3a%3C%3c", L"/%3A%3a%3C%3c", "/%3A%3a%3C%3c", Component(0, 13), true}, + // Funny characters that are unescaped should be escaped + {"/foo\tbar", L"/foo\tbar", "/foo%09bar", Component(0, 10), true}, + // Backslashes should get converted to forward slashes + {"\\foo\\bar", L"\\foo\\bar", "/foo/bar", Component(0, 8), true}, + // Hashes found in paths (possibly only when the caller explicitly sets + // the path on an already-parsed URL) should be escaped. + {"/foo#bar", L"/foo#bar", "/foo%23bar", Component(0, 10), true}, + // %7f should be allowed and %3D should not be unescaped (these were wrong + // in a previous version). + {"/%7Ffp3%3Eju%3Dduvgw%3Dd", L"/%7Ffp3%3Eju%3Dduvgw%3Dd", "/%7Ffp3%3Eju%3Dduvgw%3Dd", Component(0, 24), true}, + // @ should be passed through unchanged (escaped or unescaped). + {"/@asdf%40", L"/@asdf%40", "/@asdf%40", Component(0, 9), true}, + + // ----- encoding tests ----- + // Basic conversions + {"/\xe4\xbd\xa0\xe5\xa5\xbd\xe4\xbd\xa0\xe5\xa5\xbd", L"/\x4f60\x597d\x4f60\x597d", "/%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD", Component(0, 37), true}, + // Invalid unicode characters should fail. We only do validation on + // UTF-16 input, so this doesn't happen on 8-bit. + {"/\xef\xb7\x90zyx", NULL, "/%EF%B7%90zyx", Component(0, 13), true}, + {NULL, L"/\xfdd0zyx", "/%EF%BF%BDzyx", Component(0, 13), false}, + }; + + for (size_t i = 0; i < arraysize(path_cases); i++) { + if (path_cases[i].input8) { + int len = static_cast<int>(strlen(path_cases[i].input8)); + Component in_comp(0, len); + Component out_comp; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = + CanonicalizePath(path_cases[i].input8, in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(path_cases[i].expected_success, success); + EXPECT_EQ(path_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(path_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(path_cases[i].expected, out_str); + } + + if (path_cases[i].input16) { + base::string16 input16(WStringToUTF16(path_cases[i].input16)); + int len = static_cast<int>(input16.length()); + Component in_comp(0, len); + Component out_comp; + std::string out_str; + StdStringCanonOutput output(&out_str); + + bool success = + CanonicalizePath(input16.c_str(), in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(path_cases[i].expected_success, success); + EXPECT_EQ(path_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(path_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(path_cases[i].expected, out_str); + } + } + + // Manual test: embedded NULLs should be escaped and the URL should be marked + // as invalid. + const char path_with_null[] = "/ab\0c"; + Component in_comp(0, 5); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizePath(path_with_null, in_comp, &output, &out_comp); + output.Complete(); + EXPECT_FALSE(success); + EXPECT_EQ("/ab%00c", out_str); +} + +TEST(URLCanonTest, Query) { + struct QueryCase { + const char* input8; + const wchar_t* input16; + const char* expected; + } query_cases[] = { + // Regular ASCII case. + {"foo=bar", L"foo=bar", "?foo=bar"}, + // Allow question marks in the query without escaping + {"as?df", L"as?df", "?as?df"}, + // Always escape '#' since it would mark the ref. + {"as#df", L"as#df", "?as%23df"}, + // Escape some questionable 8-bit characters, but never unescape. + {"\x02hello\x7f bye", L"\x02hello\x7f bye", "?%02hello%7F%20bye"}, + {"%40%41123", L"%40%41123", "?%40%41123"}, + // Chinese input/output + {"q=\xe4\xbd\xa0\xe5\xa5\xbd", L"q=\x4f60\x597d", "?q=%E4%BD%A0%E5%A5%BD"}, + // Invalid UTF-8/16 input should be replaced with invalid characters. + {"q=\xed\xed", L"q=\xd800\xd800", "?q=%EF%BF%BD%EF%BF%BD"}, + // Don't allow < or > because sometimes they are used for XSS if the + // URL is echoed in content. Firefox does this, IE doesn't. + {"q=<asdf>", L"q=<asdf>", "?q=%3Casdf%3E"}, + // Escape double quotemarks in the query. + {"q=\"asdf\"", L"q=\"asdf\"", "?q=%22asdf%22"}, + }; + + for (size_t i = 0; i < arraysize(query_cases); i++) { + Component out_comp; + + if (query_cases[i].input8) { + int len = static_cast<int>(strlen(query_cases[i].input8)); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(query_cases[i].input8, in_comp, NULL, &output, + &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + + if (query_cases[i].input16) { + base::string16 input16(WStringToUTF16(query_cases[i].input16)); + int len = static_cast<int>(input16.length()); + Component in_comp(0, len); + std::string out_str; + + StdStringCanonOutput output(&out_str); + CanonicalizeQuery(input16.c_str(), in_comp, NULL, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(query_cases[i].expected, out_str); + } + } + + // Extra test for input with embedded NULL; + std::string out_str; + StdStringCanonOutput output(&out_str); + Component out_comp; + CanonicalizeQuery("a \x00z\x01", Component(0, 5), NULL, &output, &out_comp); + output.Complete(); + EXPECT_EQ("?a%20%00z%01", out_str); +} + +TEST(URLCanonTest, Ref) { + // Refs are trivial, it just checks the encoding. + DualComponentCase ref_cases[] = { + // Regular one, we shouldn't escape spaces, et al. + {"hello, world", L"hello, world", "#hello, world", Component(1, 12), true}, + // UTF-8/wide input should be preserved + {"\xc2\xa9", L"\xa9", "#\xc2\xa9", Component(1, 2), true}, + // Test a characer that takes > 16 bits (U+10300 = old italic letter A) + {"\xF0\x90\x8C\x80ss", L"\xd800\xdf00ss", "#\xF0\x90\x8C\x80ss", Component(1, 6), true}, + // Escaping should be preserved unchanged, even invalid ones + {"%41%a", L"%41%a", "#%41%a", Component(1, 5), true}, + // Invalid UTF-8/16 input should be flagged and the input made valid + {"\xc2", NULL, "#\xef\xbf\xbd", Component(1, 3), true}, + {NULL, L"\xd800\x597d", "#\xef\xbf\xbd\xe5\xa5\xbd", Component(1, 6), true}, + // Test a Unicode invalid character. + {"a\xef\xb7\x90", L"a\xfdd0", "#a\xef\xbf\xbd", Component(1, 4), true}, + // Refs can have # signs and we should preserve them. + {"asdf#qwer", L"asdf#qwer", "#asdf#qwer", Component(1, 9), true}, + {"#asdf", L"#asdf", "##asdf", Component(1, 5), true}, + }; + + for (size_t i = 0; i < arraysize(ref_cases); i++) { + // 8-bit input + if (ref_cases[i].input8) { + int len = static_cast<int>(strlen(ref_cases[i].input8)); + Component in_comp(0, len); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + CanonicalizeRef(ref_cases[i].input8, in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(ref_cases[i].expected, out_str); + } + + // 16-bit input + if (ref_cases[i].input16) { + base::string16 input16(WStringToUTF16(ref_cases[i].input16)); + int len = static_cast<int>(input16.length()); + Component in_comp(0, len); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + CanonicalizeRef(input16.c_str(), in_comp, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(ref_cases[i].expected_component.begin, out_comp.begin); + EXPECT_EQ(ref_cases[i].expected_component.len, out_comp.len); + EXPECT_EQ(ref_cases[i].expected, out_str); + } + } + + // Try one with an embedded NULL. It should be stripped. + const char null_input[5] = "ab\x00z"; + Component null_input_component(0, 4); + Component out_comp; + + std::string out_str; + StdStringCanonOutput output(&out_str); + CanonicalizeRef(null_input, null_input_component, &output, &out_comp); + output.Complete(); + + EXPECT_EQ(1, out_comp.begin); + EXPECT_EQ(3, out_comp.len); + EXPECT_EQ("#abz", out_str); +} + +TEST(URLCanonTest, CanonicalizeStandardURL) { + // The individual component canonicalize tests should have caught the cases + // for each of those components. Here, we just need to test that the various + // parts are included or excluded properly, and have the correct separators. + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + } cases[] = { + {"http://www.google.com/foo?bar=baz#", "http://www.google.com/foo?bar=baz#", true}, + {"http://[www.google.com]/", "http://[www.google.com]/", false}, + {"ht\ttp:@www.google.com:80/;p?#", "ht%09tp://www.google.com:80/;p?#", false}, + {"http:////////user:@google.com:99?foo", "http://user@google.com:99/?foo", true}, + {"www.google.com", ":www.google.com/", true}, + {"http://192.0x00A80001", "http://192.168.0.1/", true}, + {"http://www/foo%2Ehtml", "http://www/foo.html", true}, + {"http://user:pass@/", "http://user:pass@/", false}, + {"http://%25DOMAIN:foobar@foodomain.com/", "http://%25DOMAIN:foobar@foodomain.com/", true}, + + // Backslashes should get converted to forward slashes. + {"http:\\\\www.google.com\\foo", "http://www.google.com/foo", true}, + + // Busted refs shouldn't make the whole thing fail. + {"http://www.google.com/asdf#\xc2", "http://www.google.com/asdf#\xef\xbf\xbd", true}, + + // Basic port tests. + {"http://foo:80/", "http://foo/", true}, + {"http://foo:81/", "http://foo:81/", true}, + {"httpa://foo:80/", "httpa://foo:80/", true}, + {"http://foo:-80/", "http://foo:-80/", false}, + + {"https://foo:443/", "https://foo/", true}, + {"https://foo:80/", "https://foo:80/", true}, + {"ftp://foo:21/", "ftp://foo/", true}, + {"ftp://foo:80/", "ftp://foo:80/", true}, + {"gopher://foo:70/", "gopher://foo/", true}, + {"gopher://foo:443/", "gopher://foo:443/", true}, + {"ws://foo:80/", "ws://foo/", true}, + {"ws://foo:81/", "ws://foo:81/", true}, + {"ws://foo:443/", "ws://foo:443/", true}, + {"ws://foo:815/", "ws://foo:815/", true}, + {"wss://foo:80/", "wss://foo:80/", true}, + {"wss://foo:81/", "wss://foo:81/", true}, + {"wss://foo:443/", "wss://foo/", true}, + {"wss://foo:815/", "wss://foo:815/", true}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast<int>(strlen(cases[i].input)); + Parsed parsed; + ParseStandardURL(cases[i].input, url_len, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeStandardURL( + cases[i].input, url_len, parsed, NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + } +} + +// The codepath here is the same as for regular canonicalization, so we just +// need to test that things are replaced or not correctly. +TEST(URLCanonTest, ReplaceStandardURL) { + ReplaceCase replace_cases[] = { + // Common case of truncating the path. + {"http://www.google.com/foo?bar=baz#ref", NULL, NULL, NULL, NULL, NULL, "/", kDeleteComp, kDeleteComp, "http://www.google.com/"}, + // Replace everything + {"http://a:b@google.com:22/foo;bar?baz@cat", "https", "me", "pw", "host.com", "99", "/path", "query", "ref", "https://me:pw@host.com:99/path?query#ref"}, + // Replace nothing + {"http://a:b@google.com:22/foo?baz@cat", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "http://a:b@google.com:22/foo?baz@cat"}, + // Replace scheme with filesystem. The result is garbage, but you asked + // for it. + {"http://a:b@google.com:22/foo?baz@cat", "filesystem", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "filesystem://a:b@google.com:22/foo?baz@cat"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast<int>(strlen(cur.base)); + Parsed parsed; + ParseStandardURL(cur.base, base_len, &parsed); + + Replacements<char> r; + typedef Replacements<char> R; // Clean up syntax. + + // Note that for the scheme we pass in a different clear function since + // there is no function to clear the scheme. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceStandardURL(replace_cases[i].base, parsed, r, NULL, &output, + &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } + + // The path pointer should be ignored if the address is invalid. + { + const char src[] = "http://www.google.com/here_is_the_path"; + int src_len = static_cast<int>(strlen(src)); + + Parsed parsed; + ParseStandardURL(src, src_len, &parsed); + + // Replace the path to 0 length string. By using 1 as the string address, + // the test should get an access violation if it tries to dereference it. + Replacements<char> r; + r.SetPath(reinterpret_cast<char*>(0x00000001), Component(0, 0)); + std::string out_str1; + StdStringCanonOutput output1(&out_str1); + Parsed new_parsed; + ReplaceStandardURL(src, parsed, r, NULL, &output1, &new_parsed); + output1.Complete(); + EXPECT_STREQ("http://www.google.com/", out_str1.c_str()); + + // Same with an "invalid" path. + r.SetPath(reinterpret_cast<char*>(0x00000001), Component()); + std::string out_str2; + StdStringCanonOutput output2(&out_str2); + ReplaceStandardURL(src, parsed, r, NULL, &output2, &new_parsed); + output2.Complete(); + EXPECT_STREQ("http://www.google.com/", out_str2.c_str()); + } +} + +TEST(URLCanonTest, ReplaceFileURL) { + ReplaceCase replace_cases[] = { + // Replace everything + {"file:///C:/gaba?query#ref", NULL, NULL, NULL, "filer", NULL, "/foo", "b", "c", "file://filer/foo?b#c"}, + // Replace nothing + {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///C:/gaba?query#ref"}, + // Clear non-path components (common) + {"file:///C:/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "file:///C:/gaba"}, + // Replace path with something that doesn't begin with a slash and make + // sure it gets added properly. + {"file:///C:/gaba", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "file:///interesting/"}, + {"file:///home/gaba?query#ref", NULL, NULL, NULL, "filer", NULL, "/foo", "b", "c", "file://filer/foo?b#c"}, + {"file:///home/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///home/gaba?query#ref"}, + {"file:///home/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "file:///home/gaba"}, + {"file:///home/gaba", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "file:///interesting/"}, + // Replace scheme -- shouldn't do anything. + {"file:///C:/gaba?query#ref", "http", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "file:///C:/gaba?query#ref"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast<int>(strlen(cur.base)); + Parsed parsed; + ParseFileURL(cur.base, base_len, &parsed); + + Replacements<char> r; + typedef Replacements<char> R; // Clean up syntax. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceFileURL(cur.base, parsed, r, NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, ReplaceFileSystemURL) { + ReplaceCase replace_cases[] = { + // Replace everything in the outer URL. + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, "/foo", "b", "c", "filesystem:file:///temporary/foo?b#c"}, + // Replace nothing + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "filesystem:file:///temporary/gaba?query#ref"}, + // Clear non-path components (common) + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, NULL, kDeleteComp, kDeleteComp, "filesystem:file:///temporary/gaba"}, + // Replace path with something that doesn't begin with a slash and make + // sure it gets added properly. + {"filesystem:file:///temporary/gaba?query#ref", NULL, NULL, NULL, NULL, NULL, "interesting/", NULL, NULL, "filesystem:file:///temporary/interesting/?query#ref"}, + // Replace scheme -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", "http", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace username -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", NULL, "u2", NULL, NULL, NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace password -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", NULL, NULL, "pw2", NULL, NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace host -- shouldn't do anything. + {"filesystem:http://u:p@bar.com/t/gaba?query#ref", NULL, NULL, NULL, "foo.com", NULL, NULL, NULL, NULL, "filesystem:http://u:p@bar.com/t/gaba?query#ref"}, + // Replace port -- shouldn't do anything. + {"filesystem:http://u:p@bar.com:40/t/gaba?query#ref", NULL, NULL, NULL, NULL, "41", NULL, NULL, NULL, "filesystem:http://u:p@bar.com:40/t/gaba?query#ref"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast<int>(strlen(cur.base)); + Parsed parsed; + ParseFileSystemURL(cur.base, base_len, &parsed); + + Replacements<char> r; + typedef Replacements<char> R; // Clean up syntax. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceFileSystemURL(cur.base, parsed, r, NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, ReplacePathURL) { + ReplaceCase replace_cases[] = { + // Replace everything + {"data:foo", "javascript", NULL, NULL, NULL, NULL, "alert('foo?');", NULL, NULL, "javascript:alert('foo?');"}, + // Replace nothing + {"data:foo", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "data:foo"}, + // Replace one or the other + {"data:foo", "javascript", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "javascript:foo"}, + {"data:foo", NULL, NULL, NULL, NULL, NULL, "bar", NULL, NULL, "data:bar"}, + {"data:foo", NULL, NULL, NULL, NULL, NULL, kDeleteComp, NULL, NULL, "data:"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast<int>(strlen(cur.base)); + Parsed parsed; + ParsePathURL(cur.base, base_len, false, &parsed); + + Replacements<char> r; + typedef Replacements<char> R; // Clean up syntax. + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplacePathURL(cur.base, parsed, r, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, ReplaceMailtoURL) { + ReplaceCase replace_cases[] = { + // Replace everything + {"mailto:jon@foo.com?body=sup", "mailto", NULL, NULL, NULL, NULL, "addr1", "to=tony", NULL, "mailto:addr1?to=tony"}, + // Replace nothing + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, "mailto:jon@foo.com?body=sup"}, + // Replace the path + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", NULL, NULL, "mailto:jason?body=sup"}, + // Replace the query + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "custom=1", NULL, "mailto:jon@foo.com?custom=1"}, + // Replace the path and query + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "jason", "custom=1", NULL, "mailto:jason?custom=1"}, + // Set the query to empty (should leave trailing question mark) + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "", NULL, "mailto:jon@foo.com?"}, + // Clear the query + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, NULL, "|", NULL, "mailto:jon@foo.com"}, + // Clear the path + {"mailto:jon@foo.com?body=sup", NULL, NULL, NULL, NULL, NULL, "|", NULL, NULL, "mailto:?body=sup"}, + // Clear the path + query + {"mailto:", NULL, NULL, NULL, NULL, NULL, "|", "|", NULL, "mailto:"}, + // Setting the ref should have no effect + {"mailto:addr1", NULL, NULL, NULL, NULL, NULL, NULL, NULL, "BLAH", "mailto:addr1"}, + }; + + for (size_t i = 0; i < arraysize(replace_cases); i++) { + const ReplaceCase& cur = replace_cases[i]; + int base_len = static_cast<int>(strlen(cur.base)); + Parsed parsed; + ParseMailtoURL(cur.base, base_len, &parsed); + + Replacements<char> r; + typedef Replacements<char> R; + SetupReplComp(&R::SetScheme, &R::ClearRef, &r, cur.scheme); + SetupReplComp(&R::SetUsername, &R::ClearUsername, &r, cur.username); + SetupReplComp(&R::SetPassword, &R::ClearPassword, &r, cur.password); + SetupReplComp(&R::SetHost, &R::ClearHost, &r, cur.host); + SetupReplComp(&R::SetPort, &R::ClearPort, &r, cur.port); + SetupReplComp(&R::SetPath, &R::ClearPath, &r, cur.path); + SetupReplComp(&R::SetQuery, &R::ClearQuery, &r, cur.query); + SetupReplComp(&R::SetRef, &R::ClearRef, &r, cur.ref); + + std::string out_str; + StdStringCanonOutput output(&out_str); + Parsed out_parsed; + ReplaceMailtoURL(cur.base, parsed, r, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(replace_cases[i].expected, out_str); + } +} + +TEST(URLCanonTest, CanonicalizeFileURL) { + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + Component expected_host; + Component expected_path; + } cases[] = { +#ifdef _WIN32 + // Windows-style paths + {"file:c:\\foo\\bar.html", "file:///C:/foo/bar.html", true, Component(), Component(7, 16)}, + {" File:c|////foo\\bar.html", "file:///C:////foo/bar.html", true, Component(), Component(7, 19)}, + {"file:", "file:///", true, Component(), Component(7, 1)}, + {"file:UNChost/path", "file://unchost/path", true, Component(7, 7), Component(14, 5)}, + // CanonicalizeFileURL supports absolute Windows style paths for IE + // compatability. Note that the caller must decide that this is a file + // URL itself so it can call the file canonicalizer. This is usually + // done automatically as part of relative URL resolving. + {"c:\\foo\\bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"C|/foo/bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"/C|\\foo\\bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"//C|/foo/bar", "file:///C:/foo/bar", true, Component(), Component(7, 11)}, + {"//server/file", "file://server/file", true, Component(7, 6), Component(13, 5)}, + {"\\\\server\\file", "file://server/file", true, Component(7, 6), Component(13, 5)}, + {"/\\server/file", "file://server/file", true, Component(7, 6), Component(13, 5)}, + // We should preserve the number of slashes after the colon for IE + // compatability, except when there is none, in which case we should + // add one. + {"file:c:foo/bar.html", "file:///C:/foo/bar.html", true, Component(), Component(7, 16)}, + {"file:/\\/\\C:\\\\//foo\\bar.html", "file:///C:////foo/bar.html", true, Component(), Component(7, 19)}, + // Three slashes should be non-UNC, even if there is no drive spec (IE + // does this, which makes the resulting request invalid). + {"file:///foo/bar.txt", "file:///foo/bar.txt", true, Component(), Component(7, 12)}, + // TODO(brettw) we should probably fail for invalid host names, which + // would change the expected result on this test. We also currently allow + // colon even though it's probably invalid, because its currently the + // "natural" result of the way the canonicalizer is written. There doesn't + // seem to be a strong argument for why allowing it here would be bad, so + // we just tolerate it and the load will fail later. + {"FILE:/\\/\\7:\\\\//foo\\bar.html", "file://7:////foo/bar.html", false, Component(7, 2), Component(9, 16)}, + {"file:filer/home\\me", "file://filer/home/me", true, Component(7, 5), Component(12, 8)}, + // Make sure relative paths can't go above the "C:" + {"file:///C:/foo/../../../bar.html", "file:///C:/bar.html", true, Component(), Component(7, 12)}, + // Busted refs shouldn't make the whole thing fail. + {"file:///C:/asdf#\xc2", "file:///C:/asdf#\xef\xbf\xbd", true, Component(), Component(7, 8)}, +#else + // Unix-style paths + {"file:///home/me", "file:///home/me", true, Component(), Component(7, 8)}, + // Windowsy ones should get still treated as Unix-style. + {"file:c:\\foo\\bar.html", "file:///c:/foo/bar.html", true, Component(), Component(7, 16)}, + {"file:c|//foo\\bar.html", "file:///c%7C//foo/bar.html", true, Component(), Component(7, 19)}, + // file: tests from WebKit (LayoutTests/fast/loader/url-parse-1.html) + {"//", "file:///", true, Component(), Component(7, 1)}, + {"///", "file:///", true, Component(), Component(7, 1)}, + {"///test", "file:///test", true, Component(), Component(7, 5)}, + {"file://test", "file://test/", true, Component(7, 4), Component(11, 1)}, + {"file://localhost", "file://localhost/", true, Component(7, 9), Component(16, 1)}, + {"file://localhost/", "file://localhost/", true, Component(7, 9), Component(16, 1)}, + {"file://localhost/test", "file://localhost/test", true, Component(7, 9), Component(16, 5)}, +#endif // _WIN32 + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast<int>(strlen(cases[i].input)); + Parsed parsed; + ParseFileURL(cases[i].input, url_len, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeFileURL(cases[i].input, url_len, parsed, NULL, + &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + + // Make sure the spec was properly identified, the file canonicalizer has + // different code for writing the spec. + EXPECT_EQ(0, out_parsed.scheme.begin); + EXPECT_EQ(4, out_parsed.scheme.len); + + EXPECT_EQ(cases[i].expected_host.begin, out_parsed.host.begin); + EXPECT_EQ(cases[i].expected_host.len, out_parsed.host.len); + + EXPECT_EQ(cases[i].expected_path.begin, out_parsed.path.begin); + EXPECT_EQ(cases[i].expected_path.len, out_parsed.path.len); + } +} + +TEST(URLCanonTest, CanonicalizeFileSystemURL) { + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + } cases[] = { + {"Filesystem:htTp://www.Foo.com:80/tempoRary", "filesystem:http://www.foo.com/tempoRary/", true}, + {"filesystem:httpS://www.foo.com/temporary/", "filesystem:https://www.foo.com/temporary/", true}, + {"filesystem:http://www.foo.com//", "filesystem:http://www.foo.com//", false}, + {"filesystem:http://www.foo.com/persistent/bob?query#ref", "filesystem:http://www.foo.com/persistent/bob?query#ref", true}, + {"filesystem:fIle://\\temporary/", "filesystem:file:///temporary/", true}, + {"filesystem:fiLe:///temporary", "filesystem:file:///temporary/", true}, + {"filesystem:File:///temporary/Bob?qUery#reF", "filesystem:file:///temporary/Bob?qUery#reF", true}, + }; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast<int>(strlen(cases[i].input)); + Parsed parsed; + ParseFileSystemURL(cases[i].input, url_len, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeFileSystemURL(cases[i].input, url_len, parsed, + NULL, &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + + // Make sure the spec was properly identified, the filesystem canonicalizer + // has different code for writing the spec. + EXPECT_EQ(0, out_parsed.scheme.begin); + EXPECT_EQ(10, out_parsed.scheme.len); + if (success) + EXPECT_GT(out_parsed.path.len, 0); + } +} + +TEST(URLCanonTest, CanonicalizePathURL) { + // Path URLs should get canonicalized schemes but nothing else. + struct PathCase { + const char* input; + const char* expected; + } path_cases[] = { + {"javascript:", "javascript:"}, + {"JavaScript:Foo", "javascript:Foo"}, + {":\":This /is interesting;?#", ":\":This /is interesting;?#"}, + }; + + for (size_t i = 0; i < arraysize(path_cases); i++) { + int url_len = static_cast<int>(strlen(path_cases[i].input)); + Parsed parsed; + ParsePathURL(path_cases[i].input, url_len, true, &parsed); + + Parsed out_parsed; + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizePathURL(path_cases[i].input, url_len, parsed, + &output, &out_parsed); + output.Complete(); + + EXPECT_TRUE(success); + EXPECT_EQ(path_cases[i].expected, out_str); + + EXPECT_EQ(0, out_parsed.host.begin); + EXPECT_EQ(-1, out_parsed.host.len); + + // When we end with a colon at the end, there should be no path. + if (path_cases[i].input[url_len - 1] == ':') { + EXPECT_EQ(0, out_parsed.GetContent().begin); + EXPECT_EQ(-1, out_parsed.GetContent().len); + } + } +} + +TEST(URLCanonTest, CanonicalizeMailtoURL) { + struct URLCase { + const char* input; + const char* expected; + bool expected_success; + Component expected_path; + Component expected_query; + } cases[] = { + {"mailto:addr1", "mailto:addr1", true, Component(7, 5), Component()}, + {"mailto:addr1@foo.com", "mailto:addr1@foo.com", true, Component(7, 13), Component()}, + // Trailing whitespace is stripped. + {"MaIlTo:addr1 \t ", "mailto:addr1", true, Component(7, 5), Component()}, + {"MaIlTo:addr1?to=jon", "mailto:addr1?to=jon", true, Component(7, 5), Component(13,6)}, + {"mailto:addr1,addr2", "mailto:addr1,addr2", true, Component(7, 11), Component()}, + {"mailto:addr1, addr2", "mailto:addr1, addr2", true, Component(7, 12), Component()}, + {"mailto:addr1%2caddr2", "mailto:addr1%2caddr2", true, Component(7, 13), Component()}, + {"mailto:\xF0\x90\x8C\x80", "mailto:%F0%90%8C%80", true, Component(7, 12), Component()}, + // Null character should be escaped to %00 + {"mailto:addr1\0addr2?foo", "mailto:addr1%00addr2?foo", true, Component(7, 13), Component(21, 3)}, + // Invalid -- UTF-8 encoded surrogate value. + {"mailto:\xed\xa0\x80", "mailto:%EF%BF%BD", false, Component(7, 9), Component()}, + {"mailto:addr1?", "mailto:addr1?", true, Component(7, 5), Component(13, 0)}, + }; + + // Define outside of loop to catch bugs where components aren't reset + Parsed parsed; + Parsed out_parsed; + + for (size_t i = 0; i < arraysize(cases); i++) { + int url_len = static_cast<int>(strlen(cases[i].input)); + if (i == 8) { + // The 9th test case purposely has a '\0' in it -- don't count it + // as the string terminator. + url_len = 22; + } + ParseMailtoURL(cases[i].input, url_len, &parsed); + + std::string out_str; + StdStringCanonOutput output(&out_str); + bool success = CanonicalizeMailtoURL(cases[i].input, url_len, parsed, + &output, &out_parsed); + output.Complete(); + + EXPECT_EQ(cases[i].expected_success, success); + EXPECT_EQ(cases[i].expected, out_str); + + // Make sure the spec was properly identified + EXPECT_EQ(0, out_parsed.scheme.begin); + EXPECT_EQ(6, out_parsed.scheme.len); + + EXPECT_EQ(cases[i].expected_path.begin, out_parsed.path.begin); + EXPECT_EQ(cases[i].expected_path.len, out_parsed.path.len); + + EXPECT_EQ(cases[i].expected_query.begin, out_parsed.query.begin); + EXPECT_EQ(cases[i].expected_query.len, out_parsed.query.len); + } +} + +#ifndef WIN32 + +TEST(URLCanonTest, _itoa_s) { + // We fill the buffer with 0xff to ensure that it's getting properly + // null-terminated. We also allocate one byte more than what we tell + // _itoa_s about, and ensure that the extra byte is untouched. + char buf[6]; + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(12, buf, sizeof(buf) - 1, 10)); + EXPECT_STREQ("12", buf); + EXPECT_EQ('\xFF', buf[3]); + + // Test the edge cases - exactly the buffer size and one over + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(1234, buf, sizeof(buf) - 1, 10)); + EXPECT_STREQ("1234", buf); + EXPECT_EQ('\xFF', buf[5]); + + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(EINVAL, _itoa_s(12345, buf, sizeof(buf) - 1, 10)); + EXPECT_EQ('\xFF', buf[5]); // should never write to this location + + // Test the template overload (note that this will see the full buffer) + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(12, buf, 10)); + EXPECT_STREQ("12", buf); + EXPECT_EQ('\xFF', buf[3]); + + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(12345, buf, 10)); + EXPECT_STREQ("12345", buf); + + EXPECT_EQ(EINVAL, _itoa_s(123456, buf, 10)); + + // Test that radix 16 is supported. + memset(buf, 0xff, sizeof(buf)); + EXPECT_EQ(0, _itoa_s(1234, buf, sizeof(buf) - 1, 16)); + EXPECT_STREQ("4d2", buf); + EXPECT_EQ('\xFF', buf[5]); +} + +TEST(URLCanonTest, _itow_s) { + // We fill the buffer with 0xff to ensure that it's getting properly + // null-terminated. We also allocate one byte more than what we tell + // _itoa_s about, and ensure that the extra byte is untouched. + base::char16 buf[6]; + const char fill_mem = 0xff; + const base::char16 fill_char = 0xffff; + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(0, _itow_s(12, buf, sizeof(buf) / 2 - 1, 10)); + EXPECT_EQ(WStringToUTF16(L"12"), base::string16(buf)); + EXPECT_EQ(fill_char, buf[3]); + + // Test the edge cases - exactly the buffer size and one over + EXPECT_EQ(0, _itow_s(1234, buf, sizeof(buf) / 2 - 1, 10)); + EXPECT_EQ(WStringToUTF16(L"1234"), base::string16(buf)); + EXPECT_EQ(fill_char, buf[5]); + + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(EINVAL, _itow_s(12345, buf, sizeof(buf) / 2 - 1, 10)); + EXPECT_EQ(fill_char, buf[5]); // should never write to this location + + // Test the template overload (note that this will see the full buffer) + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(0, _itow_s(12, buf, 10)); + EXPECT_EQ(WStringToUTF16(L"12"), base::string16(buf)); + EXPECT_EQ(fill_char, buf[3]); + + memset(buf, fill_mem, sizeof(buf)); + EXPECT_EQ(0, _itow_s(12345, buf, 10)); + EXPECT_EQ(WStringToUTF16(L"12345"), base::string16(buf)); + + EXPECT_EQ(EINVAL, _itow_s(123456, buf, 10)); +} + +#endif // !WIN32 + +// Returns true if the given two structures are the same. +static bool ParsedIsEqual(const Parsed& a, const Parsed& b) { + return a.scheme.begin == b.scheme.begin && a.scheme.len == b.scheme.len && + a.username.begin == b.username.begin && a.username.len == b.username.len && + a.password.begin == b.password.begin && a.password.len == b.password.len && + a.host.begin == b.host.begin && a.host.len == b.host.len && + a.port.begin == b.port.begin && a.port.len == b.port.len && + a.path.begin == b.path.begin && a.path.len == b.path.len && + a.query.begin == b.query.begin && a.query.len == b.query.len && + a.ref.begin == b.ref.begin && a.ref.len == b.ref.len; +} + +TEST(URLCanonTest, ResolveRelativeURL) { + struct RelativeCase { + const char* base; // Input base URL: MUST BE CANONICAL + bool is_base_hier; // Is the base URL hierarchical + bool is_base_file; // Tells us if the base is a file URL. + const char* test; // Input URL to test against. + bool succeed_relative; // Whether we expect IsRelativeURL to succeed + bool is_rel; // Whether we expect |test| to be relative or not. + bool succeed_resolve; // Whether we expect ResolveRelativeURL to succeed. + const char* resolved; // What we expect in the result when resolving. + } rel_cases[] = { + // Basic absolute input. + {"http://host/a", true, false, "http://another/", true, false, false, NULL}, + {"http://host/a", true, false, "http:////another/", true, false, false, NULL}, + // Empty relative URLs should only remove the ref part of the URL, + // leaving the rest unchanged. + {"http://foo/bar", true, false, "", true, true, true, "http://foo/bar"}, + {"http://foo/bar#ref", true, false, "", true, true, true, "http://foo/bar"}, + {"http://foo/bar#", true, false, "", true, true, true, "http://foo/bar"}, + // Spaces at the ends of the relative path should be ignored. + {"http://foo/bar", true, false, " another ", true, true, true, "http://foo/another"}, + {"http://foo/bar", true, false, " . ", true, true, true, "http://foo/"}, + {"http://foo/bar", true, false, " \t ", true, true, true, "http://foo/bar"}, + // Matching schemes without two slashes are treated as relative. + {"http://host/a", true, false, "http:path", true, true, true, "http://host/path"}, + {"http://host/a/", true, false, "http:path", true, true, true, "http://host/a/path"}, + {"http://host/a", true, false, "http:/path", true, true, true, "http://host/path"}, + {"http://host/a", true, false, "HTTP:/path", true, true, true, "http://host/path"}, + // Nonmatching schemes are absolute. + {"http://host/a", true, false, "https:host2", true, false, false, NULL}, + {"http://host/a", true, false, "htto:/host2", true, false, false, NULL}, + // Absolute path input + {"http://host/a", true, false, "/b/c/d", true, true, true, "http://host/b/c/d"}, + {"http://host/a", true, false, "\\b\\c\\d", true, true, true, "http://host/b/c/d"}, + {"http://host/a", true, false, "/b/../c", true, true, true, "http://host/c"}, + {"http://host/a?b#c", true, false, "/b/../c", true, true, true, "http://host/c"}, + {"http://host/a", true, false, "\\b/../c?x#y", true, true, true, "http://host/c?x#y"}, + {"http://host/a?b#c", true, false, "/b/../c?x#y", true, true, true, "http://host/c?x#y"}, + // Relative path input + {"http://host/a", true, false, "b", true, true, true, "http://host/b"}, + {"http://host/a", true, false, "bc/de", true, true, true, "http://host/bc/de"}, + {"http://host/a/", true, false, "bc/de?query#ref", true, true, true, "http://host/a/bc/de?query#ref"}, + {"http://host/a/", true, false, ".", true, true, true, "http://host/a/"}, + {"http://host/a/", true, false, "..", true, true, true, "http://host/"}, + {"http://host/a/", true, false, "./..", true, true, true, "http://host/"}, + {"http://host/a/", true, false, "../.", true, true, true, "http://host/"}, + {"http://host/a/", true, false, "././.", true, true, true, "http://host/a/"}, + {"http://host/a?query#ref", true, false, "../../../foo", true, true, true, "http://host/foo"}, + // Query input + {"http://host/a", true, false, "?foo=bar", true, true, true, "http://host/a?foo=bar"}, + {"http://host/a?x=y#z", true, false, "?", true, true, true, "http://host/a?"}, + {"http://host/a?x=y#z", true, false, "?foo=bar#com", true, true, true, "http://host/a?foo=bar#com"}, + // Ref input + {"http://host/a", true, false, "#ref", true, true, true, "http://host/a#ref"}, + {"http://host/a#b", true, false, "#", true, true, true, "http://host/a#"}, + {"http://host/a?foo=bar#hello", true, false, "#bye", true, true, true, "http://host/a?foo=bar#bye"}, + // Non-hierarchical base: no relative handling. Relative input should + // error, and if a scheme is present, it should be treated as absolute. + {"data:foobar", false, false, "baz.html", false, false, false, NULL}, + {"data:foobar", false, false, "data:baz", true, false, false, NULL}, + {"data:foobar", false, false, "data:/base", true, false, false, NULL}, + // Non-hierarchical base: absolute input should succeed. + {"data:foobar", false, false, "http://host/", true, false, false, NULL}, + {"data:foobar", false, false, "http:host", true, false, false, NULL}, + // Invalid schemes should be treated as relative. + {"http://foo/bar", true, false, "./asd:fgh", true, true, true, "http://foo/asd:fgh"}, + {"http://foo/bar", true, false, ":foo", true, true, true, "http://foo/:foo"}, + {"http://foo/bar", true, false, " hello world", true, true, true, "http://foo/hello%20world"}, + {"data:asdf", false, false, ":foo", false, false, false, NULL}, + {"data:asdf", false, false, "bad(':foo')", false, false, false, NULL}, + // We should treat semicolons like any other character in URL resolving + {"http://host/a", true, false, ";foo", true, true, true, "http://host/;foo"}, + {"http://host/a;", true, false, ";foo", true, true, true, "http://host/;foo"}, + {"http://host/a", true, false, ";/../bar", true, true, true, "http://host/bar"}, + // Relative URLs can also be written as "//foo/bar" which is relative to + // the scheme. In this case, it would take the old scheme, so for http + // the example would resolve to "http://foo/bar". + {"http://host/a", true, false, "//another", true, true, true, "http://another/"}, + {"http://host/a", true, false, "//another/path?query#ref", true, true, true, "http://another/path?query#ref"}, + {"http://host/a", true, false, "///another/path", true, true, true, "http://another/path"}, + {"http://host/a", true, false, "//Another\\path", true, true, true, "http://another/path"}, + {"http://host/a", true, false, "//", true, true, false, "http:"}, + // IE will also allow one or the other to be a backslash to get the same + // behavior. + {"http://host/a", true, false, "\\/another/path", true, true, true, "http://another/path"}, + {"http://host/a", true, false, "/\\Another\\path", true, true, true, "http://another/path"}, +#ifdef WIN32 + // Resolving against Windows file base URLs. + {"file:///C:/foo", true, true, "http://host/", true, false, false, NULL}, + {"file:///C:/foo", true, true, "bar", true, true, true, "file:///C:/bar"}, + {"file:///C:/foo", true, true, "../../../bar.html", true, true, true, "file:///C:/bar.html"}, + {"file:///C:/foo", true, true, "/../bar.html", true, true, true, "file:///C:/bar.html"}, + // But two backslashes on Windows should be UNC so should be treated + // as absolute. + {"http://host/a", true, false, "\\\\another\\path", true, false, false, NULL}, + // IE doesn't support drive specs starting with two slashes. It fails + // immediately and doesn't even try to load. We fix it up to either + // an absolute path or UNC depending on what it looks like. + {"file:///C:/something", true, true, "//c:/foo", true, true, true, "file:///C:/foo"}, + {"file:///C:/something", true, true, "//localhost/c:/foo", true, true, true, "file:///C:/foo"}, + // Windows drive specs should be allowed and treated as absolute. + {"file:///C:/foo", true, true, "c:", true, false, false, NULL}, + {"file:///C:/foo", true, true, "c:/foo", true, false, false, NULL}, + {"http://host/a", true, false, "c:\\foo", true, false, false, NULL}, + // Relative paths with drive letters should be allowed when the base is + // also a file. + {"file:///C:/foo", true, true, "/z:/bar", true, true, true, "file:///Z:/bar"}, + // Treat absolute paths as being off of the drive. + {"file:///C:/foo", true, true, "/bar", true, true, true, "file:///C:/bar"}, + {"file://localhost/C:/foo", true, true, "/bar", true, true, true, "file://localhost/C:/bar"}, + {"file:///C:/foo/com/", true, true, "/bar", true, true, true, "file:///C:/bar"}, + // On Windows, two slashes without a drive letter when the base is a file + // means that the path is UNC. + {"file:///C:/something", true, true, "//somehost/path", true, true, true, "file://somehost/path"}, + {"file:///C:/something", true, true, "/\\//somehost/path", true, true, true, "file://somehost/path"}, +#else + // On Unix we fall back to relative behavior since there's nothing else + // reasonable to do. + {"http://host/a", true, false, "\\\\Another\\path", true, true, true, "http://another/path"}, +#endif + // Even on Windows, we don't allow relative drive specs when the base + // is not file. + {"http://host/a", true, false, "/c:\\foo", true, true, true, "http://host/c:/foo"}, + {"http://host/a", true, false, "//c:\\foo", true, true, true, "http://c/foo"}, + // Ensure that ports aren't allowed for hosts relative to a file url. + // Although the result string shows a host:port portion, the call to + // resolve the relative URL returns false, indicating parse failure, + // which is what is required. + {"file:///foo.txt", true, true, "//host:80/bar.txt", true, true, false, "file://host:80/bar.txt"}, + // Filesystem URL tests; filesystem URLs are only valid and relative if + // they have no scheme, e.g. "./index.html". There's no valid equivalent + // to http:index.html. + {"filesystem:http://host/t/path", true, false, "filesystem:http://host/t/path2", true, false, false, NULL}, + {"filesystem:http://host/t/path", true, false, "filesystem:https://host/t/path2", true, false, false, NULL}, + {"filesystem:http://host/t/path", true, false, "http://host/t/path2", true, false, false, NULL}, + {"http://host/t/path", true, false, "filesystem:http://host/t/path2", true, false, false, NULL}, + {"filesystem:http://host/t/path", true, false, "./path2", true, true, true, "filesystem:http://host/t/path2"}, + {"filesystem:http://host/t/path/", true, false, "path2", true, true, true, "filesystem:http://host/t/path/path2"}, + {"filesystem:http://host/t/path", true, false, "filesystem:http:path2", true, false, false, NULL}, + // Absolute URLs are still not relative to a non-standard base URL. + {"about:blank", false, false, "http://X/A", true, false, true, ""}, + {"about:blank", false, false, "content://content.Provider/", true, false, true, ""}, + }; + + for (size_t i = 0; i < arraysize(rel_cases); i++) { + const RelativeCase& cur_case = rel_cases[i]; + + Parsed parsed; + int base_len = static_cast<int>(strlen(cur_case.base)); + if (cur_case.is_base_file) + ParseFileURL(cur_case.base, base_len, &parsed); + else if (cur_case.is_base_hier) + ParseStandardURL(cur_case.base, base_len, &parsed); + else + ParsePathURL(cur_case.base, base_len, false, &parsed); + + // First see if it is relative. + int test_len = static_cast<int>(strlen(cur_case.test)); + bool is_relative; + Component relative_component; + bool succeed_is_rel = IsRelativeURL( + cur_case.base, parsed, cur_case.test, test_len, cur_case.is_base_hier, + &is_relative, &relative_component); + + EXPECT_EQ(cur_case.succeed_relative, succeed_is_rel) << + "succeed is rel failure on " << cur_case.test; + EXPECT_EQ(cur_case.is_rel, is_relative) << + "is rel failure on " << cur_case.test; + // Now resolve it. + if (succeed_is_rel && is_relative && cur_case.is_rel) { + std::string resolved; + StdStringCanonOutput output(&resolved); + Parsed resolved_parsed; + + bool succeed_resolve = ResolveRelativeURL( + cur_case.base, parsed, cur_case.is_base_file, cur_case.test, + relative_component, NULL, &output, &resolved_parsed); + output.Complete(); + + EXPECT_EQ(cur_case.succeed_resolve, succeed_resolve); + EXPECT_EQ(cur_case.resolved, resolved) << " on " << cur_case.test; + + // Verify that the output parsed structure is the same as parsing a + // the URL freshly. + Parsed ref_parsed; + int resolved_len = static_cast<int>(resolved.size()); + if (cur_case.is_base_file) { + ParseFileURL(resolved.c_str(), resolved_len, &ref_parsed); + } else if (cur_case.is_base_hier) { + ParseStandardURL(resolved.c_str(), resolved_len, &ref_parsed); + } else { + ParsePathURL(resolved.c_str(), resolved_len, false, &ref_parsed); + } + EXPECT_TRUE(ParsedIsEqual(ref_parsed, resolved_parsed)); + } + } +} + +// It used to be when we did a replacement with a long buffer of UTF-16 +// characters, we would get invalid data in the URL. This is because the buffer +// it used to hold the UTF-8 data was resized, while some pointers were still +// kept to the old buffer that was removed. +TEST(URLCanonTest, ReplacementOverflow) { + const char src[] = "file:///C:/foo/bar"; + int src_len = static_cast<int>(strlen(src)); + Parsed parsed; + ParseFileURL(src, src_len, &parsed); + + // Override two components, the path with something short, and the query with + // sonething long enough to trigger the bug. + Replacements<base::char16> repl; + base::string16 new_query; + for (int i = 0; i < 4800; i++) + new_query.push_back('a'); + + base::string16 new_path(WStringToUTF16(L"/foo")); + repl.SetPath(new_path.c_str(), Component(0, 4)); + repl.SetQuery(new_query.c_str(), + Component(0, static_cast<int>(new_query.length()))); + + // Call ReplaceComponents on the string. It doesn't matter if we call it for + // standard URLs, file URLs, etc, since they will go to the same replacement + // function that was buggy. + Parsed repl_parsed; + std::string repl_str; + StdStringCanonOutput repl_output(&repl_str); + ReplaceFileURL(src, parsed, repl, NULL, &repl_output, &repl_parsed); + repl_output.Complete(); + + // Generate the expected string and check. + std::string expected("file:///foo?"); + for (size_t i = 0; i < new_query.length(); i++) + expected.push_back('a'); + EXPECT_TRUE(expected == repl_str); +} + +} // namespace url
diff --git a/src/url/url_constants.cc b/src/url/url_constants.cc new file mode 100644 index 0000000..2dc1478 --- /dev/null +++ b/src/url/url_constants.cc
@@ -0,0 +1,28 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "url/url_constants.h" + +namespace url { + +const char kAboutBlankURL[] = "about:blank"; + +const char kAboutScheme[] = "about"; +const char kBlobScheme[] = "blob"; +const char kContentScheme[] = "content"; +const char kDataScheme[] = "data"; +const char kFileScheme[] = "file"; +const char kFileSystemScheme[] = "filesystem"; +const char kFtpScheme[] = "ftp"; +const char kGopherScheme[] = "gopher"; +const char kHttpScheme[] = "http"; +const char kHttpsScheme[] = "https"; +const char kJavaScriptScheme[] = "javascript"; +const char kMailToScheme[] = "mailto"; +const char kWsScheme[] = "ws"; +const char kWssScheme[] = "wss"; + +const char kStandardSchemeSeparator[] = "://"; + +} // namespace url
diff --git a/src/url/url_constants.h b/src/url/url_constants.h new file mode 100644 index 0000000..c48dafc --- /dev/null +++ b/src/url/url_constants.h
@@ -0,0 +1,35 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_CONSTANTS_H_ +#define URL_URL_CONSTANTS_H_ + +#include "url/url_export.h" + +namespace url { + +URL_EXPORT extern const char kAboutBlankURL[]; + +URL_EXPORT extern const char kAboutScheme[]; +URL_EXPORT extern const char kBlobScheme[]; +// The content scheme is specific to Android for identifying a stored file. +URL_EXPORT extern const char kContentScheme[]; +URL_EXPORT extern const char kDataScheme[]; +URL_EXPORT extern const char kFileScheme[]; +URL_EXPORT extern const char kFileSystemScheme[]; +URL_EXPORT extern const char kFtpScheme[]; +URL_EXPORT extern const char kGopherScheme[]; +URL_EXPORT extern const char kHttpScheme[]; +URL_EXPORT extern const char kHttpsScheme[]; +URL_EXPORT extern const char kJavaScriptScheme[]; +URL_EXPORT extern const char kMailToScheme[]; +URL_EXPORT extern const char kWsScheme[]; +URL_EXPORT extern const char kWssScheme[]; + +// Used to separate a standard scheme and the hostname: "://". +URL_EXPORT extern const char kStandardSchemeSeparator[]; + +} // namespace url + +#endif // URL_URL_CONSTANTS_H_
diff --git a/src/url/url_export.h b/src/url/url_export.h new file mode 100644 index 0000000..15ef19e --- /dev/null +++ b/src/url/url_export.h
@@ -0,0 +1,33 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_EXPORT_H_ +#define URL_URL_EXPORT_H_ + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(URL_IMPLEMENTATION) +#define URL_EXPORT __declspec(dllexport) +#else +#define URL_EXPORT __declspec(dllimport) +#endif // defined(URL_IMPLEMENTATION) + +#else // !defined(WIN32) + +#if defined(URL_IMPLEMENTATION) +#define URL_EXPORT __attribute__((visibility("default"))) +#else +#define URL_EXPORT +#endif // defined(URL_IMPLEMENTATION) + +#endif // defined(WIN32) + +#else // !defined(COMPONENT_BUILD) + +#define URL_EXPORT + +#endif // define(COMPONENT_BUILD) + +#endif // URL_URL_EXPORT_H_
diff --git a/src/url/url_file.h b/src/url/url_file.h new file mode 100644 index 0000000..540cb25 --- /dev/null +++ b/src/url/url_file.h
@@ -0,0 +1,83 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_FILE_H_ +#define URL_URL_FILE_H_ + +// Provides shared functions used by the internals of the parser and +// canonicalizer for file URLs. Do not use outside of these modules. + +#include "url/url_parse_internal.h" + +namespace url { + +#ifdef WIN32 + +// We allow both "c:" and "c|" as drive identifiers. +inline bool IsWindowsDriveSeparator(base::char16 ch) { + return ch == ':' || ch == '|'; +} +inline bool IsWindowsDriveLetter(base::char16 ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); +} + +#endif // WIN32 + +// Returns the index of the next slash in the input after the given index, or +// spec_len if the end of the input is reached. +template<typename CHAR> +inline int FindNextSlash(const CHAR* spec, int begin_index, int spec_len) { + int idx = begin_index; + while (idx < spec_len && !IsURLSlash(spec[idx])) + idx++; + return idx; +} + +#ifdef WIN32 + +// Returns true if the start_offset in the given spec looks like it begins a +// drive spec, for example "c:". This function explicitly handles start_offset +// values that are equal to or larger than the spec_len to simplify callers. +// +// If this returns true, the spec is guaranteed to have a valid drive letter +// plus a colon starting at |start_offset|. +template<typename CHAR> +inline bool DoesBeginWindowsDriveSpec(const CHAR* spec, int start_offset, + int spec_len) { + int remaining_len = spec_len - start_offset; + if (remaining_len < 2) + return false; // Not enough room. + if (!IsWindowsDriveLetter(spec[start_offset])) + return false; // Doesn't start with a valid drive letter. + if (!IsWindowsDriveSeparator(spec[start_offset + 1])) + return false; // Isn't followed with a drive separator. + return true; +} + +// Returns true if the start_offset in the given text looks like it begins a +// UNC path, for example "\\". This function explicitly handles start_offset +// values that are equal to or larger than the spec_len to simplify callers. +// +// When strict_slashes is set, this function will only accept backslashes as is +// standard for Windows. Otherwise, it will accept forward slashes as well +// which we use for a lot of URL handling. +template<typename CHAR> +inline bool DoesBeginUNCPath(const CHAR* text, + int start_offset, + int len, + bool strict_slashes) { + int remaining_len = len - start_offset; + if (remaining_len < 2) + return false; + + if (strict_slashes) + return text[start_offset] == '\\' && text[start_offset + 1] == '\\'; + return IsURLSlash(text[start_offset]) && IsURLSlash(text[start_offset + 1]); +} + +#endif // WIN32 + +} // namespace url + +#endif // URL_URL_FILE_H_
diff --git a/src/url/url_parse.h b/src/url/url_parse.h new file mode 100644 index 0000000..3b9c546 --- /dev/null +++ b/src/url/url_parse.h
@@ -0,0 +1,11 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_PARSE_H_ +#define URL_URL_PARSE_H_ + +// TODO(tfarina): Remove this file when the callers are updated. +#include "url/third_party/mozilla/url_parse.h" + +#endif // URL_URL_PARSE_H_
diff --git a/googleurl/src/url_parse_file.cc b/src/url/url_parse_file.cc similarity index 78% rename from googleurl/src/url_parse_file.cc rename to src/url/url_parse_file.cc index 2e8429f..c08ddc6 100644 --- a/googleurl/src/url_parse_file.cc +++ b/src/url/url_parse_file.cc
@@ -1,36 +1,11 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. #include "base/logging.h" -#include "googleurl/src/url_file.h" -#include "googleurl/src/url_parse.h" -#include "googleurl/src/url_parse_internal.h" +#include "url/url_file.h" +#include "url/url_parse.h" +#include "url/url_parse_internal.h" // Interesting IE file:isms... // @@ -63,7 +38,7 @@ // it looks like an absolute drive path. Also, slashes and backslashes are // equally valid here. -namespace url_parse { +namespace url { namespace { @@ -135,8 +110,9 @@ } // Backend for the external functions that operates on either char type. -// We are handed the character after the "file:" at the beginning of the spec. -// Usually this is a slash, but needn't be; we allow paths like "file:c:\foo". +// Handles cases where there is a scheme, but also when handed the first +// character following the "file:" at the beginning of the spec. If so, +// this is usually a slash, but needn't be; we allow paths like "file:c:\foo". template<typename CHAR> void DoParseFileURL(const CHAR* spec, int spec_len, Parsed* parsed) { DCHECK(spec_len >= 0); @@ -155,8 +131,8 @@ int begin = 0; TrimURL(spec, &begin, &spec_len); - // Find the scheme. - int num_slashes; + // Find the scheme, if any. + int num_slashes = CountConsecutiveSlashes(spec, begin, spec_len); int after_scheme; int after_slashes; #ifdef WIN32 @@ -165,7 +141,6 @@ // links like "c:/foo/bar" or "//foo/bar". This is also called by the // relative URL resolver when it determines there is an absolute URL, which // may give us input like "/c:/foo". - num_slashes = CountConsecutiveSlashes(spec, begin, spec_len); after_slashes = begin + num_slashes; if (DoesBeginWindowsDriveSpec(spec, after_slashes, spec_len)) { // Windows path, don't try to extract the scheme (for example, "c:\foo"). @@ -178,7 +153,12 @@ } else #endif { - if (ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) { + // ExtractScheme doesn't understand the possibility of filenames with + // colons in them, in which case it returns the entire spec up to the + // colon as the scheme. So handle /foo.c:5 as a file but foo.c:5 as + // the foo.c: scheme. + if (!num_slashes && + ExtractScheme(&spec[begin], spec_len - begin, &parsed->scheme)) { // Offset the results since we gave ExtractScheme a substring. parsed->scheme.begin += begin; after_scheme = parsed->scheme.end() + 1; @@ -198,7 +178,6 @@ } num_slashes = CountConsecutiveSlashes(spec, after_scheme, spec_len); - after_slashes = after_scheme + num_slashes; #ifdef WIN32 // Check whether the input is a drive again. We checked above for windows @@ -236,8 +215,8 @@ DoParseFileURL(url, url_len, parsed); } -void ParseFileURL(const char16* url, int url_len, Parsed* parsed) { +void ParseFileURL(const base::char16* url, int url_len, Parsed* parsed) { DoParseFileURL(url, url_len, parsed); } -} // namespace url_parse +} // namespace url
diff --git a/src/url/url_parse_internal.h b/src/url/url_parse_internal.h new file mode 100644 index 0000000..4070b7e --- /dev/null +++ b/src/url/url_parse_internal.h
@@ -0,0 +1,91 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_PARSE_INTERNAL_H_ +#define URL_URL_PARSE_INTERNAL_H_ + +// Contains common inline helper functions used by the URL parsing routines. + +#include "url/url_parse.h" + +namespace url { + +// We treat slashes and backslashes the same for IE compatability. +inline bool IsURLSlash(base::char16 ch) { + return ch == '/' || ch == '\\'; +} + +// Returns true if we should trim this character from the URL because it is a +// space or a control character. +inline bool ShouldTrimFromURL(base::char16 ch) { + return ch <= ' '; +} + +// Given an already-initialized begin index and length, this shrinks the range +// to eliminate "should-be-trimmed" characters. Note that the length does *not* +// indicate the length of untrimmed data from |*begin|, but rather the position +// in the input string (so the string starts at character |*begin| in the spec, +// and goes until |*len|). +template<typename CHAR> +inline void TrimURL(const CHAR* spec, int* begin, int* len, + bool trim_path_end = true) { + // Strip leading whitespace and control characters. + while (*begin < *len && ShouldTrimFromURL(spec[*begin])) + (*begin)++; + + if (trim_path_end) { + // Strip trailing whitespace and control characters. We need the >i test + // for when the input string is all blanks; we don't want to back past the + // input. + while (*len > *begin && ShouldTrimFromURL(spec[*len - 1])) + (*len)--; + } +} + +// Counts the number of consecutive slashes starting at the given offset +// in the given string of the given length. +template<typename CHAR> +inline int CountConsecutiveSlashes(const CHAR *str, + int begin_offset, int str_len) { + int count = 0; + while (begin_offset + count < str_len && + IsURLSlash(str[begin_offset + count])) + ++count; + return count; +} + +// Internal functions in url_parse.cc that parse the path, that is, everything +// following the authority section. The input is the range of everything +// following the authority section, and the output is the identified ranges. +// +// This is designed for the file URL parser or other consumers who may do +// special stuff at the beginning, but want regular path parsing, it just +// maps to the internal parsing function for paths. +void ParsePathInternal(const char* spec, + const Component& path, + Component* filepath, + Component* query, + Component* ref); +void ParsePathInternal(const base::char16* spec, + const Component& path, + Component* filepath, + Component* query, + Component* ref); + + +// Given a spec and a pointer to the character after the colon following the +// scheme, this parses it and fills in the structure, Every item in the parsed +// structure is filled EXCEPT for the scheme, which is untouched. +void ParseAfterScheme(const char* spec, + int spec_len, + int after_scheme, + Parsed* parsed); +void ParseAfterScheme(const base::char16* spec, + int spec_len, + int after_scheme, + Parsed* parsed); + +} // namespace url + +#endif // URL_URL_PARSE_INTERNAL_H_
diff --git a/googleurl/src/url_parse_unittest.cc b/src/url/url_parse_unittest.cc similarity index 71% rename from googleurl/src/url_parse_unittest.cc rename to src/url/url_parse_unittest.cc index 299488b..dedd663 100644 --- a/googleurl/src/url_parse_unittest.cc +++ b/src/url/url_parse_unittest.cc
@@ -1,42 +1,12 @@ -// Copyright 2007, Google 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: -// -// * 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 Google Inc. 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. +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. -#include "base/basictypes.h" -#include "googleurl/src/url_parse.h" +#include "url/url_parse.h" + +#include "base/macros.h" #include "testing/base/public/gunit.h" - -// Some implementations of base/basictypes.h may define ARRAYSIZE. -// If it's not defined, we define it to the ARRAYSIZE_UNSAFE macro -// which is in our version of basictypes.h. -#ifndef ARRAYSIZE -#define ARRAYSIZE ARRAYSIZE_UNSAFE -#endif +#include "url/url_parse.h" // Interesting IE file:isms... // @@ -68,6 +38,7 @@ // it looks like an absolute drive path. Also, slashes and backslashes are // equally valid here. +namespace url { namespace { // Used for regular URL parse cases. @@ -101,10 +72,24 @@ const char* query; }; +// More complicated version of URLParseCase for testing filesystem URLs. +struct FileSystemURLParseCase { + const char* input; + + const char* inner_scheme; + const char* inner_username; + const char* inner_password; + const char* inner_host; + int inner_port; + const char* inner_path; + const char* path; + const char* query; + const char* ref; +}; bool ComponentMatches(const char* input, const char* reference, - const url_parse::Component& component) { + const Component& component) { // If the component is nonexistant (length == -1), it should begin at 0. EXPECT_TRUE(component.len >= 0 || component.len == -1); @@ -124,13 +109,11 @@ return strncmp(reference, &input[component.begin], component.len) == 0; } -void ExpectInvalidComponent(const url_parse::Component& component) { +void ExpectInvalidComponent(const Component& component) { EXPECT_EQ(0, component.begin); EXPECT_EQ(-1, component.len); } -} // namespace - // Parsed ---------------------------------------------------------------------- TEST(URLParser, Length) { @@ -155,24 +138,23 @@ for (size_t i = 0; i < arraysize(length_cases); i++) { int true_length = static_cast<int>(strlen(length_cases[i])); - url_parse::Parsed parsed; - url_parse::ParseStandardURL(length_cases[i], true_length, &parsed); + Parsed parsed; + ParseStandardURL(length_cases[i], true_length, &parsed); EXPECT_EQ(true_length, parsed.Length()); } } TEST(URLParser, CountCharactersBefore) { - using namespace url_parse; struct CountCase { const char* url; Parsed::ComponentType component; bool include_delimiter; int expected_count; } count_cases[] = { - // Test each possibility in the case where all components are present. -// 0 1 2 -// 0123456789012345678901 + // Test each possibility in the case where all components are present. + // 0 1 2 + // 0123456789012345678901 {"http://u:p@h:8/p?q#r", Parsed::SCHEME, true, 0}, {"http://u:p@h:8/p?q#r", Parsed::SCHEME, false, 0}, {"http://u:p@h:8/p?q#r", Parsed::USERNAME, true, 7}, @@ -212,15 +194,15 @@ {"file:///c:/foo", Parsed::HOST, true, 7}, {"file:///c:/foo", Parsed::PATH, true, 7}, }; - for (size_t i = 0; i < ARRAYSIZE(count_cases); i++) { + for (size_t i = 0; i < arraysize(count_cases); i++) { int length = static_cast<int>(strlen(count_cases[i].url)); // Simple test to distinguish file and standard URLs. - url_parse::Parsed parsed; + Parsed parsed; if (length > 0 && count_cases[i].url[0] == 'f') - url_parse::ParseFileURL(count_cases[i].url, length, &parsed); + ParseFileURL(count_cases[i].url, length, &parsed); else - url_parse::ParseStandardURL(count_cases[i].url, length, &parsed); + ParseStandardURL(count_cases[i].url, length, &parsed); int chars_before = parsed.CountCharactersBefore( count_cases[i].component, count_cases[i].include_delimiter); @@ -328,12 +310,12 @@ TEST(URLParser, Standard) { // Declared outside for loop to try to catch cases in init() where we forget - // to reset something that is reset by the construtor. - url_parse::Parsed parsed; + // to reset something that is reset by the constructor. + Parsed parsed; for (size_t i = 0; i < arraysize(cases); i++) { const char* url = cases[i].input; - url_parse::ParseStandardURL(url, static_cast<int>(strlen(url)), &parsed); - int port = url_parse::ParsePort(url, parsed.port); + ParseStandardURL(url, static_cast<int>(strlen(url)), &parsed); + int port = ParsePort(url, parsed.port); EXPECT_TRUE(ComponentMatches(url, cases[i].scheme, parsed.scheme)); EXPECT_TRUE(ComponentMatches(url, cases[i].username, parsed.username)); @@ -354,40 +336,37 @@ {":", "", NULL}, {":/", "", "/"}, {"/", NULL, "/"}, -{" This is \\interesting// \t", NULL, "This is \\interesting//"}, +{" This is \\interesting// \t", NULL, "This is \\interesting// \t"}, {"about:", "about", NULL}, {"about:blank", "about", "blank"}, -{" about: blank ", "about", " blank"}, -{"javascript :alert(\"He:/l\\l#o?foo\"); ", "javascript ", "alert(\"He:/l\\l#o?foo\");"}, +{" about: blank ", "about", " blank "}, +{"javascript :alert(\"He:/l\\l#o?foo\"); ", "javascript ", "alert(\"He:/l\\l#o?foo\"); "}, }; TEST(URLParser, PathURL) { // Declared outside for loop to try to catch cases in init() where we forget // to reset something that is reset by the construtor. - url_parse::Parsed parsed; + Parsed parsed; for (size_t i = 0; i < arraysize(path_cases); i++) { const char* url = path_cases[i].input; - url_parse::ParsePathURL(url, static_cast<int>(strlen(url)), &parsed); + ParsePathURL(url, static_cast<int>(strlen(url)), false, &parsed); - EXPECT_TRUE(ComponentMatches(url, path_cases[i].scheme, parsed.scheme)); - EXPECT_TRUE(ComponentMatches(url, path_cases[i].path, parsed.path)); + EXPECT_TRUE(ComponentMatches(url, path_cases[i].scheme, parsed.scheme)) + << i; + EXPECT_TRUE(ComponentMatches(url, path_cases[i].path, parsed.GetContent())) + << i; // The remaining components are never used for path urls. ExpectInvalidComponent(parsed.username); ExpectInvalidComponent(parsed.password); ExpectInvalidComponent(parsed.host); ExpectInvalidComponent(parsed.port); - ExpectInvalidComponent(parsed.query); - ExpectInvalidComponent(parsed.ref); } } -#ifdef WIN32 - -// WindowsFile ---------------------------------------------------------------- - -// Various incarnations of file URLs. These are for Windows only. +// Various incarnations of file URLs. static URLParseCase file_cases[] = { +#ifdef WIN32 {"file:server", "file", NULL, NULL, "server", -1, NULL, NULL, NULL}, {" file: server \t", "file", NULL, NULL, " server",-1, NULL, NULL, NULL}, {"FiLe:c|", "FiLe", NULL, NULL, NULL, -1, "c|", NULL, NULL}, @@ -415,29 +394,96 @@ // Queries and refs are valid for file URLs as well. {"file:///C:/foo.html?#", "file", NULL, NULL, NULL, -1, "/C:/foo.html", "", ""}, {"file:///C:/foo.html?query=yes#ref", "file", NULL, NULL, NULL, -1, "/C:/foo.html", "query=yes", "ref"}, +#else // WIN32 + // No slashes. + {"file:", "file", NULL, NULL, NULL, -1, NULL, NULL, NULL}, + {"file:path", "file", NULL, NULL, NULL, -1, "path", NULL, NULL}, + {"file:path/", "file", NULL, NULL, NULL, -1, "path/", NULL, NULL}, + {"file:path/f.txt", "file", NULL, NULL, NULL, -1, "path/f.txt", NULL, NULL}, + // One slash. + {"file:/", "file", NULL, NULL, NULL, -1, "/", NULL, NULL}, + {"file:/path", "file", NULL, NULL, NULL, -1, "/path", NULL, NULL}, + {"file:/path/", "file", NULL, NULL, NULL, -1, "/path/", NULL, NULL}, + {"file:/path/f.txt", "file", NULL, NULL, NULL, -1, "/path/f.txt", NULL, NULL}, + // Two slashes. + {"file://", "file", NULL, NULL, NULL, -1, NULL, NULL, NULL}, + {"file://server", "file", NULL, NULL, "server", -1, NULL, NULL, NULL}, + {"file://server/", "file", NULL, NULL, "server", -1, "/", NULL, NULL}, + {"file://server/f.txt", "file", NULL, NULL, "server", -1, "/f.txt", NULL, NULL}, + // Three slashes. + {"file:///", "file", NULL, NULL, NULL, -1, "/", NULL, NULL}, + {"file:///path", "file", NULL, NULL, NULL, -1, "/path", NULL, NULL}, + {"file:///path/", "file", NULL, NULL, NULL, -1, "/path/", NULL, NULL}, + {"file:///path/f.txt", "file", NULL, NULL, NULL, -1, "/path/f.txt", NULL, NULL}, + // More than three slashes. + {"file:////", "file", NULL, NULL, NULL, -1, "/", NULL, NULL}, + {"file:////path", "file", NULL, NULL, NULL, -1, "/path", NULL, NULL}, + {"file:////path/", "file", NULL, NULL, NULL, -1, "/path/", NULL, NULL}, + {"file:////path/f.txt", "file", NULL, NULL, NULL, -1, "/path/f.txt", NULL, NULL}, + // Schemeless URLs + {"path/f.txt", NULL, NULL, NULL, NULL, -1, "path/f.txt", NULL, NULL}, + {"path:80/f.txt", "path", NULL, NULL, NULL, -1, "80/f.txt", NULL, NULL}, + {"path/f.txt:80", "path/f.txt",NULL, NULL, NULL, -1, "80", NULL, NULL}, // Wrong. + {"/path/f.txt", NULL, NULL, NULL, NULL, -1, "/path/f.txt", NULL, NULL}, + {"/path:80/f.txt", NULL, NULL, NULL, NULL, -1, "/path:80/f.txt",NULL, NULL}, + {"/path/f.txt:80", NULL, NULL, NULL, NULL, -1, "/path/f.txt:80",NULL, NULL}, + {"//server/f.txt", NULL, NULL, NULL, "server", -1, "/f.txt", NULL, NULL}, + {"//server:80/f.txt", NULL, NULL, NULL, "server:80",-1, "/f.txt", NULL, NULL}, + {"//server/f.txt:80", NULL, NULL, NULL, "server", -1, "/f.txt:80", NULL, NULL}, + {"///path/f.txt", NULL, NULL, NULL, NULL, -1, "/path/f.txt", NULL, NULL}, + {"///path:80/f.txt", NULL, NULL, NULL, NULL, -1, "/path:80/f.txt",NULL, NULL}, + {"///path/f.txt:80", NULL, NULL, NULL, NULL, -1, "/path/f.txt:80",NULL, NULL}, + {"////path/f.txt", NULL, NULL, NULL, NULL, -1, "/path/f.txt", NULL, NULL}, + {"////path:80/f.txt", NULL, NULL, NULL, NULL, -1, "/path:80/f.txt",NULL, NULL}, + {"////path/f.txt:80", NULL, NULL, NULL, NULL, -1, "/path/f.txt:80",NULL, NULL}, + // Queries and refs are valid for file URLs as well. + {"file:///foo.html?#", "file", NULL, NULL, NULL, -1, "/foo.html", "", ""}, + {"file:///foo.html?q=y#ref", "file", NULL, NULL, NULL, -1, "/foo.html", "q=y", "ref"}, +#endif // WIN32 }; -TEST(URLParser, WindowsFile) { +TEST(URLParser, ParseFileURL) { // Declared outside for loop to try to catch cases in init() where we forget // to reset something that is reset by the construtor. - url_parse::Parsed parsed; - for (int i = 0; i < arraysize(file_cases); i++) { + Parsed parsed; + for (size_t i = 0; i < arraysize(file_cases); i++) { const char* url = file_cases[i].input; - url_parse::ParseFileURL(url, static_cast<int>(strlen(url)), &parsed); - int port = url_parse::ParsePort(url, parsed.port); + ParseFileURL(url, static_cast<int>(strlen(url)), &parsed); + int port = ParsePort(url, parsed.port); - EXPECT_TRUE(ComponentMatches(url, file_cases[i].scheme, parsed.scheme)); - EXPECT_TRUE(ComponentMatches(url, file_cases[i].username, parsed.username)); - EXPECT_TRUE(ComponentMatches(url, file_cases[i].password, parsed.password)); - EXPECT_TRUE(ComponentMatches(url, file_cases[i].host, parsed.host)); - EXPECT_EQ(file_cases[i].port, port); - EXPECT_TRUE(ComponentMatches(url, file_cases[i].path, parsed.path)); - EXPECT_TRUE(ComponentMatches(url, file_cases[i].query, parsed.query)); - EXPECT_TRUE(ComponentMatches(url, file_cases[i].ref, parsed.ref)); + EXPECT_TRUE(ComponentMatches(url, file_cases[i].scheme, parsed.scheme)) + << " for case #" << i << " [" << url << "] " + << parsed.scheme.begin << ", " << parsed.scheme.len; + + EXPECT_TRUE(ComponentMatches(url, file_cases[i].username, parsed.username)) + << " for case #" << i << " [" << url << "] " + << parsed.username.begin << ", " << parsed.username.len; + + EXPECT_TRUE(ComponentMatches(url, file_cases[i].password, parsed.password)) + << " for case #" << i << " [" << url << "] " + << parsed.password.begin << ", " << parsed.password.len; + + EXPECT_TRUE(ComponentMatches(url, file_cases[i].host, parsed.host)) + << " for case #" << i << " [" << url << "] " + << parsed.host.begin << ", " << parsed.host.len; + + EXPECT_EQ(file_cases[i].port, port) + << " for case #" << i << " [ " << url << "] " << port; + + EXPECT_TRUE(ComponentMatches(url, file_cases[i].path, parsed.path)) + << " for case #" << i << " [" << url << "] " + << parsed.path.begin << ", " << parsed.path.len; + + EXPECT_TRUE(ComponentMatches(url, file_cases[i].query, parsed.query)) + << " for case #" << i << " [" << url << "] " + << parsed.query.begin << ", " << parsed.query.len; + + EXPECT_TRUE(ComponentMatches(url, file_cases[i].ref, parsed.ref)) + << " for case #" << i << " [ "<< url << "] " + << parsed.query.begin << ", " << parsed.scheme.len; } } -#endif // WIN32 TEST(URLParser, ExtractFileName) { struct FileCase { @@ -452,19 +498,24 @@ {"http://www.google.com/foo/bar.html#ref", "bar.html"}, {"http://www.google.com/search/;param", ""}, {"http://www.google.com/foo/bar.html;param#ref", "bar.html"}, - {"http://www.google.com/foo/bar.html;foo;param#ref", "bar.html;foo"}, + {"http://www.google.com/foo/bar.html;foo;param#ref", "bar.html"}, {"http://www.google.com/foo/bar.html?query#ref", "bar.html"}, + {"http://www.google.com/foo;/bar.html", "bar.html"}, + {"http://www.google.com/foo;/", ""}, + {"http://www.google.com/foo;", "foo"}, + {"http://www.google.com/;", ""}, + {"http://www.google.com/foo;bar;html", "foo"}, }; - for (size_t i = 0; i < ARRAYSIZE(file_cases); i++) { + for (size_t i = 0; i < arraysize(file_cases); i++) { const char* url = file_cases[i].input; int len = static_cast<int>(strlen(url)); - url_parse::Parsed parsed; - url_parse::ParseStandardURL(url, len, &parsed); + Parsed parsed; + ParseStandardURL(url, len, &parsed); - url_parse::Component file_name; - url_parse::ExtractFileName(url, parsed.path, &file_name); + Component file_name; + ExtractFileName(url, parsed.path, &file_name); EXPECT_TRUE(ComponentMatches(url, file_cases[i].expected, file_name)); } @@ -477,14 +528,14 @@ int parameter, const char* expected_key, const char* expected_value) { - url_parse::Parsed parsed; - url_parse::ParseStandardURL(url, static_cast<int>(strlen(url)), &parsed); + Parsed parsed; + ParseStandardURL(url, static_cast<int>(strlen(url)), &parsed); - url_parse::Component query = parsed.query; + Component query = parsed.query; for (int i = 1; i <= parameter; i++) { - url_parse::Component key, value; - if (!url_parse::ExtractQueryKeyValue(url, &query, &key, &value)) { + Component key, value; + if (!ExtractQueryKeyValue(url, &query, &key, &value)) { if (parameter >= i && !expected_key) return true; // Expected nonexistant key, got one. return false; // Not enough keys. @@ -563,16 +614,16 @@ TEST(URLParser, MailtoUrl) { // Declared outside for loop to try to catch cases in init() where we forget // to reset something that is reset by the construtor. - url_parse::Parsed parsed; + Parsed parsed; for (size_t i = 0; i < arraysize(mailto_cases); ++i) { const char* url = mailto_cases[i].input; - url_parse::ParseMailtoURL(url, static_cast<int>(strlen(url)), &parsed); - int port = url_parse::ParsePort(url, parsed.port); + ParseMailtoURL(url, static_cast<int>(strlen(url)), &parsed); + int port = ParsePort(url, parsed.port); EXPECT_TRUE(ComponentMatches(url, mailto_cases[i].scheme, parsed.scheme)); EXPECT_TRUE(ComponentMatches(url, mailto_cases[i].path, parsed.path)); EXPECT_TRUE(ComponentMatches(url, mailto_cases[i].query, parsed.query)); - EXPECT_EQ(url_parse::PORT_UNSPECIFIED, port); + EXPECT_EQ(PORT_UNSPECIFIED, port); // The remaining components are never used for mailto urls. ExpectInvalidComponent(parsed.username); @@ -581,3 +632,57 @@ ExpectInvalidComponent(parsed.ref); } } + +// Various incarnations of filesystem URLs. +static FileSystemURLParseCase filesystem_cases[] = { + // Regular URL with all the parts +{"filesystem:http://user:pass@foo:21/temporary/bar;par?b#c", "http", "user", "pass", "foo", 21, "/temporary", "/bar;par", "b", "c"}, +{"filesystem:https://foo/persistent/bar;par/", "https", NULL, NULL, "foo", -1, "/persistent", "/bar;par/", NULL, NULL}, +{"filesystem:file:///persistent/bar;par/", "file", NULL, NULL, NULL, -1, "/persistent", "/bar;par/", NULL, NULL}, +{"filesystem:file:///persistent/bar;par/?query#ref", "file", NULL, NULL, NULL, -1, "/persistent", "/bar;par/", "query", "ref"}, +{"filesystem:file:///persistent", "file", NULL, NULL, NULL, -1, "/persistent", "", NULL, NULL}, +}; + +TEST(URLParser, FileSystemURL) { + // Declared outside for loop to try to catch cases in init() where we forget + // to reset something that is reset by the construtor. + Parsed parsed; + for (size_t i = 0; i < arraysize(filesystem_cases); i++) { + const FileSystemURLParseCase* parsecase = &filesystem_cases[i]; + const char* url = parsecase->input; + ParseFileSystemURL(url, static_cast<int>(strlen(url)), &parsed); + + EXPECT_TRUE(ComponentMatches(url, "filesystem", parsed.scheme)); + EXPECT_EQ(!parsecase->inner_scheme, !parsed.inner_parsed()); + // Only check the inner_parsed if there is one. + if (parsed.inner_parsed()) { + EXPECT_TRUE(ComponentMatches(url, parsecase->inner_scheme, + parsed.inner_parsed()->scheme)); + EXPECT_TRUE(ComponentMatches(url, parsecase->inner_username, + parsed.inner_parsed()->username)); + EXPECT_TRUE(ComponentMatches(url, parsecase->inner_password, + parsed.inner_parsed()->password)); + EXPECT_TRUE(ComponentMatches(url, parsecase->inner_host, + parsed.inner_parsed()->host)); + int port = ParsePort(url, parsed.inner_parsed()->port); + EXPECT_EQ(parsecase->inner_port, port); + + // The remaining components are never used for filesystem urls. + ExpectInvalidComponent(parsed.inner_parsed()->query); + ExpectInvalidComponent(parsed.inner_parsed()->ref); + } + + EXPECT_TRUE(ComponentMatches(url, parsecase->path, parsed.path)); + EXPECT_TRUE(ComponentMatches(url, parsecase->query, parsed.query)); + EXPECT_TRUE(ComponentMatches(url, parsecase->ref, parsed.ref)); + + // The remaining components are never used for filesystem urls. + ExpectInvalidComponent(parsed.username); + ExpectInvalidComponent(parsed.password); + ExpectInvalidComponent(parsed.host); + ExpectInvalidComponent(parsed.port); + } +} + +} // namespace +} // namespace url
diff --git a/src/url/url_test_utils.h b/src/url/url_test_utils.h new file mode 100644 index 0000000..6e66e85 --- /dev/null +++ b/src/url/url_test_utils.h
@@ -0,0 +1,56 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_TEST_UTILS_H_ +#define URL_URL_TEST_UTILS_H_ + +// Convenience functions for string conversions. +// These are mostly intended for use in unit tests. + +#include <string> + +#include "base/strings/string16.h" +#include "testing/base/public/gunit.h" +#include "url/url_canon_internal.h" + +namespace url { + +namespace test_utils { + +// Converts a UTF-16 string from native wchar_t format to char16, by +// truncating the high 32 bits. This is not meant to handle true UTF-32 +// encoded strings. +inline base::string16 WStringToUTF16(const wchar_t* src) { + base::string16 str; + int length = static_cast<int>(wcslen(src)); + for (int i = 0; i < length; ++i) { + str.push_back(static_cast<base::char16>(src[i])); + } + return str; +} + +// Converts a string from UTF-8 to UTF-16 +inline base::string16 ConvertUTF8ToUTF16(const std::string& src) { + int length = static_cast<int>(src.length()); + EXPECT_LT(length, 1024); + RawCanonOutputW<1024> output; + EXPECT_TRUE(ConvertUTF8ToUTF16(src.data(), length, &output)); + return base::string16(output.data(), output.length()); +} + +// Converts a string from UTF-16 to UTF-8 +inline std::string ConvertUTF16ToUTF8(const base::string16& src) { + std::string str; + StdStringCanonOutput output(&str); + EXPECT_TRUE(ConvertUTF16ToUTF8(src.data(), static_cast<int>(src.length()), + &output)); + output.Complete(); + return str; +} + +} // namespace test_utils + +} // namespace url + +#endif // URL_URL_TEST_UTILS_H_
diff --git a/src/url/url_util.cc b/src/url/url_util.cc new file mode 100644 index 0000000..f4246e9 --- /dev/null +++ b/src/url/url_util.cc
@@ -0,0 +1,590 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "url/url_util.h" + +#include <string.h> +#include <vector> + +#ifdef GOOGLEURL_IN_GOOGLE3 +#include "base/heap-checker.h" +#endif +#include "base/logging.h" +#include "url/url_canon_internal.h" +#include "url/url_file.h" +#include "url/url_util_internal.h" + +namespace url { + +namespace { + +// ASCII-specific tolower. The standard library's tolower is locale sensitive, +// so we don't want to use it here. +template<class Char> +inline Char ToLowerASCII(Char c) { + return (c >= 'A' && c <= 'Z') ? (c + ('a' - 'A')) : c; +} + +// Backend for LowerCaseEqualsASCII. +template<typename Iter> +inline bool DoLowerCaseEqualsASCII(Iter a_begin, Iter a_end, const char* b) { + for (Iter it = a_begin; it != a_end; ++it, ++b) { + if (!*b || ToLowerASCII(*it) != *b) + return false; + } + return *b == 0; +} + +const int kNumStandardURLSchemes = 8; +const char* kStandardURLSchemes[kNumStandardURLSchemes] = { + kHttpScheme, + kHttpsScheme, + kFileScheme, // Yes, file urls can have a hostname! + kFtpScheme, + kGopherScheme, + kWsScheme, // WebSocket. + kWssScheme, // WebSocket secure. + kFileSystemScheme, +}; + +// List of the currently installed standard schemes. This list is lazily +// initialized by InitStandardSchemes and is leaked on shutdown to prevent +// any destructors from being called that will slow us down or cause problems. +std::vector<const char*>* standard_schemes = NULL; + +// See the LockStandardSchemes declaration in the header. +bool standard_schemes_locked = false; + +// Ensures that the standard_schemes list is initialized, does nothing if it +// already has values. +void InitStandardSchemes() { + if (standard_schemes) + return; + standard_schemes = new std::vector<const char*>; + for (int i = 0; i < kNumStandardURLSchemes; i++) + standard_schemes->push_back(kStandardURLSchemes[i]); +} + +// Given a string and a range inside the string, compares it to the given +// lower-case |compare_to| buffer. +template<typename CHAR> +inline bool DoCompareSchemeComponent(const CHAR* spec, + const Component& component, + const char* compare_to) { + if (!component.is_nonempty()) + return compare_to[0] == 0; // When component is empty, match empty scheme. + return LowerCaseEqualsASCII(&spec[component.begin], + &spec[component.end()], + compare_to); +} + +// Returns true if the given scheme identified by |scheme| within |spec| is one +// of the registered "standard" schemes. +template<typename CHAR> +bool DoIsStandard(const CHAR* spec, const Component& scheme) { + if (!scheme.is_nonempty()) + return false; // Empty or invalid schemes are non-standard. + + InitStandardSchemes(); + for (size_t i = 0; i < standard_schemes->size(); i++) { + if (LowerCaseEqualsASCII(&spec[scheme.begin], &spec[scheme.end()], + standard_schemes->at(i))) + return true; + } + return false; +} + +template<typename CHAR> +bool DoFindAndCompareScheme(const CHAR* str, + int str_len, + const char* compare, + Component* found_scheme) { + // Before extracting scheme, canonicalize the URL to remove any whitespace. + // This matches the canonicalization done in DoCanonicalize function. + RawCanonOutputT<CHAR> whitespace_buffer; + int spec_len; + const CHAR* spec = RemoveURLWhitespace(str, str_len, + &whitespace_buffer, &spec_len); + + Component our_scheme; + if (!ExtractScheme(spec, spec_len, &our_scheme)) { + // No scheme. + if (found_scheme) + *found_scheme = Component(); + return false; + } + if (found_scheme) + *found_scheme = our_scheme; + return DoCompareSchemeComponent(spec, our_scheme, compare); +} + +template<typename CHAR> +bool DoCanonicalize(const CHAR* in_spec, + int in_spec_len, + bool trim_path_end, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed) { + // Remove any whitespace from the middle of the relative URL, possibly + // copying to the new buffer. + RawCanonOutputT<CHAR> whitespace_buffer; + int spec_len; + const CHAR* spec = RemoveURLWhitespace(in_spec, in_spec_len, + &whitespace_buffer, &spec_len); + + Parsed parsed_input; +#ifdef WIN32 + // For Windows, we allow things that look like absolute Windows paths to be + // fixed up magically to file URLs. This is done for IE compatability. For + // example, this will change "c:/foo" into a file URL rather than treating + // it as a URL with the protocol "c". It also works for UNC ("\\foo\bar.txt"). + // There is similar logic in url_canon_relative.cc for + // + // For Max & Unix, we don't do this (the equivalent would be "/foo/bar" which + // has no meaning as an absolute path name. This is because browsers on Mac + // & Unix don't generally do this, so there is no compatibility reason for + // doing so. + if (DoesBeginUNCPath(spec, 0, spec_len, false) || + DoesBeginWindowsDriveSpec(spec, 0, spec_len)) { + ParseFileURL(spec, spec_len, &parsed_input); + return CanonicalizeFileURL(spec, spec_len, parsed_input, charset_converter, + output, output_parsed); + } +#endif + + Component scheme; + if (!ExtractScheme(spec, spec_len, &scheme)) + return false; + + // This is the parsed version of the input URL, we have to canonicalize it + // before storing it in our object. + bool success; + if (DoCompareSchemeComponent(spec, scheme, url::kFileScheme)) { + // File URLs are special. + ParseFileURL(spec, spec_len, &parsed_input); + success = CanonicalizeFileURL(spec, spec_len, parsed_input, + charset_converter, output, output_parsed); + } else if (DoCompareSchemeComponent(spec, scheme, url::kFileSystemScheme)) { + // Filesystem URLs are special. + ParseFileSystemURL(spec, spec_len, &parsed_input); + success = CanonicalizeFileSystemURL(spec, spec_len, parsed_input, + charset_converter, output, + output_parsed); + + } else if (DoIsStandard(spec, scheme)) { + // All "normal" URLs. + ParseStandardURL(spec, spec_len, &parsed_input); + success = CanonicalizeStandardURL(spec, spec_len, parsed_input, + charset_converter, output, output_parsed); + + } else if (DoCompareSchemeComponent(spec, scheme, url::kMailToScheme)) { + // Mailto are treated like a standard url with only a scheme, path, query + ParseMailtoURL(spec, spec_len, &parsed_input); + success = CanonicalizeMailtoURL(spec, spec_len, parsed_input, output, + output_parsed); + + } else { + // "Weird" URLs like data: and javascript: + ParsePathURL(spec, spec_len, trim_path_end, &parsed_input); + success = CanonicalizePathURL(spec, spec_len, parsed_input, output, + output_parsed); + } + return success; +} + +template<typename CHAR> +bool DoResolveRelative(const char* base_spec, + int base_spec_len, + const Parsed& base_parsed, + const CHAR* in_relative, + int in_relative_length, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed) { + // Remove any whitespace from the middle of the relative URL, possibly + // copying to the new buffer. + RawCanonOutputT<CHAR> whitespace_buffer; + int relative_length; + const CHAR* relative = RemoveURLWhitespace(in_relative, in_relative_length, + &whitespace_buffer, + &relative_length); + bool base_is_authority_based = false; + bool base_is_hierarchical = false; + if (base_spec && + base_parsed.scheme.is_nonempty()) { + int after_scheme = base_parsed.scheme.end() + 1; // Skip past the colon. + int num_slashes = CountConsecutiveSlashes(base_spec, after_scheme, + base_spec_len); + base_is_authority_based = num_slashes > 1; + base_is_hierarchical = num_slashes > 0; + } + + bool standard_base_scheme = + base_parsed.scheme.is_nonempty() && + DoIsStandard(base_spec, base_parsed.scheme); + + bool is_relative; + Component relative_component; + if (!IsRelativeURL(base_spec, base_parsed, relative, relative_length, + (base_is_hierarchical || standard_base_scheme), + &is_relative, &relative_component)) { + // Error resolving. + return false; + } + + // Pretend for a moment that |base_spec| is a standard URL. Normally + // non-standard URLs are treated as PathURLs, but if the base has an + // authority we would like to preserve it. + if (is_relative && base_is_authority_based && !standard_base_scheme) { + Parsed base_parsed_authority; + ParseStandardURL(base_spec, base_spec_len, &base_parsed_authority); + if (base_parsed_authority.host.is_nonempty()) { + RawCanonOutputT<char> temporary_output; + bool did_resolve_succeed = + ResolveRelativeURL(base_spec, base_parsed_authority, false, relative, + relative_component, charset_converter, + &temporary_output, output_parsed); + // The output_parsed is incorrect at this point (because it was built + // based on base_parsed_authority instead of base_parsed) and needs to be + // re-created. + DoCanonicalize(temporary_output.data(), temporary_output.length(), true, + charset_converter, output, output_parsed); + return did_resolve_succeed; + } + } else if (is_relative) { + // Relative, resolve and canonicalize. + bool file_base_scheme = base_parsed.scheme.is_nonempty() && + DoCompareSchemeComponent(base_spec, base_parsed.scheme, kFileScheme); + return ResolveRelativeURL(base_spec, base_parsed, file_base_scheme, relative, + relative_component, charset_converter, output, + output_parsed); + } + + // Not relative, canonicalize the input. + return DoCanonicalize(relative, relative_length, true, charset_converter, + output, output_parsed); +} + +template<typename CHAR> +bool DoReplaceComponents(const char* spec, + int spec_len, + const Parsed& parsed, + const Replacements<CHAR>& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* out_parsed) { + // If the scheme is overridden, just do a simple string substitution and + // reparse the whole thing. There are lots of edge cases that we really don't + // want to deal with. Like what happens if I replace "http://e:8080/foo" + // with a file. Does it become "file:///E:/8080/foo" where the port number + // becomes part of the path? Parsing that string as a file URL says "yes" + // but almost no sane rule for dealing with the components individually would + // come up with that. + // + // Why allow these crazy cases at all? Programatically, there is almost no + // case for replacing the scheme. The most common case for hitting this is + // in JS when building up a URL using the location object. In this case, the + // JS code expects the string substitution behavior: + // http://www.w3.org/TR/2008/WD-html5-20080610/structured.html#common3 + if (replacements.IsSchemeOverridden()) { + // Canonicalize the new scheme so it is 8-bit and can be concatenated with + // the existing spec. + RawCanonOutput<128> scheme_replaced; + Component scheme_replaced_parsed; + CanonicalizeScheme(replacements.sources().scheme, + replacements.components().scheme, + &scheme_replaced, &scheme_replaced_parsed); + + // We can assume that the input is canonicalized, which means it always has + // a colon after the scheme (or where the scheme would be). + int spec_after_colon = parsed.scheme.is_valid() ? parsed.scheme.end() + 1 + : 1; + if (spec_len - spec_after_colon > 0) { + scheme_replaced.Append(&spec[spec_after_colon], + spec_len - spec_after_colon); + } + + // We now need to completely re-parse the resulting string since its meaning + // may have changed with the different scheme. + RawCanonOutput<128> recanonicalized; + Parsed recanonicalized_parsed; + DoCanonicalize(scheme_replaced.data(), scheme_replaced.length(), true, + charset_converter, + &recanonicalized, &recanonicalized_parsed); + + // Recurse using the version with the scheme already replaced. This will now + // use the replacement rules for the new scheme. + // + // Warning: this code assumes that ReplaceComponents will re-check all + // components for validity. This is because we can't fail if DoCanonicalize + // failed above since theoretically the thing making it fail could be + // getting replaced here. If ReplaceComponents didn't re-check everything, + // we wouldn't know if something *not* getting replaced is a problem. + // If the scheme-specific replacers are made more intelligent so they don't + // re-check everything, we should instead recanonicalize the whole thing + // after this call to check validity (this assumes replacing the scheme is + // much much less common than other types of replacements, like clearing the + // ref). + Replacements<CHAR> replacements_no_scheme = replacements; + replacements_no_scheme.SetScheme(NULL, Component()); + return DoReplaceComponents(recanonicalized.data(), recanonicalized.length(), + recanonicalized_parsed, replacements_no_scheme, + charset_converter, output, out_parsed); + } + + // If we get here, then we know the scheme doesn't need to be replaced, so can + // just key off the scheme in the spec to know how to do the replacements. + if (DoCompareSchemeComponent(spec, parsed.scheme, url::kFileScheme)) { + return ReplaceFileURL(spec, parsed, replacements, charset_converter, output, + out_parsed); + } + if (DoCompareSchemeComponent(spec, parsed.scheme, url::kFileSystemScheme)) { + return ReplaceFileSystemURL(spec, parsed, replacements, charset_converter, + output, out_parsed); + } + if (DoIsStandard(spec, parsed.scheme)) { + return ReplaceStandardURL(spec, parsed, replacements, charset_converter, + output, out_parsed); + } + if (DoCompareSchemeComponent(spec, parsed.scheme, url::kMailToScheme)) { + return ReplaceMailtoURL(spec, parsed, replacements, output, out_parsed); + } + + // Default is a path URL. + return ReplacePathURL(spec, parsed, replacements, output, out_parsed); +} + +} // namespace + +void Initialize() { + InitStandardSchemes(); +} + +void Shutdown() { + if (standard_schemes) { + delete standard_schemes; + standard_schemes = NULL; + } +} + +void AddStandardScheme(const char* new_scheme) { + // If this assert triggers, it means you've called AddStandardScheme after + // LockStandardSchemes have been called (see the header file for + // LockStandardSchemes for more). + // + // This normally means you're trying to set up a new standard scheme too late + // in your application's init process. Locate where your app does this + // initialization and calls LockStandardScheme, and add your new standard + // scheme there. + DCHECK(!standard_schemes_locked) << + "Trying to add a standard scheme after the list has been locked."; + + size_t scheme_len = strlen(new_scheme); + if (scheme_len == 0) + return; + + // Dulicate the scheme into a new buffer and add it to the list of standard + // schemes. This pointer will be leaked on shutdown. + char* dup_scheme = new char[scheme_len + 1]; +#ifdef GOOGLEURL_IN_GOOGLE3 + HeapLeakChecker::IgnoreObject(dup_scheme); +#endif + memcpy(dup_scheme, new_scheme, scheme_len + 1); + + InitStandardSchemes(); + standard_schemes->push_back(dup_scheme); +} + +void LockStandardSchemes() { + standard_schemes_locked = true; +} + +bool IsStandard(const char* spec, const Component& scheme) { + return DoIsStandard(spec, scheme); +} + +bool IsStandard(const base::char16* spec, const Component& scheme) { + return DoIsStandard(spec, scheme); +} + +bool FindAndCompareScheme(const char* str, + int str_len, + const char* compare, + Component* found_scheme) { + return DoFindAndCompareScheme(str, str_len, compare, found_scheme); +} + +bool FindAndCompareScheme(const base::char16* str, + int str_len, + const char* compare, + Component* found_scheme) { + return DoFindAndCompareScheme(str, str_len, compare, found_scheme); +} + +bool Canonicalize(const char* spec, + int spec_len, + bool trim_path_end, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed) { + return DoCanonicalize(spec, spec_len, trim_path_end, charset_converter, + output, output_parsed); +} + +bool Canonicalize(const base::char16* spec, + int spec_len, + bool trim_path_end, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed) { + return DoCanonicalize(spec, spec_len, trim_path_end, charset_converter, + output, output_parsed); +} + +bool ResolveRelative(const char* base_spec, + int base_spec_len, + const Parsed& base_parsed, + const char* relative, + int relative_length, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed) { + return DoResolveRelative(base_spec, base_spec_len, base_parsed, + relative, relative_length, + charset_converter, output, output_parsed); +} + +bool ResolveRelative(const char* base_spec, + int base_spec_len, + const Parsed& base_parsed, + const base::char16* relative, + int relative_length, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed) { + return DoResolveRelative(base_spec, base_spec_len, base_parsed, + relative, relative_length, + charset_converter, output, output_parsed); +} + +bool ReplaceComponents(const char* spec, + int spec_len, + const Parsed& parsed, + const Replacements<char>& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* out_parsed) { + return DoReplaceComponents(spec, spec_len, parsed, replacements, + charset_converter, output, out_parsed); +} + +bool ReplaceComponents(const char* spec, + int spec_len, + const Parsed& parsed, + const Replacements<base::char16>& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* out_parsed) { + return DoReplaceComponents(spec, spec_len, parsed, replacements, + charset_converter, output, out_parsed); +} + +// Front-ends for LowerCaseEqualsASCII. +bool LowerCaseEqualsASCII(const char* a_begin, + const char* a_end, + const char* b) { + return DoLowerCaseEqualsASCII(a_begin, a_end, b); +} + +bool LowerCaseEqualsASCII(const char* a_begin, + const char* a_end, + const char* b_begin, + const char* b_end) { + while (a_begin != a_end && b_begin != b_end && + ToLowerASCII(*a_begin) == *b_begin) { + a_begin++; + b_begin++; + } + return a_begin == a_end && b_begin == b_end; +} + +bool LowerCaseEqualsASCII(const base::char16* a_begin, + const base::char16* a_end, + const char* b) { + return DoLowerCaseEqualsASCII(a_begin, a_end, b); +} + +void DecodeURLEscapeSequences(const char* input, + int length, + CanonOutputW* output) { + RawCanonOutputT<char> unescaped_chars; + for (int i = 0; i < length; i++) { + if (input[i] == '%') { + unsigned char ch; + if (DecodeEscaped(input, &i, length, &ch)) { + unescaped_chars.push_back(ch); + } else { + // Invalid escape sequence, copy the percent literal. + unescaped_chars.push_back('%'); + } + } else { + // Regular non-escaped 8-bit character. + unescaped_chars.push_back(input[i]); + } + } + + // Convert that 8-bit to UTF-16. It's not clear IE does this at all to + // JavaScript URLs, but Firefox and Safari do. + for (int i = 0; i < unescaped_chars.length(); i++) { + unsigned char uch = static_cast<unsigned char>(unescaped_chars.at(i)); + if (uch < 0x80) { + // Non-UTF-8, just append directly + output->push_back(uch); + } else { + // next_ch will point to the last character of the decoded + // character. + int next_character = i; + unsigned code_point; + if (ReadUTFChar(unescaped_chars.data(), &next_character, + unescaped_chars.length(), &code_point)) { + // Valid UTF-8 character, convert to UTF-16. + AppendUTF16Value(code_point, output); + i = next_character; + } else { + // If there are any sequences that are not valid UTF-8, we keep + // invalid code points and promote to UTF-16. We copy all characters + // from the current position to the end of the identified sequence. + while (i < next_character) { + output->push_back(static_cast<unsigned char>(unescaped_chars.at(i))); + i++; + } + output->push_back(static_cast<unsigned char>(unescaped_chars.at(i))); + } + } + } +} + +void EncodeURIComponent(const char* input, int length, CanonOutput* output) { + for (int i = 0; i < length; ++i) { + unsigned char c = static_cast<unsigned char>(input[i]); + if (IsComponentChar(c)) + output->push_back(c); + else + AppendEscapedChar(c, output); + } +} + +bool CompareSchemeComponent(const char* spec, + const Component& component, + const char* compare_to) { + return DoCompareSchemeComponent(spec, component, compare_to); +} + +bool CompareSchemeComponent(const base::char16* spec, + const Component& component, + const char* compare_to) { + return DoCompareSchemeComponent(spec, component, compare_to); +} + +} // namespace url
diff --git a/src/url/url_util.h b/src/url/url_util.h new file mode 100644 index 0000000..458d1e8 --- /dev/null +++ b/src/url/url_util.h
@@ -0,0 +1,205 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_UTIL_H_ +#define URL_URL_UTIL_H_ + +#include <string> + +#include "base/strings/string16.h" +#include "url/url_canon.h" +#include "url/url_constants.h" +#include "url/url_export.h" +#include "url/url_parse.h" + +namespace url { + +// Init ------------------------------------------------------------------------ + +// Initialization is NOT required, it will be implicitly initialized when first +// used. However, this implicit initialization is NOT threadsafe. If you are +// using this library in a threaded environment and don't have a consistent +// "first call" (an example might be calling "AddStandardScheme" with your +// special application-specific schemes) then you will want to call initialize +// before spawning any threads. +// +// It is OK to call this function more than once, subsequent calls will simply +// "noop", unless Shutdown() was called in the mean time. This will also be a +// "noop" if other calls to the library have forced an initialization +// beforehand. +URL_EXPORT void Initialize(); + +// Cleanup is not required, except some strings may leak. For most user +// applications, this is fine. If you're using it in a library that may get +// loaded and unloaded, you'll want to unload to properly clean up your +// library. +URL_EXPORT void Shutdown(); + +// Schemes -------------------------------------------------------------------- + +// Adds an application-defined scheme to the internal list of "standard" URL +// schemes. This function is not threadsafe and can not be called concurrently +// with any other url_util function. It will assert if the list of standard +// schemes has been locked (see LockStandardSchemes). +URL_EXPORT void AddStandardScheme(const char* new_scheme); + +// Sets a flag to prevent future calls to AddStandardScheme from succeeding. +// +// This is designed to help prevent errors for multithreaded applications. +// Normal usage would be to call AddStandardScheme for your custom schemes at +// the beginning of program initialization, and then LockStandardSchemes. This +// prevents future callers from mistakenly calling AddStandardScheme when the +// program is running with multiple threads, where such usage would be +// dangerous. +// +// We could have had AddStandardScheme use a lock instead, but that would add +// some platform-specific dependencies we don't otherwise have now, and is +// overkill considering the normal usage is so simple. +URL_EXPORT void LockStandardSchemes(); + +// Locates the scheme in the given string and places it into |found_scheme|, +// which may be NULL to indicate the caller does not care about the range. +// +// Returns whether the given |compare| scheme matches the scheme found in the +// input (if any). The |compare| scheme must be a valid canonical scheme or +// the result of the comparison is undefined. +URL_EXPORT bool FindAndCompareScheme(const char* str, + int str_len, + const char* compare, + Component* found_scheme); +URL_EXPORT bool FindAndCompareScheme(const base::char16* str, + int str_len, + const char* compare, + Component* found_scheme); +inline bool FindAndCompareScheme(const std::string& str, + const char* compare, + Component* found_scheme) { + return FindAndCompareScheme(str.data(), static_cast<int>(str.size()), + compare, found_scheme); +} +inline bool FindAndCompareScheme(const base::string16& str, + const char* compare, + Component* found_scheme) { + return FindAndCompareScheme(str.data(), static_cast<int>(str.size()), + compare, found_scheme); +} + +// Returns true if the given string represents a standard URL. This means that +// either the scheme is in the list of known standard schemes. +URL_EXPORT bool IsStandard(const char* spec, const Component& scheme); +URL_EXPORT bool IsStandard(const base::char16* spec, const Component& scheme); + +// TODO(brettw) remove this. This is a temporary compatibility hack to avoid +// breaking the WebKit build when this version is synced via Chrome. +inline bool IsStandard(const char* spec, + int spec_len, + const Component& scheme) { + return IsStandard(spec, scheme); +} + +// URL library wrappers ------------------------------------------------------- + +// Parses the given spec according to the extracted scheme type. Normal users +// should use the URL object, although this may be useful if performance is +// critical and you don't want to do the heap allocation for the std::string. +// +// As with the Canonicalize* functions, the charset converter can +// be NULL to use UTF-8 (it will be faster in this case). +// +// Returns true if a valid URL was produced, false if not. On failure, the +// output and parsed structures will still be filled and will be consistent, +// but they will not represent a loadable URL. +URL_EXPORT bool Canonicalize(const char* spec, + int spec_len, + bool trim_path_end, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed); +URL_EXPORT bool Canonicalize(const base::char16* spec, + int spec_len, + bool trim_path_end, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed); + +// Resolves a potentially relative URL relative to the given parsed base URL. +// The base MUST be valid. The resulting canonical URL and parsed information +// will be placed in to the given out variables. +// +// The relative need not be relative. If we discover that it's absolute, this +// will produce a canonical version of that URL. See Canonicalize() for more +// about the charset_converter. +// +// Returns true if the output is valid, false if the input could not produce +// a valid URL. +URL_EXPORT bool ResolveRelative(const char* base_spec, + int base_spec_len, + const Parsed& base_parsed, + const char* relative, + int relative_length, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed); +URL_EXPORT bool ResolveRelative(const char* base_spec, + int base_spec_len, + const Parsed& base_parsed, + const base::char16* relative, + int relative_length, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* output_parsed); + +// Replaces components in the given VALID input url. The new canonical URL info +// is written to output and out_parsed. +// +// Returns true if the resulting URL is valid. +URL_EXPORT bool ReplaceComponents(const char* spec, + int spec_len, + const Parsed& parsed, + const Replacements<char>& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* out_parsed); +URL_EXPORT bool ReplaceComponents( + const char* spec, + int spec_len, + const Parsed& parsed, + const Replacements<base::char16>& replacements, + CharsetConverter* charset_converter, + CanonOutput* output, + Parsed* out_parsed); + +// String helper functions ---------------------------------------------------- + +// Compare the lower-case form of the given string against the given ASCII +// string. This is useful for doing checking if an input string matches some +// token, and it is optimized to avoid intermediate string copies. +// +// The versions of this function that don't take a b_end assume that the b +// string is NULL terminated. +URL_EXPORT bool LowerCaseEqualsASCII(const char* a_begin, + const char* a_end, + const char* b); +URL_EXPORT bool LowerCaseEqualsASCII(const char* a_begin, + const char* a_end, + const char* b_begin, + const char* b_end); +URL_EXPORT bool LowerCaseEqualsASCII(const base::char16* a_begin, + const base::char16* a_end, + const char* b); + +// Unescapes the given string using URL escaping rules. +URL_EXPORT void DecodeURLEscapeSequences(const char* input, + int length, + CanonOutputW* output); + +// Escapes the given string as defined by the JS method encodeURIComponent. See +// https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/encodeURIComponent +URL_EXPORT void EncodeURIComponent(const char* input, + int length, + CanonOutput* output); + +} // namespace url + +#endif // URL_URL_UTIL_H_
diff --git a/src/url/url_util_internal.h b/src/url/url_util_internal.h new file mode 100644 index 0000000..c72598f --- /dev/null +++ b/src/url/url_util_internal.h
@@ -0,0 +1,26 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef URL_URL_UTIL_INTERNAL_H_ +#define URL_URL_UTIL_INTERNAL_H_ + +#include <string> + +#include "base/strings/string16.h" +#include "url/url_parse.h" + +namespace url { + +// Given a string and a range inside the string, compares it to the given +// lower-case |compare_to| buffer. +bool CompareSchemeComponent(const char* spec, + const Component& component, + const char* compare_to); +bool CompareSchemeComponent(const base::char16* spec, + const Component& component, + const char* compare_to); + +} // namespace url + +#endif // URL_URL_UTIL_INTERNAL_H_
diff --git a/src/url/url_util_unittest.cc b/src/url/url_util_unittest.cc new file mode 100644 index 0000000..2216252 --- /dev/null +++ b/src/url/url_util_unittest.cc
@@ -0,0 +1,319 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/macros.h" +#include "testing/base/public/gunit.h" +#include "url/url_canon.h" +#include "url/url_canon_stdstring.h" +#include "url/url_parse.h" +#include "url/url_test_utils.h" +#include "url/url_util.h" + +namespace url { + +TEST(URLUtilTest, FindAndCompareScheme) { + Component found_scheme; + + // Simple case where the scheme is found and matches. + const char kStr1[] = "http://www.com/"; + EXPECT_TRUE(FindAndCompareScheme( + kStr1, static_cast<int>(strlen(kStr1)), "http", NULL)); + EXPECT_TRUE(FindAndCompareScheme( + kStr1, static_cast<int>(strlen(kStr1)), "http", &found_scheme)); + EXPECT_TRUE(found_scheme == Component(0, 4)); + + // A case where the scheme is found and doesn't match. + EXPECT_FALSE(FindAndCompareScheme( + kStr1, static_cast<int>(strlen(kStr1)), "https", &found_scheme)); + EXPECT_TRUE(found_scheme == Component(0, 4)); + + // A case where there is no scheme. + const char kStr2[] = "httpfoobar"; + EXPECT_FALSE(FindAndCompareScheme( + kStr2, static_cast<int>(strlen(kStr2)), "http", &found_scheme)); + EXPECT_TRUE(found_scheme == Component()); + + // When there is an empty scheme, it should match the empty scheme. + const char kStr3[] = ":foo.com/"; + EXPECT_TRUE(FindAndCompareScheme( + kStr3, static_cast<int>(strlen(kStr3)), "", &found_scheme)); + EXPECT_TRUE(found_scheme == Component(0, 0)); + + // But when there is no scheme, it should fail. + EXPECT_FALSE(FindAndCompareScheme("", 0, "", &found_scheme)); + EXPECT_TRUE(found_scheme == Component()); + + // When there is a whitespace char in scheme, it should canonicalize the url + // before comparison. + const char whtspc_str[] = " \r\n\tjav\ra\nscri\tpt:alert(1)"; + EXPECT_TRUE(FindAndCompareScheme(whtspc_str, + static_cast<int>(strlen(whtspc_str)), + "javascript", &found_scheme)); + EXPECT_TRUE(found_scheme == Component(1, 10)); + + // Control characters should be stripped out on the ends, and kept in the + // middle. + const char ctrl_str[] = "\02jav\02scr\03ipt:alert(1)"; + EXPECT_FALSE(FindAndCompareScheme(ctrl_str, + static_cast<int>(strlen(ctrl_str)), + "javascript", &found_scheme)); + EXPECT_TRUE(found_scheme == Component(1, 11)); +} + +TEST(URLUtilTest, ReplaceComponents) { + Parsed parsed; + RawCanonOutputT<char> output; + Parsed new_parsed; + + // Check that the following calls do not cause crash + Replacements<char> replacements; + replacements.SetRef("test", Component(0, 4)); + ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, &new_parsed); + ReplaceComponents("", 0, parsed, replacements, NULL, &output, &new_parsed); + replacements.ClearRef(); + replacements.SetHost("test", Component(0, 4)); + ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, &new_parsed); + ReplaceComponents("", 0, parsed, replacements, NULL, &output, &new_parsed); + + replacements.ClearHost(); + ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, &new_parsed); + ReplaceComponents("", 0, parsed, replacements, NULL, &output, &new_parsed); + ReplaceComponents(NULL, 0, parsed, replacements, NULL, &output, &new_parsed); + ReplaceComponents("", 0, parsed, replacements, NULL, &output, &new_parsed); +} + +static std::string CheckReplaceScheme(const char* base_url, + const char* scheme) { + // Make sure the input is canonicalized. + RawCanonOutput<32> original; + Parsed original_parsed; + Canonicalize(base_url, strlen(base_url), true, NULL, &original, + &original_parsed); + + Replacements<char> replacements; + replacements.SetScheme(scheme, Component(0, strlen(scheme))); + + std::string output_string; + StdStringCanonOutput output(&output_string); + Parsed output_parsed; + ReplaceComponents(original.data(), original.length(), original_parsed, + replacements, NULL, &output, &output_parsed); + + output.Complete(); + return output_string; +} + +TEST(URLUtilTest, ReplaceScheme) { + EXPECT_EQ("https://google.com/", + CheckReplaceScheme("http://google.com/", "https")); + EXPECT_EQ("file://google.com/", + CheckReplaceScheme("http://google.com/", "file")); + EXPECT_EQ("http://home/Build", + CheckReplaceScheme("file:///Home/Build", "http")); + EXPECT_EQ("javascript:foo", + CheckReplaceScheme("about:foo", "javascript")); + EXPECT_EQ("://google.com/", + CheckReplaceScheme("http://google.com/", "")); + EXPECT_EQ("http://google.com/", + CheckReplaceScheme("about:google.com", "http")); + EXPECT_EQ("http:", CheckReplaceScheme("", "http")); + +#ifdef WIN32 + // Magic Windows drive letter behavior when converting to a file URL. + EXPECT_EQ("file:///E:/foo/", + CheckReplaceScheme("http://localhost/e:foo/", "file")); +#endif + + // This will probably change to "about://google.com/" when we fix + // http://crbug.com/160 which should also be an acceptable result. + EXPECT_EQ("about://google.com/", + CheckReplaceScheme("http://google.com/", "about")); + + EXPECT_EQ("http://example.com/%20hello%20# world", + CheckReplaceScheme("myscheme:example.com/ hello # world ", "http")); +} + +TEST(URLUtilTest, DecodeURLEscapeSequences) { + struct DecodeCase { + const char* input; + const char* output; + } decode_cases[] = { + {"hello, world", "hello, world"}, + {"%01%02%03%04%05%06%07%08%09%0a%0B%0C%0D%0e%0f/", + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0B\x0C\x0D\x0e\x0f/"}, + {"%10%11%12%13%14%15%16%17%18%19%1a%1B%1C%1D%1e%1f/", + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1B\x1C\x1D\x1e\x1f/"}, + {"%20%21%22%23%24%25%26%27%28%29%2a%2B%2C%2D%2e%2f/", + " !\"#$%&'()*+,-.//"}, + {"%30%31%32%33%34%35%36%37%38%39%3a%3B%3C%3D%3e%3f/", + "0123456789:;<=>?/"}, + {"%40%41%42%43%44%45%46%47%48%49%4a%4B%4C%4D%4e%4f/", + "@ABCDEFGHIJKLMNO/"}, + {"%50%51%52%53%54%55%56%57%58%59%5a%5B%5C%5D%5e%5f/", + "PQRSTUVWXYZ[\\]^_/"}, + {"%60%61%62%63%64%65%66%67%68%69%6a%6B%6C%6D%6e%6f/", + "`abcdefghijklmno/"}, + {"%70%71%72%73%74%75%76%77%78%79%7a%7B%7C%7D%7e%7f/", + "pqrstuvwxyz{|}~\x7f/"}, + // Test un-UTF-8-ization. + {"%e4%bd%a0%e5%a5%bd", "\xe4\xbd\xa0\xe5\xa5\xbd"}, + }; + + for (size_t i = 0; i < arraysize(decode_cases); i++) { + const char* input = decode_cases[i].input; + RawCanonOutputT<base::char16> output; + DecodeURLEscapeSequences(input, strlen(input), &output); + EXPECT_EQ(decode_cases[i].output, + test_utils::ConvertUTF16ToUTF8(base::string16(output.data(), + output.length()))); + } + + // Our decode should decode %00 + const char zero_input[] = "%00"; + RawCanonOutputT<base::char16> zero_output; + DecodeURLEscapeSequences(zero_input, strlen(zero_input), &zero_output); + EXPECT_NE("%00", test_utils::ConvertUTF16ToUTF8( + base::string16(zero_output.data(), zero_output.length()))); + + // Test the error behavior for invalid UTF-8. + const char invalid_input[] = "%e4%a0%e5%a5%bd"; + const base::char16 invalid_expected[4] = {0x00e4, 0x00a0, 0x597d, 0}; + RawCanonOutputT<base::char16> invalid_output; + DecodeURLEscapeSequences(invalid_input, strlen(invalid_input), + &invalid_output); + EXPECT_EQ(base::string16(invalid_expected), + base::string16(invalid_output.data(), invalid_output.length())); +} + +TEST(URLUtilTest, TestEncodeURIComponent) { + struct EncodeCase { + const char* input; + const char* output; + } encode_cases[] = { + {"hello, world", "hello%2C%20world"}, + {"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + "%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F"}, + {"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", + "%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F"}, + {" !\"#$%&'()*+,-./", + "%20!%22%23%24%25%26%27()*%2B%2C-.%2F"}, + {"0123456789:;<=>?", + "0123456789%3A%3B%3C%3D%3E%3F"}, + {"@ABCDEFGHIJKLMNO", + "%40ABCDEFGHIJKLMNO"}, + {"PQRSTUVWXYZ[\\]^_", + "PQRSTUVWXYZ%5B%5C%5D%5E_"}, + {"`abcdefghijklmno", + "%60abcdefghijklmno"}, + {"pqrstuvwxyz{|}~\x7f", + "pqrstuvwxyz%7B%7C%7D~%7F"}, + }; + + for (size_t i = 0; i < arraysize(encode_cases); i++) { + const char* input = encode_cases[i].input; + RawCanonOutputT<char> buffer; + EncodeURIComponent(input, strlen(input), &buffer); + std::string output(buffer.data(), buffer.length()); + EXPECT_EQ(encode_cases[i].output, output); + } +} + +TEST(URLUtilTest, TestResolveRelativeWithNonStandardBase) { + // This tests non-standard (in the sense that GIsStandard() == false) + // hierarchical schemes. + struct ResolveRelativeCase { + const char* base; + const char* rel; + bool is_valid; + const char* out; + } resolve_non_standard_cases[] = { + // Resolving a relative path against a non-hierarchical URL should fail. + {"scheme:opaque_data", "/path", false, ""}, + // Resolving a relative path against a non-standard authority-based base + // URL doesn't alter the authority section. + {"scheme://Authority/", "../path", true, "scheme://Authority/path"}, + // A non-standard hierarchical base is resolved with path URL + // canonicalization rules. + {"data:/Blah:Blah/", "file.html", true, "data:/Blah:Blah/file.html"}, + {"data:/Path/../part/part2", "file.html", true, + "data:/Path/../part/file.html"}, + // Path URL canonicalization rules also apply to non-standard authority- + // based URLs. + {"custom://Authority/", "file.html", true, + "custom://Authority/file.html"}, + {"custom://Authority/", "other://Auth/", true, "other://Auth/"}, + {"custom://Authority/", "../../file.html", true, + "custom://Authority/file.html"}, + {"custom://Authority/path/", "file.html", true, + "custom://Authority/path/file.html"}, + {"custom://Authority:NoCanon/path/", "file.html", true, + "custom://Authority:NoCanon/path/file.html"}, + // It's still possible to get an invalid path URL. + {"custom://Invalid:!#Auth/", "file.html", false, ""}, + // A path with an authority section gets canonicalized under standard URL + // rules, even though the base was non-standard. + {"content://content.Provider/", "//other.Provider", true, + "content://other.provider/"}, + // Resolving an absolute URL doesn't cause canonicalization of the + // result. + {"about:blank", "custom://Authority", true, "custom://Authority"}, + // Fragment URLs can be resolved against a non-standard base. + {"scheme://Authority/path", "#fragment", true, + "scheme://Authority/path#fragment"}, + {"scheme://Authority/", "#fragment", true, "scheme://Authority/#fragment"}, + // Resolving should fail if the base URL is authority-based but is + // missing a path component (the '/' at the end). + {"scheme://Authority", "path", false, ""}, + // Test resolving a fragment (only) against any kind of base-URL. + {"about:blank", "#id42", true, "about:blank#id42" }, + {"about:blank", " #id42", true, "about:blank#id42" }, + {"about:blank#oldfrag", "#newfrag", true, "about:blank#newfrag" }, + // A surprising side effect of allowing fragments to resolve against + // any URL scheme is we might break javascript: URLs by doing so... + {"javascript:alert('foo#bar')", "#badfrag", true, + "javascript:alert('foo#badfrag" }, + }; + + for (size_t i = 0; i < arraysize(resolve_non_standard_cases); i++) { + const ResolveRelativeCase& test_data = resolve_non_standard_cases[i]; + Parsed base_parsed; + ParsePathURL(test_data.base, strlen(test_data.base), false, &base_parsed); + + std::string resolved; + StdStringCanonOutput output(&resolved); + Parsed resolved_parsed; + bool valid = ResolveRelative(test_data.base, strlen(test_data.base), + base_parsed, test_data.rel, + strlen(test_data.rel), NULL, &output, + &resolved_parsed); + output.Complete(); + + EXPECT_EQ(test_data.is_valid, valid) << i; + if (test_data.is_valid && valid) + EXPECT_EQ(test_data.out, resolved) << i; + } +} + +TEST(URLUtilTest, TestNoRefComponent) { + // The hash-mark must be ignored when mailto: scheme is + // parsed, even if the url has a base and relative part. + const char* base = "mailto://to/"; + const char* rel = "any#body"; + + Parsed base_parsed; + ParsePathURL(base, strlen(base), false, &base_parsed); + + std::string resolved; + StdStringCanonOutput output(&resolved); + Parsed resolved_parsed; + + bool valid = ResolveRelative(base, strlen(base), + base_parsed, rel, + strlen(rel), NULL, &output, + &resolved_parsed); + EXPECT_TRUE(valid); + EXPECT_FALSE(resolved_parsed.ref.is_valid()); +} + +} // namespace url