diff --git a/cef.gyp b/cef.gyp index 0aa053869..1909488aa 100644 --- a/cef.gyp +++ b/cef.gyp @@ -183,6 +183,7 @@ 'tests/unittests/string_unittest.cc', 'tests/unittests/test_handler.h', 'tests/unittests/test_suite.h', + 'tests/unittests/url_unittest.cc', 'tests/unittests/v8_unittest.cc', 'tests/unittests/xml_reader_unittest.cc', 'tests/unittests/zip_reader_unittest.cc', diff --git a/include/cef.h b/include/cef.h index 8d1f2af21..68df98fcd 100644 --- a/include/cef.h +++ b/include/cef.h @@ -59,6 +59,7 @@ class CefSettings; class CefStreamReader; class CefStreamWriter; class CefTask; +class CefURLParts; class CefV8Handler; class CefV8Value; @@ -179,6 +180,18 @@ bool CefPostTask(CefThreadId threadId, CefRefPtr task); bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr task, long delay_ms); +// Parse the specified |url| into its component parts. +// Returns false if the URL is empty or invalid. +/*--cef()--*/ +bool CefParseURL(const CefString& url, + CefURLParts& parts); + +// Creates a URL from the specified |parts|, which must contain a non-empty +// spec or a non-empty host and path (at a minimum), but not both. +// Returns false if |parts| isn't initialized as described. +/*--cef()--*/ +bool CefCreateURL(const CefURLParts& parts, + CefString& url); // Interface defining the the reference count implementation methods. All // framework classes must implement the CefBase class. @@ -1886,4 +1899,77 @@ protected: } }; +// Class used to represent a URL's component parts. +class CefURLParts : public cef_urlparts_t +{ +public: + CefURLParts() + { + Init(); + } + virtual ~CefURLParts() + { + Reset(); + } + + CefURLParts(const CefURLParts& r) + { + Init(); + *this = r; + } + CefURLParts(const cef_urlparts_t& r) + { + Init(); + *this = r; + } + + void Reset() + { + cef_string_clear(&spec); + cef_string_clear(&scheme); + cef_string_clear(&username); + cef_string_clear(&password); + cef_string_clear(&host); + cef_string_clear(&port); + cef_string_clear(&path); + cef_string_clear(&query); + Init(); + } + + void Attach(const cef_urlparts_t& r) + { + Reset(); + *static_cast(this) = r; + } + + void Detach() + { + Init(); + } + + CefURLParts& operator=(const CefURLParts& r) + { + return operator=(static_cast(r)); + } + + CefURLParts& operator=(const cef_urlparts_t& r) + { + cef_string_copy(r.spec.str, r.spec.length, &spec); + cef_string_copy(r.scheme.str, r.scheme.length, &scheme); + cef_string_copy(r.username.str, r.username.length, &username); + cef_string_copy(r.password.str, r.password.length, &password); + cef_string_copy(r.host.str, r.host.length, &host); + cef_string_copy(r.port.str, r.port.length, &port); + cef_string_copy(r.path.str, r.path.length, &path); + cef_string_copy(r.query.str, r.query.length, &query); + return *this; + } + +protected: + void Init() + { + memset(static_cast(this), 0, sizeof(cef_urlparts_t)); + } +}; + #endif // _CEF_H diff --git a/include/cef_capi.h b/include/cef_capi.h index 652a8bbc7..6ccb65ff7 100644 --- a/include/cef_capi.h +++ b/include/cef_capi.h @@ -152,6 +152,17 @@ CEF_EXPORT int cef_post_task(cef_thread_id_t threadId, CEF_EXPORT int cef_post_delayed_task(cef_thread_id_t threadId, struct _cef_task_t* task, long delay_ms); +// Parse the specified |url| into its component parts. Returns false (0) if the +// URL is NULL or invalid. +CEF_EXPORT int cef_parse_url(const cef_string_t* url, + struct _cef_urlparts_t* parts); + +// Creates a URL from the specified |parts|, which must contain a non-NULL spec +// or a non-NULL host and path (at a minimum), but not both. Returns false (0) +// if |parts| isn't initialized as described. +CEF_EXPORT int cef_create_url(const struct _cef_urlparts_t* parts, + cef_string_t* url); + typedef struct _cef_base_t { // Size of the data structure. diff --git a/include/cef_types.h b/include/cef_types.h index c6d9895a6..82d8ed696 100644 --- a/include/cef_types.h +++ b/include/cef_types.h @@ -238,6 +238,35 @@ typedef struct _cef_browser_settings_t bool developer_tools_disabled; } cef_browser_settings_t; +// URL component parts. +typedef struct _cef_urlparts_t +{ + // The complete URL specification. + cef_string_t spec; + + // Scheme component not including the colon (e.g., "http"). + cef_string_t scheme; + + // User name component. + cef_string_t username; + + // Password component. + cef_string_t password; + + // Host component. This may be a hostname, an IPv4 address or an IPv6 literal + // surrounded by square brackets (e.g., "[2001:db8::1]"). + cef_string_t host; + + // Port number component. + cef_string_t port; + + // Path component including the first slash following the host. + cef_string_t path; + + // Query string component (i.e., everything following the '?'). + cef_string_t query; +} cef_urlparts_t; + // Define handler return value types. Returning RV_HANDLED indicates // that the implementation completely handled the method and that no further // processing is required. Returning RV_CONTINUE indicates that the diff --git a/libcef/cef_context.cc b/libcef/cef_context.cc index 9f9e693b2..532534402 100644 --- a/libcef/cef_context.cc +++ b/libcef/cef_context.cc @@ -187,6 +187,69 @@ bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr task, new CefTaskHelper(task, threadId), delay_ms); } +bool CefParseURL(const CefString& url, + CefURLParts& parts) +{ + GURL gurl(url.ToString()); + if (!gurl.is_valid()) + return false; + + CefString(&parts.spec).FromString(gurl.spec()); + CefString(&parts.scheme).FromString(gurl.scheme()); + CefString(&parts.username).FromString(gurl.username()); + CefString(&parts.password).FromString(gurl.password()); + CefString(&parts.host).FromString(gurl.host()); + CefString(&parts.port).FromString(gurl.port()); + CefString(&parts.path).FromString(gurl.path()); + CefString(&parts.query).FromString(gurl.query()); + + return true; +} + +bool CefCreateURL(const CefURLParts& parts, + CefString& url) +{ + std::string spec = CefString(parts.spec.str, parts.spec.length, false); + std::string scheme = CefString(parts.scheme.str, parts.scheme.length, false); + std::string username = + CefString(parts.username.str, parts.username.length, false); + std::string password = + CefString(parts.password.str, parts.password.length, false); + std::string host = CefString(parts.host.str, parts.host.length, false); + std::string port = CefString(parts.port.str, parts.port.length, false); + std::string path = CefString(parts.path.str, parts.path.length, false); + std::string query = CefString(parts.query.str, parts.query.length, false); + + GURL gurl; + if (!spec.empty()) { + gurl = GURL(spec); + } else if (!scheme.empty() && !host.empty()) { + std::stringstream ss; + ss << scheme << "://"; + if (!username.empty()) { + ss << username; + if (!password.empty()) + ss << ":" << password; + ss << "@"; + } + ss << host; + if (!port.empty()) + ss << ":" << port; + if (!path.empty()) + ss << path; + if (!query.empty()) + ss << "?" << query; + gurl = GURL(ss.str()); + } + + if (gurl.is_valid()) { + url = gurl.spec(); + return true; + } + + return false; +} + // CefContext diff --git a/libcef_dll/libcef_dll.cc b/libcef_dll/libcef_dll.cc index 4a4c552c0..1c4e705a9 100644 --- a/libcef_dll/libcef_dll.cc +++ b/libcef_dll/libcef_dll.cc @@ -186,3 +186,57 @@ CEF_EXPORT int cef_post_delayed_task(cef_thread_id_t threadId, return CefPostDelayedTask(threadId, CefTaskCToCpp::Wrap(task), delay_ms); } + +CEF_EXPORT int cef_parse_url(const cef_string_t* url, + struct _cef_urlparts_t* parts) +{ + DCHECK(url && parts); + if(!url || !parts) + return 0; + + CefURLParts urlParts; + bool ret = CefParseURL(CefString(url), urlParts); + + // Clear the current structure values, if any. + cef_string_clear(&parts->spec); + cef_string_clear(&parts->scheme); + cef_string_clear(&parts->username); + cef_string_clear(&parts->password); + cef_string_clear(&parts->host); + cef_string_clear(&parts->port); + cef_string_clear(&parts->path); + cef_string_clear(&parts->query); + + // Transfer ownership of the values from |urlParts| to the structure. + memcpy(parts, static_cast(&urlParts), + sizeof(cef_urlparts_t)); + urlParts.Detach(); + + return ret; +} + +CEF_EXPORT int cef_create_url(const struct _cef_urlparts_t* parts, + cef_string_t* url) +{ + DCHECK(parts && url); + if(!parts || !url) + return 0; + + CefURLParts urlParts; + + // Reference the existing structure values without copying. + cef_string_set(parts->spec.str, parts->spec.length, &urlParts.spec, false); + cef_string_set(parts->scheme.str, parts->scheme.length, &urlParts.scheme, + false); + cef_string_set(parts->username.str, parts->username.length, + &urlParts.username, false); + cef_string_set(parts->password.str, parts->password.length, + &urlParts.password, false); + cef_string_set(parts->host.str, parts->host.length, &urlParts.host, false); + cef_string_set(parts->port.str, parts->port.length, &urlParts.port, false); + cef_string_set(parts->path.str, parts->path.length, &urlParts.path, false); + cef_string_set(parts->query.str, parts->query.length, &urlParts.query, false); + + CefString urlStr(url); + return CefCreateURL(urlParts, urlStr); +} diff --git a/libcef_dll/wrapper/libcef_dll_wrapper.cc b/libcef_dll/wrapper/libcef_dll_wrapper.cc index 379024d1a..97a3d1f9b 100644 --- a/libcef_dll/wrapper/libcef_dll_wrapper.cc +++ b/libcef_dll/wrapper/libcef_dll_wrapper.cc @@ -159,3 +159,15 @@ bool CefPostDelayedTask(CefThreadId threadId, CefRefPtr task, return cef_post_delayed_task(threadId, CefTaskCppToC::Wrap(task), delay_ms)? true:false; } + +bool CefParseURL(const CefString& url, + CefURLParts& parts) +{ + return cef_parse_url(url.GetStruct(), &parts) ? true : false; +} + +bool CefCreateURL(const CefURLParts& parts, + CefString& url) +{ + return cef_create_url(&parts, url.GetWritableStruct()) ? true : false; +} diff --git a/tests/unittests/url_unittest.cc b/tests/unittests/url_unittest.cc new file mode 100644 index 000000000..8c35e34fb --- /dev/null +++ b/tests/unittests/url_unittest.cc @@ -0,0 +1,185 @@ +// Copyright (c) 2011 The Chromium Embedded Framework 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 "include/cef.h" +#include "testing/gtest/include/gtest/gtest.h" + +TEST(URLTest, CreateURL) +{ + // Create the URL using the spec. + { + CefURLParts parts; + CefString url; + CefString(&parts.spec).FromASCII( + "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2"); + ASSERT_TRUE(CefCreateURL(parts, url)); + ASSERT_EQ(url, + "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2"); + } + + // Test that scheme and host are required. + { + CefURLParts parts; + CefString url; + CefString(&parts.scheme).FromASCII("http"); + ASSERT_FALSE(CefCreateURL(parts, url)); + } + { + CefURLParts parts; + CefString url; + CefString(&parts.host).FromASCII("www.example.com"); + ASSERT_FALSE(CefCreateURL(parts, url)); + } + + // Create the URL using scheme and host. + { + CefURLParts parts; + CefString url; + CefString(&parts.scheme).FromASCII("http"); + CefString(&parts.host).FromASCII("www.example.com"); + ASSERT_TRUE(CefCreateURL(parts, url)); + ASSERT_EQ(url, "http://www.example.com/"); + } + + // Create the URL using scheme, host and path. + { + CefURLParts parts; + CefString url; + CefString(&parts.scheme).FromASCII("http"); + CefString(&parts.host).FromASCII("www.example.com"); + CefString(&parts.path).FromASCII("/path/to.html"); + ASSERT_TRUE(CefCreateURL(parts, url)); + ASSERT_EQ(url, "http://www.example.com/path/to.html"); + } + + // Create the URL using scheme, host, path and query. + { + CefURLParts parts; + CefString url; + CefString(&parts.scheme).FromASCII("http"); + CefString(&parts.host).FromASCII("www.example.com"); + CefString(&parts.path).FromASCII("/path/to.html"); + CefString(&parts.query).FromASCII("foo=test&bar=test2"); + ASSERT_TRUE(CefCreateURL(parts, url)); + ASSERT_EQ(url, "http://www.example.com/path/to.html?foo=test&bar=test2"); + } + + // Create the URL using all the various components. + { + CefURLParts parts; + CefString url; + CefString(&parts.scheme).FromASCII("http"); + CefString(&parts.username).FromASCII("user"); + CefString(&parts.password).FromASCII("pass"); + CefString(&parts.host).FromASCII("www.example.com"); + CefString(&parts.port).FromASCII("88"); + CefString(&parts.path).FromASCII("/path/to.html"); + CefString(&parts.query).FromASCII("foo=test&bar=test2"); + ASSERT_TRUE(CefCreateURL(parts, url)); + ASSERT_EQ(url, + "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2"); + } +} + +TEST(URLTest, ParseURL) +{ + // Parse the URL using scheme and host. + { + CefURLParts parts; + CefString url; + url.FromASCII("http://www.example.com"); + ASSERT_TRUE(CefParseURL(url, parts)); + + CefString spec(&parts.spec); + ASSERT_EQ(spec, "http://www.example.com/"); + ASSERT_EQ(parts.username.length, 0); + ASSERT_EQ(parts.password.length, 0); + CefString scheme(&parts.scheme); + ASSERT_EQ(scheme, "http"); + CefString host(&parts.host); + ASSERT_EQ(host, "www.example.com"); + ASSERT_EQ(parts.port.length, 0); + CefString path(&parts.path); + ASSERT_EQ(path, "/"); + ASSERT_EQ(parts.query.length, 0); + } + + // Parse the URL using scheme, host and path. + { + CefURLParts parts; + CefString url; + url.FromASCII("http://www.example.com/path/to.html"); + ASSERT_TRUE(CefParseURL(url, parts)); + + CefString spec(&parts.spec); + ASSERT_EQ(spec, "http://www.example.com/path/to.html"); + ASSERT_EQ(parts.username.length, 0); + ASSERT_EQ(parts.password.length, 0); + CefString scheme(&parts.scheme); + ASSERT_EQ(scheme, "http"); + CefString host(&parts.host); + ASSERT_EQ(host, "www.example.com"); + ASSERT_EQ(parts.port.length, 0); + CefString path(&parts.path); + ASSERT_EQ(path, "/path/to.html"); + ASSERT_EQ(parts.query.length, 0); + } + + // Parse the URL using scheme, host, path and query. + { + CefURLParts parts; + CefString url; + url.FromASCII("http://www.example.com/path/to.html?foo=test&bar=test2"); + ASSERT_TRUE(CefParseURL(url, parts)); + + CefString spec(&parts.spec); + ASSERT_EQ(spec, "http://www.example.com/path/to.html?foo=test&bar=test2"); + ASSERT_EQ(parts.username.length, 0); + ASSERT_EQ(parts.password.length, 0); + CefString scheme(&parts.scheme); + ASSERT_EQ(scheme, "http"); + CefString host(&parts.host); + ASSERT_EQ(host, "www.example.com"); + ASSERT_EQ(parts.port.length, 0); + CefString path(&parts.path); + ASSERT_EQ(path, "/path/to.html"); + CefString query(&parts.query); + ASSERT_EQ(query, "foo=test&bar=test2"); + } + + // Parse the URL using all the various components. + { + CefURLParts parts; + CefString url; + url.FromASCII( + "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2"); + ASSERT_TRUE(CefParseURL(url, parts)); + + CefString spec(&parts.spec); + ASSERT_EQ(spec, + "http://user:pass@www.example.com:88/path/to.html?foo=test&bar=test2"); + CefString scheme(&parts.scheme); + ASSERT_EQ(scheme, "http"); + CefString username(&parts.username); + ASSERT_EQ(username, "user"); + CefString password(&parts.password); + ASSERT_EQ(password, "pass"); + CefString host(&parts.host); + ASSERT_EQ(host, "www.example.com"); + CefString port(&parts.port); + ASSERT_EQ(port, "88"); + CefString path(&parts.path); + ASSERT_EQ(path, "/path/to.html"); + CefString query(&parts.query); + ASSERT_EQ(query, "foo=test&bar=test2"); + } + + // Parse an invalid URL. + { + CefURLParts parts; + CefString url; + url.FromASCII("www.example.com"); + ASSERT_FALSE(CefParseURL(url, parts)); + } +} diff --git a/tools/cef_parser.py b/tools/cef_parser.py index c36534ad4..ff38a653f 100644 --- a/tools/cef_parser.py +++ b/tools/cef_parser.py @@ -1060,6 +1060,7 @@ class obj_analysis: 'CefPopupFeatures' : 'cef_popup_features_t', 'CefSettings' : 'cef_settings_t', 'CefBrowserSettings' : 'cef_browser_settings_t', + 'CefURLParts' : 'cef_urlparts_t', } if value in structuretypes.keys(): return {