Home | History | Annotate | Download | only in test
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_TEST_H_
      6 #define CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_TEST_H_
      7 
      8 #include <atlbase.h>
      9 #include <atlcom.h>
     10 #include <string>
     11 #include <vector>
     12 
     13 #include "base/files/file_path.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "base/strings/utf_string_conversions.h"
     17 #include "base/win/object_watcher.h"
     18 #include "chrome_frame/test/chrome_frame_test_utils.h"
     19 #include "chrome_frame/test/chrome_frame_ui_test_utils.h"
     20 #include "chrome_frame/test/ie_event_sink.h"
     21 #include "chrome_frame/test/test_server.h"
     22 #include "chrome_frame/test/test_with_web_server.h"
     23 #include "chrome_frame/test/win_event_receiver.h"
     24 #include "testing/gmock/include/gmock/gmock.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 #include "third_party/xulrunner-sdk/win/include/accessibility/AccessibleEventId.h"
     27 
     28 namespace chrome_frame_test {
     29 
     30 // Convenience enum for specifying whether a load occurred in IE or CF.
     31 enum LoadedInRenderer {
     32   IN_IE = 0,
     33   IN_CF
     34 };
     35 
     36 // This mocks an IEEventListener, providing methods for expecting certain
     37 // sequences of events.
     38 class MockIEEventSink : public IEEventListener {
     39  public:
     40   MockIEEventSink() {
     41     CComObject<IEEventSink>::CreateInstance(&event_sink_);
     42     event_sink_->AddRef();
     43   }
     44 
     45   ~MockIEEventSink() {
     46     Detach();
     47     int reference_count = event_sink_->reference_count();
     48     LOG_IF(ERROR, reference_count != 1)
     49         << "Event sink is still referenced externally: ref count = "
     50         << reference_count;
     51     event_sink_->Release();
     52   }
     53 
     54   // Override IEEventListener methods.
     55   MOCK_METHOD7(OnBeforeNavigate2, void (IDispatch* dispatch,  // NOLINT
     56                                         VARIANT* url,
     57                                         VARIANT* flags,
     58                                         VARIANT* target_frame_name,
     59                                         VARIANT* post_data,
     60                                         VARIANT* headers,
     61                                         VARIANT_BOOL* cancel));
     62   MOCK_METHOD2(OnNavigateComplete2, void (IDispatch* dispatch,  // NOLINT
     63                                           VARIANT* url));
     64   MOCK_METHOD5(OnNewWindow3, void (IDispatch** dispatch,  // NOLINT
     65                                    VARIANT_BOOL* cancel,
     66                                    DWORD flags,
     67                                    BSTR url_context,
     68                                    BSTR url));
     69   MOCK_METHOD2(OnNewWindow2, void (IDispatch** dispatch,  // NOLINT
     70                                    VARIANT_BOOL* cancel));
     71   MOCK_METHOD5(OnNavigateError, void (IDispatch* dispatch,  // NOLINT
     72                                       VARIANT* url,
     73                                       VARIANT* frame_name,
     74                                       VARIANT* status_code,
     75                                       VARIANT* cancel));
     76   MOCK_METHOD2(OnFileDownload, void (VARIANT_BOOL active_doc,  // NOLINT
     77                                      VARIANT_BOOL* cancel));
     78   MOCK_METHOD0(OnQuit, void ());  // NOLINT
     79   MOCK_METHOD1(OnLoadError, void (const wchar_t* url));  // NOLINT
     80   MOCK_METHOD3(OnMessage, void (const wchar_t* message,  // NOLINT
     81                                 const wchar_t* origin,
     82                                 const wchar_t* source));
     83   MOCK_METHOD2(OnNewBrowserWindow, void (IDispatch* dispatch,  // NOLINT
     84                                          const wchar_t* url));
     85 
     86   // Convenience OnLoad method which is called once when a page is loaded with
     87   // |is_cf| set to whether the renderer is CF or not.
     88   MOCK_METHOD2(OnLoad, void (bool is_cf, const wchar_t* url));  // NOLINT
     89 
     90   // Attach |dispatch| to the event sink and begin listening to the source's
     91   // events.
     92   void Attach(IDispatch* dispatch) {
     93     event_sink_->set_listener(this);
     94     event_sink_->Attach(dispatch);
     95   }
     96 
     97   void Detach() {
     98     event_sink_->set_listener(NULL);
     99     event_sink_->Uninitialize();
    100   }
    101 
    102   // Expect a normal navigation to |url| to occur in CF or IE.
    103   void ExpectNavigation(bool is_cf, const std::wstring& url);
    104 
    105   // Similar as above, but should be used in cases where IE6 with CF may not
    106   // generate an OnBeforeNavigate event during the navigation. This occurs
    107   // during in-page navigation (via anchors), form submission, window.open
    108   // to target "self", and possibly others.
    109   void ExpectNavigationOptionalBefore(bool is_cf, const std::wstring& url);
    110 
    111   // Expect a navigation in a new window created by a window.open call to |url|.
    112   // |parent_cf| signifies whether the parent frame was loaded in CF, while
    113   // |new_window_cf| signifies whether to expect the new page to be loaded in
    114   // CF.
    115   void ExpectJavascriptWindowOpenNavigation(bool parent_cf, bool new_window_cf,
    116                                             const std::wstring& url);
    117 
    118   // Expect a new window to open. The new event sink will be attached to
    119   // |new_window_mock|.
    120   void ExpectNewWindow(MockIEEventSink* new_window_mock);
    121 
    122   // Expects any and all navigations.
    123   void ExpectAnyNavigations();
    124 
    125   void ExpectDocumentReadystate(int ready_state);
    126 
    127   IEEventSink* event_sink() { return event_sink_; }
    128 
    129  private:
    130   // Override IE's OnDocumentComplete to call our OnLoad, iff it is IE actually
    131   // rendering the page.
    132   virtual void OnDocumentComplete(IDispatch* dispatch, VARIANT* url);
    133 
    134   // Override CF's OnLoad to call our OnLoad.
    135   virtual void OnLoad(const wchar_t* url) {
    136     OnLoad(IN_CF, url);
    137   }
    138 
    139   // Helper method for expecting navigations. |before_cardinality| specifies
    140   // the cardinality for the BeforeNavigate expectation and
    141   // |complete_cardinality| specifies the cardinality for the NavigateComplete
    142   // expectation. Returns the set of expectations added.
    143   // Note: Prefer making a new Expect... method before making this public.
    144   testing::ExpectationSet ExpectNavigationCardinality(const std::wstring& url,
    145       testing::Cardinality before_cardinality,
    146       testing::Cardinality complete_cardinality);
    147 
    148   // It may be necessary to create this on the heap. Otherwise, if the
    149   // reference count is greater than zero, a debug assert will be triggered
    150   // in the destructor. This happens at least when IE crashes. In that case,
    151   // DispEventUnadvise and CoDisconnectObject are not sufficient to decrement
    152   // the reference count.
    153   // TODO(kkania): Investigate if the above is true.
    154   CComObject<IEEventSink>* event_sink_;
    155 };
    156 
    157 // This mocks a PropertyNotifySinkListener, providing methods for
    158 // expecting certain sequences of events.
    159 class MockPropertyNotifySinkListener : public PropertyNotifySinkListener {
    160  public:
    161   MockPropertyNotifySinkListener() : cookie_(0), sink_(NULL) {
    162     CComObject<PropertyNotifySinkImpl>::CreateInstance(&sink_);
    163     sink_->AddRef();
    164     sink_->set_listener(this);
    165   }
    166 
    167   ~MockPropertyNotifySinkListener() {
    168     Detach();
    169     sink_->set_listener(NULL);
    170     LOG_IF(ERROR, sink_->m_dwRef != 1)
    171         << "Event sink is still referenced externally: ref count = "
    172         << sink_->m_dwRef;
    173     sink_->Release();
    174   }
    175 
    176   // Override PropertyNotifySinkListener methods.
    177   MOCK_METHOD1(OnChanged, void (DISPID dispid));  // NOLINT
    178 
    179   bool Attach(IUnknown* obj) {
    180     DCHECK_EQ(cookie_, 0UL);
    181     DCHECK(obj);
    182     HRESULT hr = AtlAdvise(obj, sink_->GetUnknown(), IID_IPropertyNotifySink,
    183                            &cookie_);
    184     if (SUCCEEDED(hr)) {
    185       event_source_ = obj;
    186     } else {
    187       LOG(ERROR) << base::StringPrintf("AtlAdvise: 0x%08X", hr);
    188       cookie_ = 0;
    189     }
    190 
    191     return SUCCEEDED(hr);
    192   }
    193 
    194   void Detach() {
    195     if (event_source_) {
    196       DCHECK_NE(cookie_, 0UL);
    197       AtlUnadvise(event_source_, IID_IPropertyNotifySink, cookie_);
    198       event_source_.Release();
    199       cookie_ = 0;
    200     }
    201   }
    202 
    203  private:
    204   CComObject<PropertyNotifySinkImpl>* sink_;
    205   DWORD cookie_;
    206   base::win::ScopedComPtr<IUnknown> event_source_;
    207 };
    208 
    209 // Allows tests to observe when processes exit.
    210 class MockObjectWatcherDelegate : public base::win::ObjectWatcher::Delegate {
    211  public:
    212   // base::win::ObjectWatcher::Delegate implementation
    213   MOCK_METHOD1(OnObjectSignaled, void (HANDLE process_handle));  // NOLINT
    214 
    215   virtual ~MockObjectWatcherDelegate() {
    216     // Would be nice to free them when OnObjectSignaled is called, too, but
    217     // it doesn't seem worth it.
    218     for (std::vector<HANDLE>::iterator it = process_handles_.begin();
    219          it != process_handles_.end(); ++it) {
    220       ::CloseHandle(*it);
    221     }
    222   }
    223 
    224   // Registers this instance to watch |process_handle| for termination.
    225   void WatchProcess(HANDLE process_handle) {
    226     process_handles_.push_back(process_handle);
    227     object_watcher_.StartWatching(process_handle, this);
    228   }
    229 
    230   // Registers this instance to watch |hwnd|'s owning process for termination.
    231   void WatchProcessForHwnd(HWND hwnd) {
    232     DWORD pid = 0;
    233     ::GetWindowThreadProcessId(hwnd, &pid);
    234     EXPECT_TRUE(pid);
    235     if (pid != 0) {
    236       HANDLE process_handle = ::OpenProcess(SYNCHRONIZE, FALSE, pid);
    237       EXPECT_TRUE(process_handle);
    238       if (process_handle != NULL) {
    239         WatchProcess(process_handle);
    240       }
    241     }
    242   }
    243 
    244  private:
    245   std::vector<HANDLE> process_handles_;
    246   base::win::ObjectWatcher object_watcher_;
    247 };
    248 
    249 // Mocks a window observer so that tests can detect new windows.
    250 class MockWindowObserver : public WindowObserver {
    251  public:
    252   // WindowObserver implementation
    253   MOCK_METHOD1(OnWindowOpen, void (HWND hwnd));  // NOLINT
    254   MOCK_METHOD1(OnWindowClose, void (HWND hwnd));  // NOLINT
    255 
    256   void WatchWindow(std::string caption_pattern, std::string class_pattern) {
    257     window_watcher_.AddObserver(this, caption_pattern, class_pattern);
    258   }
    259 
    260  private:
    261   WindowWatchdog window_watcher_;
    262 };
    263 
    264 class MockAccEventObserver : public AccEventObserver {
    265  public:
    266   MOCK_METHOD1(OnAccDocLoad, void (HWND));  // NOLINT
    267   MOCK_METHOD3(OnAccValueChange, void (HWND, AccObject*,  // NOLINT
    268                                        const std::wstring&));
    269   MOCK_METHOD1(OnMenuPopup, void (HWND));  // NOLINT
    270   MOCK_METHOD2(OnTextCaretMoved, void (HWND, AccObject*));  // NOLINT
    271 };
    272 
    273 // This test fixture provides common methods needed for testing CF
    274 // integration with IE. gMock is used to verify that IE is reporting correct
    275 // navigational events and MockWebServer is used to verify that the correct
    276 // requests are going out.
    277 class MockIEEventSinkTest {
    278  public:
    279   MockIEEventSinkTest();
    280   MockIEEventSinkTest(int port, const std::wstring& address,
    281                       const base::FilePath& root_dir);
    282 
    283   ~MockIEEventSinkTest() {
    284     // Detach manually here so that it occurs before |last_resort_close_ie_|
    285     // is destroyed.
    286     ie_mock_.Detach();
    287   }
    288 
    289   // Launches IE as a COM server and sets |ie_mock_| as the event sink, then
    290   // navigates to the given url. Then the timed message loop is run until
    291   // |ie_mock_| receives OnQuit or the timeout is exceeded.
    292   void LaunchIEAndNavigate(const std::wstring& url);
    293 
    294   // Same as above but allows the timeout to be specified.
    295   void LaunchIENavigateAndLoop(const std::wstring& url,
    296                                base::TimeDelta timeout);
    297 
    298   // Returns the url for the test file given. |relative_path| should be
    299   // relative to the test data directory.
    300   std::wstring GetTestUrl(const std::wstring& relative_path);
    301 
    302   // Returns the absolute FilePath for the test file given. |relative_path|
    303   // should be relative to the test data directory.
    304   base::FilePath GetTestFilePath(const std::wstring& relative_path);
    305 
    306   // Returns the url for an html page just containing some text. Iff |use_cf|
    307   // is true, the chrome_frame meta tag will be included too.
    308   std::wstring GetSimplePageUrl() {
    309     return GetTestUrl(L"simple.html");
    310   }
    311 
    312   // Returns the title of the html page at |GetSimplePageUrl()|.
    313   std::wstring GetSimplePageTitle() {
    314     return L"simple web page";
    315   }
    316 
    317   // Returns the url for an html page just containing one link to the simple
    318   // page mentioned above.
    319   std::wstring GetLinkPageUrl() {
    320     return GetTestUrl(L"link.html");
    321   }
    322 
    323   // Returns the title of the html page at |GetLinkPageUrl()|.
    324   std::wstring GetLinkPageTitle() {
    325     return L"link";
    326   }
    327 
    328   // Returns the url for an html page containing several anchors pointing
    329   // to different parts of the page. |index| specifies what fragment to
    330   // append to the url. If zero, no fragment is appended. The highest fragment
    331   // is #a4.
    332   std::wstring GetAnchorPageUrl(int index) {
    333     DCHECK_LT(index, 5);
    334     std::wstring base_name = L"anchor.html";
    335     if (index > 0)
    336       base_name += std::wstring(L"#a") + base::IntToString16(index);
    337     return GetTestUrl(base_name);
    338   }
    339 
    340   // Returns the title of the html page at |GetAnchorPageUrl()|.
    341   std::wstring GetAnchorPageTitle() {
    342     return L"Chrome Frame Test";
    343   }
    344 
    345   // Returns the url for an html page that will, when clicked, open a new window
    346   // to |target|.
    347   std::wstring GetWindowOpenUrl(const wchar_t* target) {
    348     return GetTestUrl(std::wstring(L"window_open.html?").append(target));
    349   }
    350 
    351   // Returns the title of the html page at |GetWindowOpenUrl()|.
    352   std::wstring GetWindowOpenTitle() {
    353     return L"window open";
    354   }
    355 
    356  protected:
    357   CloseIeAtEndOfScope last_resort_close_ie_;
    358   chrome_frame_test::TimedMsgLoop loop_;
    359   testing::StrictMock<MockIEEventSink> ie_mock_;
    360   testing::StrictMock<MockWebServer> server_mock_;
    361   scoped_refptr<HungCOMCallDetector> hung_call_detector_;
    362  private:
    363   DISALLOW_COPY_AND_ASSIGN(MockIEEventSinkTest);
    364 };
    365 
    366 }  // namespace chrome_frame_test
    367 
    368 #endif  // CHROME_FRAME_TEST_MOCK_IE_EVENT_SINK_TEST_H_
    369