|
1 /* |
|
2 * Copyright (C) 2004, 2007, 2008, 2009 Apple Inc. All rights reserved. |
|
3 * Copyright (C) 2008, 2009 Google Inc. All rights reserved. |
|
4 * |
|
5 * Redistribution and use in source and binary forms, with or without |
|
6 * modification, are permitted provided that the following conditions are |
|
7 * met: |
|
8 * |
|
9 * * Redistributions of source code must retain the above copyright |
|
10 * notice, this list of conditions and the following disclaimer. |
|
11 * * Redistributions in binary form must reproduce the above |
|
12 * copyright notice, this list of conditions and the following disclaimer |
|
13 * in the documentation and/or other materials provided with the |
|
14 * distribution. |
|
15 * * Neither the name of Google Inc. nor the names of its |
|
16 * contributors may be used to endorse or promote products derived from |
|
17 * this software without specific prior written permission. |
|
18 * |
|
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 */ |
|
31 |
|
32 #include "config.h" |
|
33 |
|
34 #if USE(GOOGLEURL) |
|
35 #include "KURL.h" |
|
36 |
|
37 #ifndef NDEBUG |
|
38 #include <stdio.h> |
|
39 #endif |
|
40 |
|
41 #include <algorithm> |
|
42 |
|
43 #include "StringHash.h" |
|
44 #include "NotImplemented.h" |
|
45 #include "TextEncoding.h" |
|
46 #include <wtf/HashMap.h> |
|
47 #include <wtf/Vector.h> |
|
48 #include <wtf/StdLibExtras.h> |
|
49 #include <wtf/text/CString.h> |
|
50 |
|
51 #include <googleurl/src/url_util.h> |
|
52 |
|
53 using WTF::isASCIILower; |
|
54 using WTF::toASCIILower; |
|
55 using std::binary_search; |
|
56 |
|
57 namespace WebCore { |
|
58 |
|
59 static const int maximumValidPortNumber = 0xFFFE; |
|
60 static const int invalidPortNumber = 0xFFFF; |
|
61 |
|
62 // Wraps WebCore's text encoding in a character set converter for the |
|
63 // canonicalizer. |
|
64 class KURLCharsetConverter : public url_canon::CharsetConverter { |
|
65 public: |
|
66 // The encoding parameter may be NULL, but in this case the object must not |
|
67 // be called. |
|
68 KURLCharsetConverter(const TextEncoding* encoding) |
|
69 : m_encoding(encoding) |
|
70 { |
|
71 } |
|
72 |
|
73 virtual void ConvertFromUTF16(const url_parse::UTF16Char* input, int inputLength, |
|
74 url_canon::CanonOutput* output) |
|
75 { |
|
76 CString encoded = m_encoding->encode(input, inputLength, URLEncodedEntitiesForUnencodables); |
|
77 output->Append(encoded.data(), static_cast<int>(encoded.length())); |
|
78 } |
|
79 |
|
80 private: |
|
81 const TextEncoding* m_encoding; |
|
82 }; |
|
83 |
|
84 // Note that this function must be named differently than the one in KURL.cpp |
|
85 // since our unit tests evilly include both files, and their local definition |
|
86 // will be ambiguous. |
|
87 static inline void assertProtocolIsGood(const char* protocol) |
|
88 { |
|
89 #ifndef NDEBUG |
|
90 const char* p = protocol; |
|
91 while (*p) { |
|
92 ASSERT(*p > ' ' && *p < 0x7F && !(*p >= 'A' && *p <= 'Z')); |
|
93 ++p; |
|
94 } |
|
95 #endif |
|
96 } |
|
97 |
|
98 // Returns the characters for the given string, or a pointer to a static empty |
|
99 // string if the input string is NULL. This will always ensure we have a non- |
|
100 // NULL character pointer since ReplaceComponents has special meaning for NULL. |
|
101 static inline const url_parse::UTF16Char* CharactersOrEmpty(const String& str) |
|
102 { |
|
103 static const url_parse::UTF16Char zero = 0; |
|
104 return str.characters() ? |
|
105 reinterpret_cast<const url_parse::UTF16Char*>(str.characters()) : |
|
106 &zero; |
|
107 } |
|
108 |
|
109 static inline bool isUnicodeEncoding(const TextEncoding* encoding) |
|
110 { |
|
111 return encoding->encodingForFormSubmission() == UTF8Encoding(); |
|
112 } |
|
113 |
|
114 static bool lowerCaseEqualsASCII(const char* begin, const char* end, const char* str) |
|
115 { |
|
116 while (begin != end && *str) { |
|
117 ASSERT(toASCIILower(*str) == *str); |
|
118 if (toASCIILower(*begin++) != *str++) |
|
119 return false; |
|
120 } |
|
121 |
|
122 // Both strings are equal (ignoring case) if and only if all of the characters were equal, |
|
123 // and the end of both has been reached. |
|
124 return begin == end && !*str; |
|
125 } |
|
126 |
|
127 static inline bool isSchemeFirstChar(char c) |
|
128 { |
|
129 return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); |
|
130 } |
|
131 |
|
132 static inline bool isSchemeChar(char c) |
|
133 { |
|
134 return isSchemeFirstChar(c) || (c >= '0' && c <= '9') || c == '.' || c == '-' || c == '*'; |
|
135 } |
|
136 |
|
137 |
|
138 // KURLGooglePrivate ----------------------------------------------------------- |
|
139 |
|
140 KURLGooglePrivate::KURLGooglePrivate() |
|
141 : m_isValid(false) |
|
142 , m_protocolInHTTPFamily(false) |
|
143 , m_utf8IsASCII(true) |
|
144 , m_stringIsValid(false) |
|
145 { |
|
146 } |
|
147 |
|
148 KURLGooglePrivate::KURLGooglePrivate(const url_parse::Parsed& parsed, bool isValid) |
|
149 : m_isValid(isValid) |
|
150 , m_protocolInHTTPFamily(false) |
|
151 , m_parsed(parsed) |
|
152 , m_utf8IsASCII(true) |
|
153 , m_stringIsValid(false) |
|
154 { |
|
155 } |
|
156 |
|
157 // Setters for the data. Using the ASCII version when you know the |
|
158 // data is ASCII will be slightly more efficient. The UTF-8 version |
|
159 // will always be correct if the caller is unsure. |
|
160 void KURLGooglePrivate::setUtf8(const CString& str) |
|
161 { |
|
162 const char* data = str.data(); |
|
163 unsigned dataLength = str.length(); |
|
164 |
|
165 // The m_utf8IsASCII must always be correct since the DeprecatedString |
|
166 // getter must create it with the proper constructor. This test can be |
|
167 // removed when DeprecatedString is gone, but it still might be a |
|
168 // performance win. |
|
169 m_utf8IsASCII = true; |
|
170 for (unsigned i = 0; i < dataLength; i++) { |
|
171 if (static_cast<unsigned char>(data[i]) >= 0x80) { |
|
172 m_utf8IsASCII = false; |
|
173 break; |
|
174 } |
|
175 } |
|
176 |
|
177 m_utf8 = str; |
|
178 m_stringIsValid = false; |
|
179 initProtocolInHTTPFamily(); |
|
180 } |
|
181 |
|
182 void KURLGooglePrivate::setAscii(const CString& str) |
|
183 { |
|
184 m_utf8 = str; |
|
185 m_utf8IsASCII = true; |
|
186 m_stringIsValid = false; |
|
187 initProtocolInHTTPFamily(); |
|
188 } |
|
189 |
|
190 void KURLGooglePrivate::init(const KURL& base, |
|
191 const String& relative, |
|
192 const TextEncoding* queryEncoding) |
|
193 { |
|
194 init(base, relative.characters(), relative.length(), queryEncoding); |
|
195 } |
|
196 |
|
197 // Note: code mostly duplicated below. |
|
198 void KURLGooglePrivate::init(const KURL& base, const char* rel, int relLength, |
|
199 const TextEncoding* queryEncoding) |
|
200 { |
|
201 // As a performance optimization, we do not use the charset converter if |
|
202 // encoding is UTF-8 or other Unicode encodings. Note that this is |
|
203 // per HTML5 2.5.3 (resolving URL). The URL canonicalizer will be |
|
204 // more efficient with no charset converter object because it |
|
205 // can do UTF-8 internally with no extra copies. |
|
206 |
|
207 // We feel free to make the charset converter object every time since it's |
|
208 // just a wrapper around a reference. |
|
209 KURLCharsetConverter charsetConverterObject(queryEncoding); |
|
210 KURLCharsetConverter* charsetConverter = |
|
211 (!queryEncoding || isUnicodeEncoding(queryEncoding)) ? 0 : |
|
212 &charsetConverterObject; |
|
213 |
|
214 url_canon::RawCanonOutputT<char> output; |
|
215 const CString& baseStr = base.m_url.utf8String(); |
|
216 m_isValid = url_util::ResolveRelative(baseStr.data(), baseStr.length(), |
|
217 base.m_url.m_parsed, rel, relLength, |
|
218 charsetConverter, |
|
219 &output, &m_parsed); |
|
220 |
|
221 // See FIXME in KURLGooglePrivate in the header. If canonicalization has not |
|
222 // changed the string, we can avoid an extra allocation by using assignment. |
|
223 // |
|
224 // When KURL encounters an error such that the URL is invalid and empty |
|
225 // (for example, resolving a relative URL on a non-hierarchical base), it |
|
226 // will produce an isNull URL, and calling setUtf8 will produce an empty |
|
227 // non-null URL. This is unlikely to affect anything, but we preserve this |
|
228 // just in case. |
|
229 if (m_isValid || output.length()) { |
|
230 // Without ref, the whole url is guaranteed to be ASCII-only. |
|
231 if (m_parsed.ref.is_nonempty()) |
|
232 setUtf8(CString(output.data(), output.length())); |
|
233 else |
|
234 setAscii(CString(output.data(), output.length())); |
|
235 } else { |
|
236 // WebCore expects resolved URLs to be empty rather than NULL. |
|
237 setUtf8(CString("", 0)); |
|
238 } |
|
239 } |
|
240 |
|
241 // Note: code mostly duplicated above. See FIXMEs and comments there. |
|
242 void KURLGooglePrivate::init(const KURL& base, const UChar* rel, int relLength, |
|
243 const TextEncoding* queryEncoding) |
|
244 { |
|
245 KURLCharsetConverter charsetConverterObject(queryEncoding); |
|
246 KURLCharsetConverter* charsetConverter = |
|
247 (!queryEncoding || isUnicodeEncoding(queryEncoding)) ? 0 : |
|
248 &charsetConverterObject; |
|
249 |
|
250 url_canon::RawCanonOutputT<char> output; |
|
251 const CString& baseStr = base.m_url.utf8String(); |
|
252 m_isValid = url_util::ResolveRelative(baseStr.data(), baseStr.length(), |
|
253 base.m_url.m_parsed, rel, relLength, |
|
254 charsetConverter, |
|
255 &output, &m_parsed); |
|
256 |
|
257 |
|
258 if (m_isValid || output.length()) { |
|
259 if (m_parsed.ref.is_nonempty()) |
|
260 setUtf8(CString(output.data(), output.length())); |
|
261 else |
|
262 setAscii(CString(output.data(), output.length())); |
|
263 } else |
|
264 setUtf8(CString("", 0)); |
|
265 } |
|
266 |
|
267 void KURLGooglePrivate::initProtocolInHTTPFamily() |
|
268 { |
|
269 if (!m_isValid) { |
|
270 m_protocolInHTTPFamily = false; |
|
271 return; |
|
272 } |
|
273 |
|
274 const char* scheme = m_utf8.data() + m_parsed.scheme.begin; |
|
275 if (m_parsed.scheme.len == 4) |
|
276 m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 4, "http"); |
|
277 else if (m_parsed.scheme.len == 5) |
|
278 m_protocolInHTTPFamily = lowerCaseEqualsASCII(scheme, scheme + 5, "https"); |
|
279 else |
|
280 m_protocolInHTTPFamily = false; |
|
281 } |
|
282 |
|
283 void KURLGooglePrivate::copyTo(KURLGooglePrivate* dest) const |
|
284 { |
|
285 dest->m_isValid = m_isValid; |
|
286 dest->m_protocolInHTTPFamily = m_protocolInHTTPFamily; |
|
287 dest->m_parsed = m_parsed; |
|
288 |
|
289 // Don't copy the 16-bit string since that will be regenerated as needed. |
|
290 dest->m_utf8 = CString(m_utf8.data(), m_utf8.length()); |
|
291 dest->m_utf8IsASCII = m_utf8IsASCII; |
|
292 dest->m_stringIsValid = false; |
|
293 } |
|
294 |
|
295 String KURLGooglePrivate::componentString(const url_parse::Component& comp) const |
|
296 { |
|
297 if (!m_isValid || comp.len <= 0) { |
|
298 // KURL returns a NULL string if the URL is itself a NULL string, and an |
|
299 // empty string for other nonexistant entities. |
|
300 if (utf8String().isNull()) |
|
301 return String(); |
|
302 return String("", 0); |
|
303 } |
|
304 // begin and len are in terms of bytes which do not match |
|
305 // if string() is UTF-16 and input contains non-ASCII characters. |
|
306 // However, the only part in urlString that can contain non-ASCII |
|
307 // characters is 'ref' at the end of the string. In that case, |
|
308 // begin will always match the actual value and len (in terms of |
|
309 // byte) will be longer than what's needed by 'mid'. However, mid |
|
310 // truncates len to avoid go past the end of a string so that we can |
|
311 // get away withtout doing anything here. |
|
312 return string().substring(comp.begin, comp.len); |
|
313 } |
|
314 |
|
315 void KURLGooglePrivate::replaceComponents(const Replacements& replacements) |
|
316 { |
|
317 url_canon::RawCanonOutputT<char> output; |
|
318 url_parse::Parsed newParsed; |
|
319 |
|
320 m_isValid = url_util::ReplaceComponents(utf8String().data(), |
|
321 utf8String().length(), m_parsed, replacements, 0, &output, &newParsed); |
|
322 |
|
323 m_parsed = newParsed; |
|
324 if (m_parsed.ref.is_nonempty()) |
|
325 setUtf8(CString(output.data(), output.length())); |
|
326 else |
|
327 setAscii(CString(output.data(), output.length())); |
|
328 } |
|
329 |
|
330 const String& KURLGooglePrivate::string() const |
|
331 { |
|
332 if (!m_stringIsValid) { |
|
333 // Must special case the NULL case, since constructing the |
|
334 // string like we do below will generate an empty rather than |
|
335 // a NULL string. |
|
336 if (m_utf8.isNull()) |
|
337 m_string = String(); |
|
338 else if (m_utf8IsASCII) |
|
339 m_string = String(m_utf8.data(), m_utf8.length()); |
|
340 else |
|
341 m_string = String::fromUTF8(m_utf8.data(), m_utf8.length()); |
|
342 m_stringIsValid = true; |
|
343 } |
|
344 return m_string; |
|
345 } |
|
346 |
|
347 // KURL ------------------------------------------------------------------------ |
|
348 |
|
349 // Creates with NULL-terminated string input representing an absolute URL. |
|
350 // WebCore generally calls this only with hardcoded strings, so the input is |
|
351 // ASCII. We treat is as UTF-8 just in case. |
|
352 KURL::KURL(ParsedURLStringTag, const char *url) |
|
353 { |
|
354 // FIXME The Mac code checks for beginning with a slash and converting to a |
|
355 // file: URL. We will want to add this as well once we can compile on a |
|
356 // system like that. |
|
357 m_url.init(KURL(), url, strlen(url), 0); |
|
358 |
|
359 // The one-argument constructors should never generate a NULL string. |
|
360 // This is a funny quirk of KURL.cpp (probably a bug) which we preserve. |
|
361 if (m_url.utf8String().isNull()) |
|
362 m_url.setAscii(CString("", 0)); |
|
363 } |
|
364 |
|
365 // Initializes with a string representing an absolute URL. No encoding |
|
366 // information is specified. This generally happens when a KURL is converted |
|
367 // to a string and then converted back. In this case, the URL is already |
|
368 // canonical and in proper escaped form so needs no encoding. We treat it was |
|
369 // UTF-8 just in case. |
|
370 KURL::KURL(ParsedURLStringTag, const String& url) |
|
371 { |
|
372 if (!url.isNull()) |
|
373 m_url.init(KURL(), url, 0); |
|
374 else { |
|
375 // WebCore expects us to preserve the nullness of strings when this |
|
376 // constructor is used. In all other cases, it expects a non-null |
|
377 // empty string, which is what init() will create. |
|
378 m_url.m_isValid = false; |
|
379 m_url.m_protocolInHTTPFamily = false; |
|
380 } |
|
381 } |
|
382 |
|
383 // Constructs a new URL given a base URL and a possibly relative input URL. |
|
384 // This assumes UTF-8 encoding. |
|
385 KURL::KURL(const KURL& base, const String& relative) |
|
386 { |
|
387 m_url.init(base, relative, 0); |
|
388 } |
|
389 |
|
390 // Constructs a new URL given a base URL and a possibly relative input URL. |
|
391 // Any query portion of the relative URL will be encoded in the given encoding. |
|
392 KURL::KURL(const KURL& base, |
|
393 const String& relative, |
|
394 const TextEncoding& encoding) |
|
395 { |
|
396 m_url.init(base, relative, &encoding.encodingForFormSubmission()); |
|
397 } |
|
398 |
|
399 KURL::KURL(const CString& canonicalSpec, |
|
400 const url_parse::Parsed& parsed, bool isValid) |
|
401 : m_url(parsed, isValid) |
|
402 { |
|
403 // We know the reference fragment is the only part that can be UTF-8, so |
|
404 // we know it's ASCII when there is no ref. |
|
405 if (parsed.ref.is_nonempty()) |
|
406 m_url.setUtf8(canonicalSpec); |
|
407 else |
|
408 m_url.setAscii(canonicalSpec); |
|
409 } |
|
410 |
|
411 #if PLATFORM(CF) |
|
412 KURL::KURL(CFURLRef) |
|
413 { |
|
414 notImplemented(); |
|
415 invalidate(); |
|
416 } |
|
417 |
|
418 CFURLRef KURL::createCFURL() const |
|
419 { |
|
420 notImplemented(); |
|
421 return 0; |
|
422 } |
|
423 #endif |
|
424 |
|
425 KURL KURL::copy() const |
|
426 { |
|
427 KURL result = *this; |
|
428 m_url.copyTo(&result.m_url); |
|
429 return result; |
|
430 } |
|
431 |
|
432 bool KURL::isNull() const |
|
433 { |
|
434 return m_url.utf8String().isNull(); |
|
435 } |
|
436 |
|
437 bool KURL::isEmpty() const |
|
438 { |
|
439 return !m_url.utf8String().length(); |
|
440 } |
|
441 |
|
442 bool KURL::isValid() const |
|
443 { |
|
444 return m_url.m_isValid; |
|
445 } |
|
446 |
|
447 bool KURL::hasPort() const |
|
448 { |
|
449 return hostEnd() < pathStart(); |
|
450 } |
|
451 |
|
452 bool KURL::protocolInHTTPFamily() const |
|
453 { |
|
454 return m_url.m_protocolInHTTPFamily; |
|
455 } |
|
456 |
|
457 bool KURL::hasPath() const |
|
458 { |
|
459 // Note that http://www.google.com/" has a path, the path is "/". This can |
|
460 // return false only for invalid or nonstandard URLs. |
|
461 return m_url.m_parsed.path.len >= 0; |
|
462 } |
|
463 |
|
464 // We handle "parameters" separated by a semicolon, while KURL.cpp does not, |
|
465 // which can lead to different results in some cases. |
|
466 String KURL::lastPathComponent() const |
|
467 { |
|
468 // When the output ends in a slash, WebCore has different expectations than |
|
469 // the GoogleURL library. For "/foo/bar/" the library will return the empty |
|
470 // string, but WebCore wants "bar". |
|
471 url_parse::Component path = m_url.m_parsed.path; |
|
472 if (path.len > 0 && m_url.utf8String().data()[path.end() - 1] == '/') |
|
473 path.len--; |
|
474 |
|
475 url_parse::Component file; |
|
476 url_parse::ExtractFileName(m_url.utf8String().data(), path, &file); |
|
477 |
|
478 // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns |
|
479 // a null string when the path is empty, which we duplicate here. |
|
480 if (!file.is_nonempty()) |
|
481 return String(); |
|
482 return m_url.componentString(file); |
|
483 } |
|
484 |
|
485 String KURL::protocol() const |
|
486 { |
|
487 return m_url.componentString(m_url.m_parsed.scheme); |
|
488 } |
|
489 |
|
490 String KURL::host() const |
|
491 { |
|
492 // Note: KURL.cpp unescapes here. |
|
493 return m_url.componentString(m_url.m_parsed.host); |
|
494 } |
|
495 |
|
496 // Returns 0 when there is no port. |
|
497 // |
|
498 // We treat URL's with out-of-range port numbers as invalid URLs, and they will |
|
499 // be rejected by the canonicalizer. KURL.cpp will allow them in parsing, but |
|
500 // return invalidPortNumber from this port() function, so we mirror that behavior here. |
|
501 unsigned short KURL::port() const |
|
502 { |
|
503 if (!m_url.m_isValid || m_url.m_parsed.port.len <= 0) |
|
504 return 0; |
|
505 int port = url_parse::ParsePort(m_url.utf8String().data(), m_url.m_parsed.port); |
|
506 ASSERT(port != url_parse::PORT_UNSPECIFIED); // Checked port.len <= 0 before. |
|
507 |
|
508 if (port == url_parse::PORT_INVALID || port > maximumValidPortNumber) // Mimic KURL::port() |
|
509 port = invalidPortNumber; |
|
510 |
|
511 return static_cast<unsigned short>(port); |
|
512 } |
|
513 |
|
514 // Returns the empty string if there is no password. |
|
515 String KURL::pass() const |
|
516 { |
|
517 // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns |
|
518 // a null string when the password is empty, which we duplicate here. |
|
519 if (!m_url.m_parsed.password.is_nonempty()) |
|
520 return String(); |
|
521 |
|
522 // Note: KURL.cpp unescapes here. |
|
523 return m_url.componentString(m_url.m_parsed.password); |
|
524 } |
|
525 |
|
526 // Returns the empty string if there is no username. |
|
527 String KURL::user() const |
|
528 { |
|
529 // Note: KURL.cpp unescapes here. |
|
530 return m_url.componentString(m_url.m_parsed.username); |
|
531 } |
|
532 |
|
533 String KURL::fragmentIdentifier() const |
|
534 { |
|
535 // Empty but present refs ("foo.com/bar#") should result in the empty |
|
536 // string, which m_url.componentString will produce. Nonexistant refs should be |
|
537 // the NULL string. |
|
538 if (!m_url.m_parsed.ref.is_valid()) |
|
539 return String(); |
|
540 |
|
541 // Note: KURL.cpp unescapes here. |
|
542 return m_url.componentString(m_url.m_parsed.ref); |
|
543 } |
|
544 |
|
545 bool KURL::hasFragmentIdentifier() const |
|
546 { |
|
547 // Note: KURL.cpp unescapes here. |
|
548 // FIXME determine if KURL.cpp agrees about an empty ref |
|
549 return m_url.m_parsed.ref.len >= 0; |
|
550 } |
|
551 |
|
552 String KURL::baseAsString() const |
|
553 { |
|
554 // FIXME: There is probably a more efficient way to do this? |
|
555 return string().left(pathAfterLastSlash()); |
|
556 } |
|
557 |
|
558 String KURL::query() const |
|
559 { |
|
560 if (m_url.m_parsed.query.len >= 0) |
|
561 return m_url.componentString(m_url.m_parsed.query); |
|
562 |
|
563 // Bug: https://bugs.webkit.org/show_bug.cgi?id=21015 this function returns |
|
564 // an empty string when the query is empty rather than a null (not sure |
|
565 // which is right). |
|
566 // Returns a null if the query is not specified, instead of empty. |
|
567 if (m_url.m_parsed.query.is_valid()) |
|
568 return String("", 0); |
|
569 return String(); |
|
570 } |
|
571 |
|
572 String KURL::path() const |
|
573 { |
|
574 // Note: KURL.cpp unescapes here. |
|
575 return m_url.componentString(m_url.m_parsed.path); |
|
576 } |
|
577 |
|
578 bool KURL::setProtocol(const String& protocol) |
|
579 { |
|
580 // Firefox and IE remove everything after the first ':'. |
|
581 int separatorPosition = protocol.find(':'); |
|
582 String newProtocol = protocol.substring(0, separatorPosition); |
|
583 |
|
584 // If KURL is given an invalid scheme, it returns failure without modifying |
|
585 // the URL at all. This is in contrast to most other setters which modify |
|
586 // the URL and set "m_isValid." |
|
587 url_canon::RawCanonOutputT<char> canonProtocol; |
|
588 url_parse::Component protocolComponent; |
|
589 if (!url_canon::CanonicalizeScheme(newProtocol.characters(), |
|
590 url_parse::Component(0, newProtocol.length()), |
|
591 &canonProtocol, &protocolComponent) |
|
592 || !protocolComponent.is_nonempty()) |
|
593 return false; |
|
594 |
|
595 KURLGooglePrivate::Replacements replacements; |
|
596 replacements.SetScheme(CharactersOrEmpty(newProtocol), |
|
597 url_parse::Component(0, newProtocol.length())); |
|
598 m_url.replaceComponents(replacements); |
|
599 |
|
600 // isValid could be false but we still return true here. This is because |
|
601 // WebCore or JS scripts can build up a URL by setting individual |
|
602 // components, and a JS exception is based on the return value of this |
|
603 // function. We want to throw the exception and stop the script only when |
|
604 // its trying to set a bad protocol, and not when it maybe just hasn't |
|
605 // finished building up its final scheme. |
|
606 return true; |
|
607 } |
|
608 |
|
609 void KURL::setHost(const String& host) |
|
610 { |
|
611 KURLGooglePrivate::Replacements replacements; |
|
612 replacements.SetHost(CharactersOrEmpty(host), |
|
613 url_parse::Component(0, host.length())); |
|
614 m_url.replaceComponents(replacements); |
|
615 } |
|
616 |
|
617 void KURL::setHostAndPort(const String& s) |
|
618 { |
|
619 String host = s; |
|
620 String port; |
|
621 int hostEnd = s.find(":"); |
|
622 if (hostEnd != -1) { |
|
623 host = s.left(hostEnd); |
|
624 port = s.substring(hostEnd + 1); |
|
625 } |
|
626 |
|
627 KURLGooglePrivate::Replacements replacements; |
|
628 // Host can't be removed, so we always set. |
|
629 replacements.SetHost(CharactersOrEmpty(host), |
|
630 url_parse::Component(0, host.length())); |
|
631 |
|
632 if (port.isEmpty()) // Port may be removed, so we support clearing. |
|
633 replacements.ClearPort(); |
|
634 else |
|
635 replacements.SetPort(CharactersOrEmpty(port), url_parse::Component(0, port.length())); |
|
636 m_url.replaceComponents(replacements); |
|
637 } |
|
638 |
|
639 void KURL::removePort() |
|
640 { |
|
641 if (hasPort()) { |
|
642 String urlWithoutPort = m_url.string().left(hostEnd()) + m_url.string().substring(pathStart()); |
|
643 m_url.setUtf8(urlWithoutPort.utf8()); |
|
644 } |
|
645 } |
|
646 |
|
647 void KURL::setPort(unsigned short i) |
|
648 { |
|
649 KURLGooglePrivate::Replacements replacements; |
|
650 String portStr; |
|
651 if (i) { |
|
652 portStr = String::number(i); |
|
653 replacements.SetPort( |
|
654 reinterpret_cast<const url_parse::UTF16Char*>(portStr.characters()), |
|
655 url_parse::Component(0, portStr.length())); |
|
656 |
|
657 } else { |
|
658 // Clear any existing port when it is set to 0. |
|
659 replacements.ClearPort(); |
|
660 } |
|
661 m_url.replaceComponents(replacements); |
|
662 } |
|
663 |
|
664 void KURL::setUser(const String& user) |
|
665 { |
|
666 // This function is commonly called to clear the username, which we |
|
667 // normally don't have, so we optimize this case. |
|
668 if (user.isEmpty() && !m_url.m_parsed.username.is_valid()) |
|
669 return; |
|
670 |
|
671 // The canonicalizer will clear any usernames that are empty, so we |
|
672 // don't have to explicitly call ClearUsername() here. |
|
673 KURLGooglePrivate::Replacements replacements; |
|
674 replacements.SetUsername(CharactersOrEmpty(user), |
|
675 url_parse::Component(0, user.length())); |
|
676 m_url.replaceComponents(replacements); |
|
677 } |
|
678 |
|
679 void KURL::setPass(const String& pass) |
|
680 { |
|
681 // This function is commonly called to clear the password, which we |
|
682 // normally don't have, so we optimize this case. |
|
683 if (pass.isEmpty() && !m_url.m_parsed.password.is_valid()) |
|
684 return; |
|
685 |
|
686 // The canonicalizer will clear any passwords that are empty, so we |
|
687 // don't have to explicitly call ClearUsername() here. |
|
688 KURLGooglePrivate::Replacements replacements; |
|
689 replacements.SetPassword(CharactersOrEmpty(pass), |
|
690 url_parse::Component(0, pass.length())); |
|
691 m_url.replaceComponents(replacements); |
|
692 } |
|
693 |
|
694 void KURL::setFragmentIdentifier(const String& s) |
|
695 { |
|
696 // This function is commonly called to clear the ref, which we |
|
697 // normally don't have, so we optimize this case. |
|
698 if (s.isNull() && !m_url.m_parsed.ref.is_valid()) |
|
699 return; |
|
700 |
|
701 KURLGooglePrivate::Replacements replacements; |
|
702 if (s.isNull()) |
|
703 replacements.ClearRef(); |
|
704 else |
|
705 replacements.SetRef(CharactersOrEmpty(s), url_parse::Component(0, s.length())); |
|
706 m_url.replaceComponents(replacements); |
|
707 } |
|
708 |
|
709 void KURL::removeFragmentIdentifier() |
|
710 { |
|
711 KURLGooglePrivate::Replacements replacements; |
|
712 replacements.ClearRef(); |
|
713 m_url.replaceComponents(replacements); |
|
714 } |
|
715 |
|
716 void KURL::setQuery(const String& query) |
|
717 { |
|
718 KURLGooglePrivate::Replacements replacements; |
|
719 if (query.isNull()) { |
|
720 // KURL.cpp sets to NULL to clear any query. |
|
721 replacements.ClearQuery(); |
|
722 } else if (query.length() > 0 && query[0] == '?') { |
|
723 // WebCore expects the query string to begin with a question mark, but |
|
724 // GoogleURL doesn't. So we trim off the question mark when setting. |
|
725 replacements.SetQuery(CharactersOrEmpty(query), |
|
726 url_parse::Component(1, query.length() - 1)); |
|
727 } else { |
|
728 // When set with the empty string or something that doesn't begin with |
|
729 // a question mark, KURL.cpp will add a question mark for you. The only |
|
730 // way this isn't compatible is if you call this function with an empty |
|
731 // string. KURL.cpp will leave a '?' with nothing following it in the |
|
732 // URL, whereas we'll clear it. |
|
733 // FIXME We should eliminate this difference. |
|
734 replacements.SetQuery(CharactersOrEmpty(query), |
|
735 url_parse::Component(0, query.length())); |
|
736 } |
|
737 m_url.replaceComponents(replacements); |
|
738 } |
|
739 |
|
740 void KURL::setPath(const String& path) |
|
741 { |
|
742 // Empty paths will be canonicalized to "/", so we don't have to worry |
|
743 // about calling ClearPath(). |
|
744 KURLGooglePrivate::Replacements replacements; |
|
745 replacements.SetPath(CharactersOrEmpty(path), |
|
746 url_parse::Component(0, path.length())); |
|
747 m_url.replaceComponents(replacements); |
|
748 } |
|
749 |
|
750 // On Mac, this just seems to return the same URL, but with "/foo/bar" for |
|
751 // file: URLs instead of file:///foo/bar. We don't bother with any of this, |
|
752 // at least for now. |
|
753 String KURL::prettyURL() const |
|
754 { |
|
755 if (!m_url.m_isValid) |
|
756 return String(); |
|
757 return m_url.string(); |
|
758 } |
|
759 |
|
760 bool protocolIsJavaScript(const String& url) |
|
761 { |
|
762 return protocolIs(url, "javascript"); |
|
763 } |
|
764 |
|
765 // We copied the KURL version here on Dec 4, 2009 while doing a WebKit |
|
766 // merge. |
|
767 // |
|
768 // FIXME Somehow share this with KURL? Like we'd theoretically merge with |
|
769 // decodeURLEscapeSequences below? |
|
770 bool isDefaultPortForProtocol(unsigned short port, const String& protocol) |
|
771 { |
|
772 if (protocol.isEmpty()) |
|
773 return false; |
|
774 |
|
775 typedef HashMap<String, unsigned, CaseFoldingHash> DefaultPortsMap; |
|
776 DEFINE_STATIC_LOCAL(DefaultPortsMap, defaultPorts, ()); |
|
777 if (defaultPorts.isEmpty()) { |
|
778 defaultPorts.set("http", 80); |
|
779 defaultPorts.set("https", 443); |
|
780 defaultPorts.set("ftp", 21); |
|
781 defaultPorts.set("ftps", 990); |
|
782 } |
|
783 return defaultPorts.get(protocol) == port; |
|
784 } |
|
785 |
|
786 // We copied the KURL version here on Dec 4, 2009 while doing a WebKit |
|
787 // merge. |
|
788 // |
|
789 // FIXME Somehow share this with KURL? Like we'd theoretically merge with |
|
790 // decodeURLEscapeSequences below? |
|
791 bool portAllowed(const KURL& url) |
|
792 { |
|
793 unsigned short port = url.port(); |
|
794 |
|
795 // Since most URLs don't have a port, return early for the "no port" case. |
|
796 if (!port) |
|
797 return true; |
|
798 |
|
799 // This blocked port list matches the port blocking that Mozilla implements. |
|
800 // See http://www.mozilla.org/projects/netlib/PortBanning.html for more information. |
|
801 static const unsigned short blockedPortList[] = { |
|
802 1, // tcpmux |
|
803 7, // echo |
|
804 9, // discard |
|
805 11, // systat |
|
806 13, // daytime |
|
807 15, // netstat |
|
808 17, // qotd |
|
809 19, // chargen |
|
810 20, // FTP-data |
|
811 21, // FTP-control |
|
812 22, // SSH |
|
813 23, // telnet |
|
814 25, // SMTP |
|
815 37, // time |
|
816 42, // name |
|
817 43, // nicname |
|
818 53, // domain |
|
819 77, // priv-rjs |
|
820 79, // finger |
|
821 87, // ttylink |
|
822 95, // supdup |
|
823 101, // hostriame |
|
824 102, // iso-tsap |
|
825 103, // gppitnp |
|
826 104, // acr-nema |
|
827 109, // POP2 |
|
828 110, // POP3 |
|
829 111, // sunrpc |
|
830 113, // auth |
|
831 115, // SFTP |
|
832 117, // uucp-path |
|
833 119, // nntp |
|
834 123, // NTP |
|
835 135, // loc-srv / epmap |
|
836 139, // netbios |
|
837 143, // IMAP2 |
|
838 179, // BGP |
|
839 389, // LDAP |
|
840 465, // SMTP+SSL |
|
841 512, // print / exec |
|
842 513, // login |
|
843 514, // shell |
|
844 515, // printer |
|
845 526, // tempo |
|
846 530, // courier |
|
847 531, // Chat |
|
848 532, // netnews |
|
849 540, // UUCP |
|
850 556, // remotefs |
|
851 563, // NNTP+SSL |
|
852 587, // ESMTP |
|
853 601, // syslog-conn |
|
854 636, // LDAP+SSL |
|
855 993, // IMAP+SSL |
|
856 995, // POP3+SSL |
|
857 2049, // NFS |
|
858 3659, // apple-sasl / PasswordServer [Apple addition] |
|
859 4045, // lockd |
|
860 6000, // X11 |
|
861 6665, // Alternate IRC [Apple addition] |
|
862 6666, // Alternate IRC [Apple addition] |
|
863 6667, // Standard IRC [Apple addition] |
|
864 6668, // Alternate IRC [Apple addition] |
|
865 6669, // Alternate IRC [Apple addition] |
|
866 invalidPortNumber, // Used to block all invalid port numbers |
|
867 }; |
|
868 const unsigned short* const blockedPortListEnd = blockedPortList + sizeof(blockedPortList) / sizeof(blockedPortList[0]); |
|
869 |
|
870 #ifndef NDEBUG |
|
871 // The port list must be sorted for binary_search to work. |
|
872 static bool checkedPortList = false; |
|
873 if (!checkedPortList) { |
|
874 for (const unsigned short* p = blockedPortList; p != blockedPortListEnd - 1; ++p) |
|
875 ASSERT(*p < *(p + 1)); |
|
876 checkedPortList = true; |
|
877 } |
|
878 #endif |
|
879 |
|
880 // If the port is not in the blocked port list, allow it. |
|
881 if (!binary_search(blockedPortList, blockedPortListEnd, port)) |
|
882 return true; |
|
883 |
|
884 // Allow ports 21 and 22 for FTP URLs, as Mozilla does. |
|
885 if ((port == 21 || port == 22) && url.protocolIs("ftp")) |
|
886 return true; |
|
887 |
|
888 // Allow any port number in a file URL, since the port number is ignored. |
|
889 if (url.protocolIs("file")) |
|
890 return true; |
|
891 |
|
892 return false; |
|
893 } |
|
894 |
|
895 // We copied the KURL version here on Sept 12, 2008 while doing a WebKit |
|
896 // merge. |
|
897 // |
|
898 // FIXME Somehow share this with KURL? Like we'd theoretically merge with |
|
899 // decodeURLEscapeSequences below? |
|
900 String mimeTypeFromDataURL(const String& url) |
|
901 { |
|
902 ASSERT(protocolIs(url, "data")); |
|
903 int index = url.find(';'); |
|
904 if (index == -1) |
|
905 index = url.find(','); |
|
906 if (index != -1) { |
|
907 int len = index - 5; |
|
908 if (len > 0) |
|
909 return url.substring(5, len); |
|
910 return "text/plain"; // Data URLs with no MIME type are considered text/plain. |
|
911 } |
|
912 return ""; |
|
913 } |
|
914 |
|
915 String decodeURLEscapeSequences(const String& str) |
|
916 { |
|
917 return decodeURLEscapeSequences(str, UTF8Encoding()); |
|
918 } |
|
919 |
|
920 // In KURL.cpp's implementation, this is called by every component getter. |
|
921 // It will unescape every character, including NULL. This is scary, and may |
|
922 // cause security holes. We never call this function for components, and |
|
923 // just return the ASCII versions instead. |
|
924 // |
|
925 // This function is also used to decode javascript: URLs and as a general |
|
926 // purpose unescaping function. |
|
927 // |
|
928 // FIXME These should be merged to the KURL.cpp implementation. |
|
929 String decodeURLEscapeSequences(const String& str, const TextEncoding& encoding) |
|
930 { |
|
931 // FIXME We can probably use KURL.cpp's version of this function |
|
932 // without modification. However, I'm concerned about |
|
933 // https://bugs.webkit.org/show_bug.cgi?id=20559 so am keeping this old |
|
934 // custom code for now. Using their version will also fix the bug that |
|
935 // we ignore the encoding. |
|
936 // |
|
937 // FIXME b/1350291: This does not get called very often. We just convert |
|
938 // first to 8-bit UTF-8, then unescape, then back to 16-bit. This kind of |
|
939 // sucks, and we don't use the encoding properly, which will make some |
|
940 // obscure anchor navigations fail. |
|
941 CString cstr = str.utf8(); |
|
942 |
|
943 const char* input = cstr.data(); |
|
944 int inputLength = cstr.length(); |
|
945 |
|
946 url_canon::RawCanonOutputT<url_parse::UTF16Char> unescaped; |
|
947 |
|
948 url_util::DecodeURLEscapeSequences(input, inputLength, &unescaped); |
|
949 |
|
950 return String(reinterpret_cast<UChar*>(unescaped.data()), |
|
951 unescaped.length()); |
|
952 } |
|
953 |
|
954 bool KURL::protocolIs(const char* protocol) const |
|
955 { |
|
956 assertProtocolIsGood(protocol); |
|
957 |
|
958 // JavaScript URLs are "valid" and should be executed even if KURL decides they are invalid. |
|
959 // The free function protocolIsJavaScript() should be used instead. |
|
960 // FIXME: Chromium code needs to be fixed for this assert to be enabled. ASSERT(strcmp(protocol, "javascript")); |
|
961 |
|
962 if (m_url.m_parsed.scheme.len <= 0) |
|
963 return !protocol; |
|
964 return lowerCaseEqualsASCII( |
|
965 m_url.utf8String().data() + m_url.m_parsed.scheme.begin, |
|
966 m_url.utf8String().data() + m_url.m_parsed.scheme.end(), |
|
967 protocol); |
|
968 } |
|
969 |
|
970 bool KURL::isLocalFile() const |
|
971 { |
|
972 return protocolIs("file"); |
|
973 } |
|
974 |
|
975 // This is called to escape a URL string. It is only used externally when |
|
976 // constructing mailto: links to set the query section. Since our query setter |
|
977 // will automatically do the correct escaping, this function does not have to |
|
978 // do any work. |
|
979 // |
|
980 // There is a possibility that a future called may use this function in other |
|
981 // ways, and may expect to get a valid URL string. The dangerous thing we want |
|
982 // to protect against here is accidentally getting NULLs in a string that is |
|
983 // not supposed to have NULLs. Therefore, we escape NULLs here to prevent this. |
|
984 String encodeWithURLEscapeSequences(const String& notEncodedString) |
|
985 { |
|
986 CString utf8 = UTF8Encoding().encode( |
|
987 reinterpret_cast<const UChar*>(notEncodedString.characters()), |
|
988 notEncodedString.length(), |
|
989 URLEncodedEntitiesForUnencodables); |
|
990 const char* input = utf8.data(); |
|
991 int inputLength = utf8.length(); |
|
992 |
|
993 Vector<char, 2048> buffer; |
|
994 for (int i = 0; i < inputLength; i++) { |
|
995 if (!input[i]) |
|
996 buffer.append("%00", 3); |
|
997 else |
|
998 buffer.append(input[i]); |
|
999 } |
|
1000 return String(buffer.data(), buffer.size()); |
|
1001 } |
|
1002 |
|
1003 bool KURL::isHierarchical() const |
|
1004 { |
|
1005 if (!m_url.m_parsed.scheme.is_nonempty()) |
|
1006 return false; |
|
1007 return url_util::IsStandard( |
|
1008 &m_url.utf8String().data()[m_url.m_parsed.scheme.begin], |
|
1009 m_url.m_parsed.scheme); |
|
1010 } |
|
1011 |
|
1012 #ifndef NDEBUG |
|
1013 void KURL::print() const |
|
1014 { |
|
1015 printf("%s\n", m_url.utf8String().data()); |
|
1016 } |
|
1017 #endif |
|
1018 |
|
1019 void KURL::invalidate() |
|
1020 { |
|
1021 // This is only called from the constructor so resetting the (automatically |
|
1022 // initialized) string and parsed structure would be a waste of time. |
|
1023 m_url.m_isValid = false; |
|
1024 m_url.m_protocolInHTTPFamily = false; |
|
1025 } |
|
1026 |
|
1027 // Equal up to reference fragments, if any. |
|
1028 bool equalIgnoringFragmentIdentifier(const KURL& a, const KURL& b) |
|
1029 { |
|
1030 // Compute the length of each URL without its ref. Note that the reference |
|
1031 // begin (if it exists) points to the character *after* the '#', so we need |
|
1032 // to subtract one. |
|
1033 int aLength = a.m_url.utf8String().length(); |
|
1034 if (a.m_url.m_parsed.ref.len >= 0) |
|
1035 aLength = a.m_url.m_parsed.ref.begin - 1; |
|
1036 |
|
1037 int bLength = b.m_url.utf8String().length(); |
|
1038 if (b.m_url.m_parsed.ref.len >= 0) |
|
1039 bLength = b.m_url.m_parsed.ref.begin - 1; |
|
1040 |
|
1041 return aLength == bLength |
|
1042 && !strncmp(a.m_url.utf8String().data(), b.m_url.utf8String().data(), aLength); |
|
1043 } |
|
1044 |
|
1045 unsigned KURL::hostStart() const |
|
1046 { |
|
1047 return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::HOST, false); |
|
1048 } |
|
1049 |
|
1050 unsigned KURL::hostEnd() const |
|
1051 { |
|
1052 return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PORT, true); |
|
1053 } |
|
1054 |
|
1055 unsigned KURL::pathStart() const |
|
1056 { |
|
1057 return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PATH, false); |
|
1058 } |
|
1059 |
|
1060 unsigned KURL::pathEnd() const |
|
1061 { |
|
1062 return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::QUERY, true); |
|
1063 } |
|
1064 |
|
1065 unsigned KURL::pathAfterLastSlash() const |
|
1066 { |
|
1067 // When there's no path, ask for what would be the beginning of it. |
|
1068 if (!m_url.m_parsed.path.is_valid()) |
|
1069 return m_url.m_parsed.CountCharactersBefore(url_parse::Parsed::PATH, false); |
|
1070 |
|
1071 url_parse::Component filename; |
|
1072 url_parse::ExtractFileName(m_url.utf8String().data(), m_url.m_parsed.path, |
|
1073 &filename); |
|
1074 return filename.begin; |
|
1075 } |
|
1076 |
|
1077 const KURL& blankURL() |
|
1078 { |
|
1079 static KURL staticBlankURL(ParsedURLString, "about:blank"); |
|
1080 return staticBlankURL; |
|
1081 } |
|
1082 |
|
1083 bool protocolIs(const String& url, const char* protocol) |
|
1084 { |
|
1085 // Do the comparison without making a new string object. |
|
1086 assertProtocolIsGood(protocol); |
|
1087 |
|
1088 // Check the scheme like GURL does. |
|
1089 return url_util::FindAndCompareScheme(url.characters(), url.length(), |
|
1090 protocol, NULL); |
|
1091 } |
|
1092 |
|
1093 inline bool KURL::protocolIs(const String& string, const char* protocol) |
|
1094 { |
|
1095 return WebCore::protocolIs(string, protocol); |
|
1096 } |
|
1097 |
|
1098 bool protocolHostAndPortAreEqual(const KURL& a, const KURL& b) |
|
1099 { |
|
1100 if (a.parsed().scheme.end() != b.parsed().scheme.end()) |
|
1101 return false; |
|
1102 |
|
1103 int hostStartA = a.hostStart(); |
|
1104 int hostLengthA = a.hostEnd() - hostStartA; |
|
1105 int hostStartB = b.hostStart(); |
|
1106 int hostLengthB = b.hostEnd() - b.hostStart(); |
|
1107 if (hostLengthA != hostLengthB) |
|
1108 return false; |
|
1109 |
|
1110 // Check the scheme |
|
1111 for (int i = 0; i < a.parsed().scheme.end(); ++i) |
|
1112 if (a.string()[i] != b.string()[i]) |
|
1113 return false; |
|
1114 |
|
1115 // And the host |
|
1116 for (int i = 0; i < hostLengthA; ++i) |
|
1117 if (a.string()[hostStartA + i] != b.string()[hostStartB + i]) |
|
1118 return false; |
|
1119 |
|
1120 if (a.port() != b.port()) |
|
1121 return false; |
|
1122 |
|
1123 return true; |
|
1124 } |
|
1125 |
|
1126 } // namespace WebCore |
|
1127 |
|
1128 #endif // USE(GOOGLEURL) |