summaryrefslogtreecommitdiffstats
path: root/ceee/ie/plugin/bho/mediumtest_browser_event.cc
diff options
context:
space:
mode:
authorinitial.commit@chromium.org <initial.commit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-02 02:14:31 +0000
committerinitial.commit@chromium.org <initial.commit@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-11-02 02:14:31 +0000
commit5a7bdf208c28c210b39cff63d1cf91302b02821b (patch)
tree5ff484561562f78b29d2670168a9e3b40b48dcab /ceee/ie/plugin/bho/mediumtest_browser_event.cc
parent26a54a5e0f4603c8fda2fe51789d331125993c9a (diff)
downloadchromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.zip
chromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.tar.gz
chromium_src-5a7bdf208c28c210b39cff63d1cf91302b02821b.tar.bz2
Checking in the initial version of CEEE (Chrome Extensions Execution
Environment), an optional feature of Chrome Frame that acts as an adapter layer for a subset of the Chrome Extension APIs. This enables extensions that stick to the supported subset of APIs to work in the context of Chrome Frame with minimal or sometimes no changes. See http://www.chromium.org/developers/design-documents/ceee for an overview of the design of CEEE. TEST=unit tests (run ceee/smoke_tests.bat as an administrator on Windows) BUG=none git-svn-id: svn://svn.chromium.org/chrome/trunk/src@64712 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ceee/ie/plugin/bho/mediumtest_browser_event.cc')
-rw-r--r--ceee/ie/plugin/bho/mediumtest_browser_event.cc495
1 files changed, 495 insertions, 0 deletions
diff --git a/ceee/ie/plugin/bho/mediumtest_browser_event.cc b/ceee/ie/plugin/bho/mediumtest_browser_event.cc
new file mode 100644
index 0000000..3288e9d
--- /dev/null
+++ b/ceee/ie/plugin/bho/mediumtest_browser_event.cc
@@ -0,0 +1,495 @@
+// 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.
+//
+// A test that hosts and excercises the webbrowser control to test
+// its event firing behavior.
+#include <atlcrack.h>
+#include <atlsync.h>
+#include <atlwin.h>
+#include <set>
+
+#include "base/logging.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "base/base_paths_win.h"
+#include "ceee/ie/testing/mediumtest_ie_common.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+#include "ceee/common/com_utils.h"
+#include "ceee/common/initializing_coclass.h"
+#include "ceee/testing/utils/mock_com.h"
+#include "ceee/testing/utils/test_utils.h"
+#include "ceee/testing/utils/instance_count_mixin.h"
+
+
+namespace {
+
+using testing::InstanceCountMixin;
+using testing::InstanceCountMixinBase;
+
+using testing::BrowserEventSinkBase;
+using testing::GetTestUrl;
+using testing::GetTempPath;
+using testing::ShellBrowserTestImpl;
+
+// {AFF1D082-6B03-4b29-9521-E52240F6333B}
+const GUID IID_Dummy =
+ { 0xaff1d082, 0x6b03, 0x4b29,
+ { 0x95, 0x21, 0xe5, 0x22, 0x40, 0xf6, 0x33, 0x3b } };
+
+class TestBrowserEventSink;
+
+class TestFrameEventHandler
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public InitializingCoClass<TestFrameEventHandler>,
+ public InstanceCountMixin<TestFrameEventHandler>,
+ public IPropertyNotifySink,
+ public IAdviseSink {
+ public:
+ BEGIN_COM_MAP(TestFrameEventHandler)
+ COM_INTERFACE_ENTRY(IPropertyNotifySink)
+ COM_INTERFACE_ENTRY(IAdviseSink)
+ END_COM_MAP()
+
+ DECLARE_PROTECT_FINAL_CONSTRUCT();
+
+ TestFrameEventHandler() : event_sink_(NULL),
+ document_property_notify_sink_cookie_(-1),
+ advise_sink_cookie_(-1) {
+ }
+
+ virtual ~TestFrameEventHandler() {
+ ATLTRACE("~TestFrameEventHandler[%ws]\n", url_ ? url_ : L"");
+ }
+
+ void DetachFromSink();
+
+ STDMETHOD(OnChanged)(DISPID changed_property);
+ STDMETHOD(OnRequestEdit)(DISPID changed_property) {
+ ATLTRACE("OnRequestEdit(%d)\n", changed_property);
+ return S_OK;
+ }
+
+ // IAdviseSink
+ STDMETHOD_(void, OnDataChange)(FORMATETC *pFormatetc, STGMEDIUM *pStgmed) {
+ ATLTRACE("%s\n", __FUNCTION__);
+ }
+ STDMETHOD_(void, OnViewChange)(DWORD dwAspect, LONG lindex) {
+ ATLTRACE("%s\n", __FUNCTION__);
+ }
+ STDMETHOD_(void, OnRename)(IMoniker *pmk) {
+ ATLTRACE("%s\n", __FUNCTION__);
+ }
+ STDMETHOD_(void, OnSave)() {
+ ATLTRACE("%s\n", __FUNCTION__);
+ }
+ STDMETHOD_(void, OnClose)();
+
+ virtual void GetDescription(std::string* description) const {
+ description->clear();
+ description->append("TestFrameEventHandler");
+ // TODO(siggi@chromium.org): append URL
+ }
+
+ HRESULT Initialize(TestBrowserEventSink* event_sink,
+ IWebBrowser2* browser,
+ BSTR url);
+ void FinalRelease();
+
+ void set_url(const wchar_t* url) { url_ = url; }
+ const wchar_t* url() const { return url_ ? url_ : L""; }
+
+ template <class Interface>
+ HRESULT GetBrowser(Interface** browser) {
+ return browser_.QueryInterface(browser);
+ }
+
+ private:
+ CComBSTR url_;
+ CComDispatchDriver document_;
+ CComPtr<IWebBrowser2> browser_;
+ TestBrowserEventSink* event_sink_;
+
+ DWORD advise_sink_cookie_;
+ DWORD document_property_notify_sink_cookie_;
+};
+
+class TestBrowserEventSink
+ : public BrowserEventSinkBase,
+ public InitializingCoClass<TestBrowserEventSink> {
+ public:
+ // Disambiguate.
+ using InitializingCoClass<TestBrowserEventSink>::CreateInitialized;
+
+ HRESULT Initialize(TestBrowserEventSink** self, IWebBrowser2* browser) {
+ *self = this;
+ return BrowserEventSinkBase::Initialize(browser);
+ }
+
+ // We have seen cases where we get destroyed while the ATL::CAxHostWindow
+ // may still hold references to frame handlers.
+ void FinalRelease() {
+ FrameHandlerMap::iterator it(frame_handlers_.begin());
+ FrameHandlerMap::iterator end(frame_handlers_.end());
+
+ // Since they will detach from us as we clear them, we must not do it
+ // while we loop.
+ std::vector<TestFrameEventHandler*> to_be_detached;
+ for (; it != end; ++it) {
+ to_be_detached.push_back(it->second);
+ }
+ for (size_t index = 0; index < to_be_detached.size(); ++index) {
+ to_be_detached[index]->DetachFromSink();
+ }
+ ASSERT_EQ(0, frame_handlers_.size());
+ }
+
+ virtual void GetDescription(std::string* description) const {
+ description->clear();
+ description->append("TestBrowserEventSink");
+ }
+
+ void AttachHandler(IWebBrowser2* browser, TestFrameEventHandler* handler) {
+ ASSERT_TRUE(browser != NULL && handler != NULL);
+ CComPtr<IUnknown> browser_unk;
+ ASSERT_HRESULT_SUCCEEDED(browser->QueryInterface(&browser_unk));
+
+ // We shouldn't already have one.
+ ASSERT_TRUE(NULL == FindHandlerForBrowser(browser));
+
+ frame_handlers_.insert(std::make_pair(browser_unk, handler));
+ }
+
+ void DetachHandler(IWebBrowser2* browser, TestFrameEventHandler* handler) {
+ ASSERT_TRUE(browser != NULL && handler != NULL);
+
+ // It should already be registered.
+ ASSERT_TRUE(NULL != FindHandlerForBrowser(browser));
+
+ CComPtr<IUnknown> browser_unk;
+ ASSERT_HRESULT_SUCCEEDED(browser->QueryInterface(&browser_unk));
+ ASSERT_EQ(1, frame_handlers_.erase(browser_unk));
+ }
+
+ TestFrameEventHandler* FindHandlerForBrowser(IDispatch* browser) {
+ CComPtr<IUnknown> browser_unk;
+ EXPECT_HRESULT_SUCCEEDED(browser->QueryInterface(&browser_unk));
+
+ FrameHandlerMap::iterator it(frame_handlers_.find(browser_unk));
+ if (it == frame_handlers_.end())
+ return NULL;
+
+ return it->second;
+ }
+
+ TestFrameEventHandler* FindHandlerForUrl(const std::wstring& url) {
+ FrameHandlerMap::iterator it(frame_handlers_.begin());
+ FrameHandlerMap::iterator end(frame_handlers_.end());
+
+ for (; it != end; ++it) {
+ CComQIPtr<IWebBrowser2> browser(it->first);
+ EXPECT_TRUE(browser != NULL);
+ CComBSTR location_url;
+ EXPECT_HRESULT_SUCCEEDED(browser->get_LocationURL(&location_url));
+
+ if (0 == ::UrlCompare(url.c_str(), location_url, TRUE))
+ return it->second;
+ }
+
+ return NULL;
+ }
+
+ // Override.
+ STDMETHOD_(void, OnNavigateComplete)(IDispatch* browser_disp,
+ VARIANT* url_var) {
+ CComBSTR url;
+ if (V_VT(url_var) == VT_BSTR)
+ url = V_BSTR(url_var);
+
+ TestFrameEventHandler* frame_handler = FindHandlerForBrowser(browser_disp);
+ if (!frame_handler) {
+ CComQIPtr<IWebBrowser2> browser(browser_disp);
+ ASSERT_TRUE(browser != NULL);
+
+ CComPtr<IUnknown> frame_handler_keeper;
+ ASSERT_HRESULT_SUCCEEDED(
+ TestFrameEventHandler::CreateInitialized(this,
+ browser,
+ url,
+ &frame_handler_keeper));
+ } else {
+ ATLTRACE("FrameHandler[%ws] -> %ws\n", frame_handler->url(), url);
+ frame_handler->set_url(url);
+ }
+ }
+
+ private:
+ typedef std::map<IUnknown*, TestFrameEventHandler*> FrameHandlerMap;
+ // Keeps a map from a frame or top-level browser's identifying
+ // IUnknown to the frame event handler instance attached.
+ FrameHandlerMap frame_handlers_;
+};
+
+
+STDMETHODIMP TestFrameEventHandler::OnChanged(DISPID changed_property) {
+ ATLTRACE("OnChanged(%d)\n", changed_property);
+
+ if (changed_property == DISPID_READYSTATE) {
+ CComVariant ready_state;
+ CComDispatchDriver document(document_);
+ EXPECT_TRUE(document != NULL);
+ EXPECT_HRESULT_SUCCEEDED(document.GetProperty(DISPID_READYSTATE,
+ &ready_state));
+ EXPECT_EQ(V_VT(&ready_state), VT_I4);
+ ATLTRACE("READYSTATE Frame[%ws]: %d\n", url_, ready_state.lVal);
+
+ TestBrowserEventSink::add_state(static_cast<READYSTATE>(ready_state.lVal));
+ }
+
+ return S_OK;
+}
+
+HRESULT TestFrameEventHandler::Initialize(TestBrowserEventSink* event_sink,
+ IWebBrowser2* browser,
+ BSTR url) {
+ EXPECT_HRESULT_SUCCEEDED(browser->get_Document(&document_));
+
+ CComQIPtr<IHTMLDocument2> html_document2(document_);
+ if (html_document2 != NULL) {
+ event_sink_ = event_sink;
+ browser_ = browser;
+ url_ = url;
+ EXPECT_HRESULT_SUCCEEDED(AtlAdvise(document_,
+ GetUnknown(),
+ IID_IPropertyNotifySink,
+ &document_property_notify_sink_cookie_));
+
+ ATLTRACE("TestFrameEventHandler::Initialize[%ws]\n", url_ ? url_ : L"");
+
+ CComQIPtr<IOleObject> document_ole_object(document_);
+ EXPECT_TRUE(document_ole_object != NULL);
+ EXPECT_HRESULT_SUCCEEDED(
+ document_ole_object->Advise(this, &advise_sink_cookie_));
+
+ event_sink_->AttachHandler(browser_, this);
+ } else {
+ // This happens when we're navigated to e.g. a PDF doc or a folder.
+ }
+
+ return S_OK;
+}
+
+
+void TestFrameEventHandler::DetachFromSink() {
+ ASSERT_TRUE(event_sink_ != NULL);
+ event_sink_->DetachHandler(browser_, this);
+ event_sink_ = NULL;
+}
+
+void TestFrameEventHandler::FinalRelease() {
+ if (event_sink_ && browser_)
+ event_sink_->DetachHandler(browser_, this);
+ browser_.Release();
+ document_.Release();
+}
+
+STDMETHODIMP_(void) TestFrameEventHandler::OnClose() {
+ EXPECT_HRESULT_SUCCEEDED(AtlUnadvise(document_,
+ IID_IPropertyNotifySink,
+ document_property_notify_sink_cookie_));
+
+ CComQIPtr<IOleObject> document_ole_object(document_);
+ EXPECT_TRUE(document_ole_object != NULL);
+ EXPECT_HRESULT_SUCCEEDED(
+ document_ole_object->Unadvise(advise_sink_cookie_));
+}
+
+class BrowserEventTest: public ShellBrowserTestImpl<TestBrowserEventSink> {
+};
+
+const wchar_t* kSimplePage = L"simple_page.html";
+
+TEST_F(BrowserEventTest, RefreshTopLevelBrowserRetainsFrameHandler) {
+ EXPECT_TRUE(NavigateBrowser(GetTestUrl(kSimplePage)));
+
+ // We should have only one frame at this point.
+ EXPECT_EQ(1, TestFrameEventHandler::instance_count());
+
+ // Refreshing the top-level browser retains it.
+ EXPECT_HRESULT_SUCCEEDED(browser_->Refresh());
+ EXPECT_TRUE(WaitForReadystateLoading());
+ EXPECT_TRUE(WaitForReadystateComplete());
+
+ // Still there after refresh.
+ EXPECT_EQ(1, TestFrameEventHandler::instance_count());
+}
+
+const wchar_t* kTwoFramesPage = L"two_frames.html";
+const wchar_t* kFrameOne = L"frame_one.html";
+const wchar_t* kFrameTwo = L"frame_two.html";
+
+TEST_F(BrowserEventTest, NavigateToFrames) {
+ EXPECT_TRUE(NavigateBrowser(GetTestUrl(kTwoFramesPage)));
+
+ // We should have three frame handlers at this point.
+ EXPECT_EQ(3, TestFrameEventHandler::instance_count());
+
+ // We should have a handler for each of these.
+ TestFrameEventHandler* two_frames =
+ event_sink()->FindHandlerForUrl(GetTestUrl(kTwoFramesPage));
+ TestFrameEventHandler* frame_one =
+ event_sink()->FindHandlerForUrl(GetTestUrl(kFrameOne));
+ TestFrameEventHandler* frame_two =
+ event_sink()->FindHandlerForUrl(GetTestUrl(kFrameTwo));
+ ASSERT_TRUE(two_frames != NULL);
+ ASSERT_TRUE(frame_one != NULL);
+ ASSERT_TRUE(frame_two != NULL);
+
+ // Noteworthy fact: the top level browser implements an
+ // IPropertyNotifySink connection point, but the sub-browsers
+ // for the frames do not.
+ {
+ CComQIPtr<IConnectionPointContainer> cpc;
+ ASSERT_HRESULT_SUCCEEDED(two_frames->GetBrowser(&cpc));
+ ASSERT_TRUE(cpc != NULL);
+ CComPtr<IConnectionPoint> cp;
+ EXPECT_HRESULT_SUCCEEDED(
+ cpc->FindConnectionPoint(IID_IPropertyNotifySink, &cp));
+ }
+
+ {
+ CComQIPtr<IConnectionPointContainer> cpc;
+ ASSERT_HRESULT_SUCCEEDED(frame_one->GetBrowser(&cpc));
+ ASSERT_TRUE(cpc != NULL);
+ CComPtr<IConnectionPoint> cp;
+ EXPECT_HRESULT_FAILED(
+ cpc->FindConnectionPoint(IID_IPropertyNotifySink, &cp));
+ }
+
+ {
+ CComQIPtr<IConnectionPointContainer> cpc;
+ ASSERT_HRESULT_SUCCEEDED(frame_two->GetBrowser(&cpc));
+ ASSERT_TRUE(cpc != NULL);
+ CComPtr<IConnectionPoint> cp;
+ EXPECT_HRESULT_FAILED(
+ cpc->FindConnectionPoint(IID_IPropertyNotifySink, &cp));
+ }
+
+ // Test sub-frame document IPropertyNotifySink.
+ {
+ CComPtr<IWebBrowser2> browser;
+ ASSERT_HRESULT_SUCCEEDED(frame_two->GetBrowser(&browser));
+
+ CComPtr<IDispatch> document_disp;
+ ASSERT_HRESULT_SUCCEEDED(browser->get_Document(&document_disp));
+
+ CComPtr<IConnectionPointContainer> cpc;
+ ASSERT_HRESULT_SUCCEEDED(document_disp->QueryInterface(&cpc));
+ CComPtr<IConnectionPoint> cp;
+ ASSERT_HRESULT_SUCCEEDED(
+ cpc->FindConnectionPoint(IID_IPropertyNotifySink, &cp));
+ }
+}
+
+TEST_F(BrowserEventTest, ReNavigateToSamePageRetainsEventHandler) {
+ const std::wstring url(GetTestUrl(kSimplePage));
+ EXPECT_TRUE(NavigateBrowser(url));
+
+ // We should have a frame handler attached now.
+ EXPECT_EQ(1, TestFrameEventHandler::instance_count());
+
+ // Retrieve it and make sure it doesn't die.
+ TestFrameEventHandler* handler_before =
+ event_sink()->FindHandlerForUrl(url);
+
+ ASSERT_TRUE(handler_before != NULL);
+ CComPtr<IUnknown> handler_before_keeper(handler_before->GetUnknown());
+
+ // Re-navigate the browser to the same page.
+ EXPECT_TRUE(NavigateBrowser(GetTestUrl(kSimplePage)));
+ // Note: on re-navigation we don't see the top-level
+ // browser's readystate drop, I guess that only happens
+ // on transitions between content types. E.g. when a
+ // navigation requires the shell browser to instantiate
+ // a new type of document, such as going from a
+ // HTML doc to a PDF doc or the like.
+ EXPECT_FALSE(WaitForReadystateLoading());
+ EXPECT_TRUE(WaitForReadystateComplete());
+
+ // We should only have the one frame handler in existence now.
+ EXPECT_EQ(1, TestFrameEventHandler::instance_count());
+
+ // Retrieve the new one.
+ TestFrameEventHandler* handler_after =
+ event_sink()->FindHandlerForUrl(GetTestUrl(kSimplePage));
+
+ ASSERT_EQ(handler_before, handler_after);
+
+ // Release the old one, it should still stay around
+ handler_before_keeper.Release();
+ EXPECT_EQ(1, TestFrameEventHandler::instance_count());
+}
+
+TEST_F(BrowserEventTest, NavigateToDifferentPageRetainsEventHandler) {
+ const std::wstring first_url(GetTestUrl(kSimplePage));
+ EXPECT_TRUE(NavigateBrowser(first_url));
+
+ // We should have a frame handler attached now.
+ EXPECT_EQ(1, TestFrameEventHandler::instance_count());
+
+ // Retrieve it and make sure it doesn't die.
+ TestFrameEventHandler* handler_before =
+ event_sink()->FindHandlerForUrl(first_url);
+
+ ASSERT_TRUE(handler_before != NULL);
+ CComPtr<IUnknown> handler_before_keeper(handler_before->GetUnknown());
+
+ // Navigate the browser to another page.
+ const std::wstring second_url(GetTestUrl(kTwoFramesPage));
+ EXPECT_TRUE(NavigateBrowser(second_url));
+ EXPECT_FALSE(WaitForReadystateLoading());
+ EXPECT_TRUE(WaitForReadystateComplete());
+
+ // We should have the three frame handlers in existence now.
+ EXPECT_EQ(3, TestFrameEventHandler::instance_count());
+
+ // Retrieve the new one for the top-level browser.
+ TestFrameEventHandler* handler_after =
+ event_sink()->FindHandlerForUrl(second_url);
+
+ ASSERT_EQ(handler_before, handler_after);
+
+ // Release the old one, it should still stay around
+ handler_before_keeper.Release();
+ EXPECT_EQ(3, TestFrameEventHandler::instance_count());
+}
+
+TEST_F(BrowserEventTest, RefreshFrameBrowserRetainsHandler) {
+ EXPECT_TRUE(NavigateBrowser(GetTestUrl(kTwoFramesPage)));
+
+ // We should have three frame handlers at this point.
+ EXPECT_EQ(3, TestFrameEventHandler::instance_count());
+
+ // Get one of the frames.
+ TestFrameEventHandler* frame_two =
+ event_sink()->FindHandlerForUrl(GetTestUrl(kFrameTwo));
+ ASSERT_TRUE(frame_two != NULL);
+
+ // Now refresh a sub-browser instance, let it settle,
+ // observe its frame event handler is still around and
+ // has signalled a readystate transition.
+ CComPtr<IWebBrowser2> browser;
+ ASSERT_HRESULT_SUCCEEDED(frame_two->GetBrowser(&browser));
+ ASSERT_HRESULT_SUCCEEDED(browser->Refresh());
+
+ EXPECT_TRUE(WaitForReadystateLoading());
+ EXPECT_TRUE(WaitForReadystateComplete());
+
+ // We should have all three frame handlers at this point.
+ EXPECT_EQ(3, TestFrameEventHandler::instance_count());
+ frame_two = event_sink()->FindHandlerForUrl(GetTestUrl(kFrameTwo));
+ EXPECT_TRUE(frame_two != NULL);
+}
+
+} // namespace