summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorerikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-25 14:14:39 +0000
committererikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-25 14:14:39 +0000
commitb57f0323eac5ecbbffd826abd92620901b55fd3a (patch)
treed907bb347a9aa2668213f27eee3eb257dbba41b3
parent543bcda1772cdd04e39fc8d912f61155ff570b9f (diff)
downloadchromium_src-b57f0323eac5ecbbffd826abd92620901b55fd3a.zip
chromium_src-b57f0323eac5ecbbffd826abd92620901b55fd3a.tar.gz
chromium_src-b57f0323eac5ecbbffd826abd92620901b55fd3a.tar.bz2
Enable developers to conditionally activate Chrome Frame, depending on the version of the host IE browser.
BUG=52601 TEST=[chrome_frame_unittests / UtilTests.XUaCompatibleDirectiveTest], [chrome_frame_tests / HeaderTest.*] Review URL: http://codereview.chromium.org/3978001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63728 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome_frame/chrome_frame.gyp1
-rw-r--r--chrome_frame/protocol_sink_wrap.cc14
-rw-r--r--chrome_frame/test/header_test.cc184
-rw-r--r--chrome_frame/test/util_unittests.cc59
-rw-r--r--chrome_frame/utils.cc95
-rw-r--r--chrome_frame/utils.h46
6 files changed, 366 insertions, 33 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp
index cde3108..9a99434 100644
--- a/chrome_frame/chrome_frame.gyp
+++ b/chrome_frame/chrome_frame.gyp
@@ -254,6 +254,7 @@
'test/chrome_frame_automation_mock.cc',
'test/chrome_frame_automation_mock.h',
'test/delete_chrome_history_test.cc',
+ 'test/header_test.cc',
'test/http_server.cc',
'test/http_server.h',
'test/ie_event_sink.cc',
diff --git a/chrome_frame/protocol_sink_wrap.cc b/chrome_frame/protocol_sink_wrap.cc
index 7e3a760..bb47a5d 100644
--- a/chrome_frame/protocol_sink_wrap.cc
+++ b/chrome_frame/protocol_sink_wrap.cc
@@ -312,12 +312,13 @@ RendererType DetermineRendererTypeFromMetaData(
}
if (info) {
- char buffer[32] = "x-ua-compatible";
+ char buffer[512] = "x-ua-compatible";
DWORD len = sizeof(buffer);
DWORD flags = 0;
HRESULT hr = info->QueryInfo(HTTP_QUERY_CUSTOM, buffer, &len, &flags, NULL);
+
if (hr == S_OK && len > 0) {
- if (StrStrIA(buffer, "chrome=1")) {
+ if (CheckXUaCompatibleDirective(buffer, GetIEMajorVersion())) {
return RENDERER_TYPE_CHROME_RESPONSE_HEADER;
}
}
@@ -345,9 +346,12 @@ RendererType DetermineRendererType(void* buffer, DWORD size, bool last_chance) {
// browsers may handle this properly, we don't and will stop scanning
// for the XUACompat content value if we encounter one.
std::wstring xua_compat_content;
- UtilGetXUACompatContentValue(html_contents, &xua_compat_content);
- if (StrStrI(xua_compat_content.c_str(), kChromeContentPrefix)) {
- renderer_type = RENDERER_TYPE_CHROME_HTTP_EQUIV;
+ if (SUCCEEDED(UtilGetXUACompatContentValue(html_contents,
+ &xua_compat_content))) {
+ if (CheckXUaCompatibleDirective(WideToASCII(xua_compat_content),
+ GetIEMajorVersion())) {
+ renderer_type = RENDERER_TYPE_CHROME_HTTP_EQUIV;
+ }
}
return renderer_type;
diff --git a/chrome_frame/test/header_test.cc b/chrome_frame/test/header_test.cc
new file mode 100644
index 0000000..1aedbd9
--- /dev/null
+++ b/chrome_frame/test/header_test.cc
@@ -0,0 +1,184 @@
+// Copyright (c) 2010 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 "chrome_frame/test/mock_ie_event_sink_actions.h"
+#include "chrome_frame/test/mock_ie_event_sink_test.h"
+#include "base/rand_util.h"
+
+namespace chrome_frame_test {
+
+class TestData {
+ public:
+ TestData(const std::string& value, bool in_header, LoadedInRenderer expected)
+ : value_(value),
+ in_header_(in_header),
+ expected_(expected),
+ name_(base::IntToString(base::RandInt(0, 1000))) {
+ }
+
+ LoadedInRenderer GetExpectedRenderer() const {
+ return expected_;
+ }
+
+ std::wstring GetPath() const {
+ return ASCIIToWide("/" + name_);
+ }
+
+ std::wstring GetUrl(MockWebServer* server_mock) const {
+ return server_mock->Resolve(ASCIIToWide(name_));
+ }
+
+ void ExpectOnServer(MockWebServer* server_mock) const {
+ EXPECT_CALL(*server_mock, Get(testing::_, GetPath(), testing::_))
+ .Times(testing::AnyNumber())
+ .WillRepeatedly(SendFast(GetHeaders(), GetHtml()));
+ }
+
+ std::string GetHeaders() const {
+ std::ostringstream headers;
+ headers << "HTTP/1.1 200 OK\r\n"
+ << "Connection: close\r\n"
+ << "Content-Type: text/html\r\n";
+ if (in_header_) {
+ headers << "X-UA-COMPATIBLE: " << value_ << "\r\n";
+ }
+ return headers.str();
+ }
+
+ std::string GetHtml() const {
+ std::ostringstream html;
+ html << "<html><head>";
+ if (!in_header_) {
+ html << "<meta http-equiv=\"x-ua-compatible\" content=\"" << value_
+ << "\" />";
+ }
+ html << "</head><body>This is some text.</body></html>";
+ return html.str();
+ }
+
+ private:
+ LoadedInRenderer expected_;
+ std::string name_;
+ std::string value_;
+ bool in_header_;
+};
+
+// Determines the major version of the installed IE
+// Returns -1 in case of failure, 0 if the version is newer than currently known
+int GetIEMajorVersion() {
+ switch (GetInstalledIEVersion()) {
+ case IE_6:
+ return 6;
+ case IE_7:
+ return 7;
+ case IE_8:
+ return 8;
+ case IE_9:
+ return 9;
+ case IE_INVALID:
+ case NON_IE:
+ case IE_UNSUPPORTED:
+ ADD_FAILURE() << "Failed to detect IE version.";
+ return -1;
+ default:
+ return 0;
+ }
+}
+
+int LowVersion() {
+ int ie_version = GetIEMajorVersion();
+ switch (ie_version) {
+ case -1:
+ case 0:
+ return 5;
+ default:
+ return ie_version - 1;
+ }
+}
+
+int HighVersion() {
+ int ie_version = GetIEMajorVersion();
+ switch (ie_version) {
+ case -1:
+ case 0:
+ return 1000;
+ default:
+ return ie_version + 1;
+ }
+}
+
+int EqualVersion() {
+ int ie_version = GetIEMajorVersion();
+ switch (ie_version) {
+ case -1:
+ case 0:
+ return 1000;
+ default:
+ return ie_version;
+ }
+}
+
+std::string HeaderValue(int ie_version) {
+ if (ie_version == -1) {
+ return "IE=8; Chrome=1";
+ } else {
+ return std::string("IE=8; Chrome=IE") + base::IntToString(ie_version);
+ }
+}
+
+std::string CorruptHeaderValue(int ie_version) {
+ return HeaderValue(ie_version) + ".0";
+}
+
+std::string LongHeaderValue(int ie_version) {
+ std::string long_value = HeaderValue(ie_version) + "; " +
+ std::string(256, 'a') + "=bbb";
+ DCHECK_GT(long_value.length(), 256u);
+ DCHECK_LT(long_value.length(), 512u);
+ return long_value;
+}
+
+class HeaderTest
+ : public MockIEEventSinkTest,
+ public testing::TestWithParam<TestData> {
+ public:
+ HeaderTest() {}
+};
+
+INSTANTIATE_TEST_CASE_P(MetaTag, HeaderTest, testing::Values(
+ TestData(HeaderValue(LowVersion()), false, IN_IE),
+ TestData(HeaderValue(EqualVersion()), false, IN_CF),
+ TestData(HeaderValue(HighVersion()), false, IN_CF),
+ TestData(LongHeaderValue(LowVersion()), false, IN_IE),
+ TestData(LongHeaderValue(EqualVersion()), false, IN_CF),
+ TestData(LongHeaderValue(HighVersion()), false, IN_CF)));
+INSTANTIATE_TEST_CASE_P(HttpHeader, HeaderTest, testing::Values(
+ TestData(HeaderValue(LowVersion()), true, IN_IE),
+ TestData(HeaderValue(EqualVersion()), true, IN_CF),
+ TestData(HeaderValue(HighVersion()), true, IN_CF),
+ TestData(LongHeaderValue(LowVersion()), true, IN_IE),
+ TestData(LongHeaderValue(EqualVersion()), true, IN_CF),
+ TestData(LongHeaderValue(HighVersion()), true, IN_CF)));
+INSTANTIATE_TEST_CASE_P(CorruptValueHeader, HeaderTest, testing::Values(
+ TestData(CorruptHeaderValue(LowVersion()), true, IN_IE),
+ TestData(CorruptHeaderValue(EqualVersion()), true, IN_IE),
+ TestData(CorruptHeaderValue(HighVersion()), true, IN_IE),
+ TestData(CorruptHeaderValue(LowVersion()), false, IN_IE),
+ TestData(CorruptHeaderValue(EqualVersion()), false, IN_IE),
+ TestData(CorruptHeaderValue(HighVersion()), false, IN_IE)));
+
+TEST_P(HeaderTest, HandleXUaCompatibleHeader) {
+ std::wstring url = GetParam().GetUrl(&server_mock_);
+ LoadedInRenderer expected_renderer = GetParam().GetExpectedRenderer();
+
+ GetParam().ExpectOnServer(&server_mock_);
+ ie_mock_.ExpectNavigation(expected_renderer, url);
+
+ EXPECT_CALL(ie_mock_, OnLoad(expected_renderer, testing::StrEq(url)))
+ .WillOnce(CloseBrowserMock(&ie_mock_));
+
+ LaunchIEAndNavigate(url);
+}
+
+} // namespace chrome_frame_test
diff --git a/chrome_frame/test/util_unittests.cc b/chrome_frame/test/util_unittests.cc
index bc18062..6f6be52 100644
--- a/chrome_frame/test/util_unittests.cc
+++ b/chrome_frame/test/util_unittests.cc
@@ -356,3 +356,62 @@ TEST(UtilTests, RendererTypeForUrlTest) {
config_key.WriteValue(kEnableGCFRendererByDefault, saved_default_renderer);
}
+TEST(UtilTests, XUaCompatibleDirectiveTest) {
+ int all_versions[] = {0, 1, 2, 5, 6, 7, 8, 9, 10, 11, 99, 100, 101, 1000};
+
+ struct Cases {
+ const char* header_value;
+ int max_version;
+ } test_cases[] = {
+ // Negative cases
+ { "", -1 },
+ { "chrome=", -1 },
+ { "chrome", -1 },
+ { "chrome=X", -1 },
+ { "chrome=IE", -1 },
+ { "chrome=IE-7", -1 },
+ { "chrome=IE+7", -1 },
+ { "chrome=IE 7", -1 },
+ { "chrome=IE7.0", -1 },
+ { "chrome=FF7", -1 },
+ { "chrome=IE7+", -1 },
+ { "chrome=IE99999999999999999999", -1 },
+ { "chrome=IE0", -1 },
+ // Always on
+ { "chrome=1", INT_MAX },
+ // Basic positive cases
+ { "chrome=IE1", 1 },
+ { "CHROME=IE6", 6 },
+ { "Chrome=IE10", 10 },
+ { "ChRoMe=IE100", 100 },
+ // Positive formatting variations
+ { " chrome=IE6 ", 6 },
+ { " chrome=IE6; ", 6 },
+ { " chrome=IE6; IE=8 ", 6 },
+ { " IE=8;chrome=IE6;", 6 },
+ { " IE=8;chrome=IE6;", 6 },
+ { " IE=8 ; chrome = IE6 ;", 6 },
+ // Ignore unrecognized values
+ { " IE=8 ; chrome = IE7.1; chrome = IE6;", 6 },
+ // First valid wins
+ { " IE=8 ; chrome = IE6; chrome = IE8;", 6 }
+ };
+
+ for (int case_index = 0; case_index < arraysize(test_cases); ++case_index) {
+ const Cases& test = test_cases[case_index];
+
+ // Check that all versions <= max_version are matched
+ for (size_t version_index = 0;
+ version_index < arraysize(all_versions);
+ ++version_index) {
+ bool expect_match = (all_versions[version_index] <= test.max_version);
+
+ ASSERT_EQ(expect_match,
+ CheckXUaCompatibleDirective(test.header_value,
+ all_versions[version_index]))
+ << "Expect '" << test.header_value << "' to "
+ << (expect_match ? "match" : "not match") << " IE major version "
+ << all_versions[version_index];
+ }
+ }
+}
diff --git a/chrome_frame/utils.cc b/chrome_frame/utils.cc
index a959adf..2e5f13e 100644
--- a/chrome_frame/utils.cc
+++ b/chrome_frame/utils.cc
@@ -7,13 +7,13 @@
#include <htiframe.h>
#include <mshtml.h>
#include <shlobj.h>
-#include <wininet.h>
#include <atlsecurity.h>
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/lazy_instance.h"
+#include "base/lock.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/string_number_conversions.h"
@@ -411,40 +411,54 @@ BrowserType GetBrowserType() {
return browser_type;
}
-IEVersion GetIEVersion() {
- static IEVersion ie_version = IE_INVALID;
+uint32 GetIEMajorVersion() {
+ static uint32 ie_major_version = UINT_MAX;
- if (ie_version == IE_INVALID) {
+ if (ie_major_version == UINT_MAX) {
wchar_t exe_path[MAX_PATH];
HMODULE mod = GetModuleHandle(NULL);
GetModuleFileName(mod, exe_path, arraysize(exe_path) - 1);
std::wstring exe_name(file_util::GetFilenameFromPath(exe_path));
if (!LowerCaseEqualsASCII(exe_name, kIEImageName)) {
- ie_version = NON_IE;
+ ie_major_version = 0;
} else {
uint32 high = 0;
uint32 low = 0;
if (GetModuleVersion(mod, &high, &low)) {
- switch (HIWORD(high)) {
- case 6:
- ie_version = IE_6;
- break;
- case 7:
- ie_version = IE_7;
- break;
- case 8:
- ie_version = IE_8;
- break;
- default:
- ie_version = HIWORD(high) >= 9 ? IE_9 : IE_UNSUPPORTED;
- break;
- }
+ ie_major_version = HIWORD(high);
} else {
- NOTREACHED() << "Can't get IE version";
+ ie_major_version = 0;
}
}
}
+ return ie_major_version;
+}
+
+IEVersion GetIEVersion() {
+ static IEVersion ie_version = IE_INVALID;
+
+ if (ie_version == IE_INVALID) {
+ uint32 major_version = GetIEMajorVersion();
+ switch (major_version) {
+ case 0:
+ ie_version = NON_IE;
+ break;
+ case 6:
+ ie_version = IE_6;
+ break;
+ case 7:
+ ie_version = IE_7;
+ break;
+ case 8:
+ ie_version = IE_8;
+ break;
+ default:
+ ie_version = major_version >= 9 ? IE_9 : IE_UNSUPPORTED;
+ break;
+ }
+ }
+
return ie_version;
}
@@ -1519,6 +1533,46 @@ void WaitWithMessageLoop(HANDLE* handles, int count, DWORD timeout) {
}
}
+bool CheckXUaCompatibleDirective(const std::string& directive,
+ int ie_major_version) {
+ net::HttpUtil::NameValuePairsIterator name_value_pairs(directive.begin(),
+ directive.end(),
+ ';');
+
+ // Loop through the values until a valid 'Chrome=<FILTER>' entry is found
+ while (name_value_pairs.GetNext()) {
+ if (!LowerCaseEqualsASCII(name_value_pairs.name_begin(),
+ name_value_pairs.name_end(),
+ "chrome")) {
+ continue;
+ }
+ std::string::const_iterator filter_begin = name_value_pairs.value_begin();
+ std::string::const_iterator filter_end = name_value_pairs.value_end();
+
+ size_t filter_length = filter_end - filter_begin;
+
+ if (filter_length == 1 && *filter_begin == '1') {
+ return true;
+ }
+
+ if (filter_length < 3 ||
+ !LowerCaseEqualsASCII(filter_begin, filter_begin + 2, "ie") ||
+ !isdigit(*(filter_begin + 2))) { // ensure no leading +/-
+ continue;
+ }
+
+ int header_ie_version = 0;
+ if (!base::StringToInt(filter_begin + 2, filter_end, &header_ie_version) ||
+ header_ie_version == 0) { // ensure it's not a sequence of 0's
+ continue;
+ }
+
+ // The first valid directive we find wins, whether it matches or not
+ return ie_major_version <= header_ie_version;
+ }
+ return false;
+}
+
void EnumerateKeyValues(HKEY parent_key, const wchar_t* sub_key_name,
std::vector<std::wstring>* values) {
DCHECK(values);
@@ -1528,4 +1582,3 @@ void EnumerateKeyValues(HKEY parent_key, const wchar_t* sub_key_name,
++url_list;
}
}
-
diff --git a/chrome_frame/utils.h b/chrome_frame/utils.h
index 4849a803..678b86c 100644
--- a/chrome_frame/utils.h
+++ b/chrome_frame/utils.h
@@ -5,12 +5,12 @@
#ifndef CHROME_FRAME_UTILS_H_
#define CHROME_FRAME_UTILS_H_
-#include <shdeprecated.h>
-#include <urlmon.h>
+#include <OAidl.h>
+#include <windows.h>
#include <wininet.h>
-#include <atlbase.h>
#include <string>
+#include <vector>
#include "base/basictypes.h"
#include "base/lock.h"
@@ -21,6 +21,7 @@
#include "googleurl/src/gurl.h"
class FilePath;
+interface IBrowserService;
// utils.h : Various utility functions and classes
@@ -213,18 +214,25 @@ bool IsChrome(RendererType renderer_type);
// To get the IE version when Chrome Frame is hosted in IE. Make sure that
// the hosting browser is IE before calling this function, otherwise NON_IE
// will be returned.
+//
+// Versions newer than the newest supported version are reported as the newest
+// supported version.
IEVersion GetIEVersion();
+// Returns the actual major version of the IE in which the current process is
+// hosted. Returns 0 if the current process is not IE or any other error occurs.
+uint32 GetIEMajorVersion();
+
FilePath GetIETemporaryFilesFolder();
// Retrieves the file version from a module handle without extra round trips
// to the disk (as happens with the regular GetFileVersionInfo API).
//
// @param module A handle to the module for which to retrieve the version info.
-// @param high On successful return holds the most significant part of the
-// file version. Must be non-null.
-// @param low On successful return holds the least significant part of the
-// file version. May be NULL.
+// @param high On successful return holds the most significant part of the file
+// version. Must be non-null.
+// @param low On successful return holds the least significant part of the file
+// version. May be NULL.
// @returns true if the version info was successfully retrieved.
bool GetModuleVersion(HMODULE module, uint32* high, uint32* low);
@@ -596,4 +604,28 @@ void WaitWithMessageLoop(HANDLE* handles, int count, DWORD timeout);
void EnumerateKeyValues(HKEY parent_key, const wchar_t* sub_key_name,
std::vector<std::wstring>* values);
+// Interprets the value of an X-UA-Compatible header (or <meta> tag equivalent)
+// and indicates whether the header value contains a Chrome Frame directive
+// matching a given host browser version.
+//
+// The header is a series of name-value pairs, with the names being HTTP tokens
+// and the values being either tokens or quoted-strings. Names and values are
+// joined by '=' and pairs are delimited by ';'. LWS may be used liberally
+// before and between names, values, '=', and ';'. See RFC 2616 for definitions
+// of token, quoted-string, and LWS. See Microsoft's documentation of the
+// X-UA-COMPATIBLE header here:
+// http://msdn.microsoft.com/en-us/library/cc288325(VS.85).aspx
+//
+// At most one 'Chrome=<FILTER>' entry is expected in the header value. The
+// first valid instance is used. The value of "<FILTER>" (possibly after
+// unquoting) is interpreted as follows:
+//
+// "1" - Always active
+// "IE7" - Active for IE major version 7 or lower
+//
+// For example:
+// X-UA-Compatible: IE=8; Chrome=IE6
+bool CheckXUaCompatibleDirective(const std::string& directive,
+ int ie_major_version);
+
#endif // CHROME_FRAME_UTILS_H_