1// Copyright (c) 2011 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#include "chrome_frame/com_message_event.h"
6
7#include "base/logging.h"
8#include "base/strings/string_util.h"
9#include "base/strings/stringprintf.h"
10#include "base/strings/utf_string_conversions.h"
11
12ComMessageEvent::ComMessageEvent() {
13}
14
15ComMessageEvent::~ComMessageEvent() {
16}
17
18bool ComMessageEvent::Initialize(IOleContainer* container,
19                                 const std::string& message,
20                                 const std::string& origin,
21                                 const std::string& event_type) {
22  DLOG_IF(WARNING, !container) << __FUNCTION__ << " no container";
23  message_ = message;
24  origin_ = origin;
25  type_ = event_type;
26
27  // May remain NULL if container not IE
28  base::win::ScopedComPtr<IHTMLEventObj> basic_event;
29  base::win::ScopedComPtr<IHTMLDocument2> doc;
30
31  // Fetching doc may fail in non-IE containers
32  // and container might be NULL in some applications.
33  if (container)
34    container->QueryInterface(doc.Receive());
35  if (doc) {
36    base::win::ScopedComPtr<IHTMLDocument4> doc4;
37    doc4.QueryFrom(doc);
38    DCHECK(doc4);  // supported by IE5.5 and higher
39    if (doc4) {
40      // IHTMLEventObj5 is only supported in IE8 and later, so we provide our
41      // own (minimalistic) implementation of it.
42      doc4->createEventObject(NULL, basic_event.Receive());
43      DCHECK(basic_event);
44    }
45  }
46
47  basic_event_ = basic_event;
48  return true;
49}
50
51STDMETHODIMP ComMessageEvent::GetTypeInfoCount(UINT* info) {
52  // Don't DCHECK as python scripts might still call this function
53  // inadvertently.
54  DLOG(WARNING) << "Not implemented: " << __FUNCTION__;
55  return E_NOTIMPL;
56}
57
58STDMETHODIMP ComMessageEvent::GetTypeInfo(UINT which_info, LCID lcid,
59                                          ITypeInfo** type_info) {
60  DLOG(WARNING) << "Not implemented: " << __FUNCTION__;
61  return E_NOTIMPL;
62}
63
64STDMETHODIMP ComMessageEvent::GetIDsOfNames(REFIID iid, LPOLESTR* names,
65                                            UINT count_names, LCID lcid,
66                                            DISPID* dispids) {
67  HRESULT hr = S_OK;
68
69  // Note that since we're using LowerCaseEqualsASCII for string comparison,
70  // the second argument _must_ be all lower case.  I.e. we cannot compare
71  // against L"messagePort" since it has a capital 'P'.
72  for (UINT i = 0; SUCCEEDED(hr) && i < count_names; ++i) {
73    const wchar_t* name_begin = names[i];
74    const wchar_t* name_end = name_begin + wcslen(name_begin);
75    if (LowerCaseEqualsASCII(name_begin, name_end, "data")) {
76      dispids[i] = DISPID_MESSAGE_EVENT_DATA;
77    } else if (LowerCaseEqualsASCII(name_begin, name_end, "origin")) {
78      dispids[i] = DISPID_MESSAGE_EVENT_ORIGIN;
79    } else if (LowerCaseEqualsASCII(name_begin, name_end, "lasteventid")) {
80      dispids[i] = DISPID_MESSAGE_EVENT_LAST_EVENT_ID;
81    } else if (LowerCaseEqualsASCII(name_begin, name_end, "source")) {
82      dispids[i] = DISPID_MESSAGE_EVENT_SOURCE;
83    } else if (LowerCaseEqualsASCII(name_begin, name_end, "messageport")) {
84      dispids[i] = DISPID_MESSAGE_EVENT_MESSAGE_PORT;
85    } else if (LowerCaseEqualsASCII(name_begin, name_end, "type")) {
86      dispids[i] = DISPID_MESSAGE_EVENT_TYPE;
87    } else {
88      if (basic_event_) {
89        hr = basic_event_->GetIDsOfNames(IID_IDispatch, &names[i], 1, lcid,
90                                         &dispids[i]);
91      } else {
92        hr = DISP_E_MEMBERNOTFOUND;
93      }
94
95      if (FAILED(hr)) {
96        DLOG(WARNING) << "member not found: " << names[i]
97                      << base::StringPrintf(L"0x%08X", hr);
98      }
99    }
100  }
101  return hr;
102}
103
104STDMETHODIMP ComMessageEvent::Invoke(DISPID dispid, REFIID iid, LCID lcid,
105                                     WORD flags, DISPPARAMS* params,
106                                     VARIANT* result, EXCEPINFO* excepinfo,
107                                     UINT* arg_err) {
108  HRESULT hr = DISP_E_MEMBERNOTFOUND;
109  switch (dispid) {
110    case DISPID_MESSAGE_EVENT_DATA:
111      hr = GetStringProperty(flags, UTF8ToWide(message_).c_str(), result);
112      break;
113
114    case DISPID_MESSAGE_EVENT_ORIGIN:
115      hr = GetStringProperty(flags, UTF8ToWide(origin_).c_str(), result);
116      break;
117
118    case DISPID_MESSAGE_EVENT_TYPE:
119      hr = GetStringProperty(flags, UTF8ToWide(type_).c_str(), result);
120      break;
121
122    case DISPID_MESSAGE_EVENT_LAST_EVENT_ID:
123      hr = GetStringProperty(flags, L"", result);
124      break;
125
126    case DISPID_MESSAGE_EVENT_SOURCE:
127    case DISPID_MESSAGE_EVENT_MESSAGE_PORT:
128      if (flags & DISPATCH_PROPERTYGET) {
129        result->vt = VT_NULL;
130        hr = S_OK;
131      } else {
132        hr = DISP_E_TYPEMISMATCH;
133      }
134      break;
135
136    default:
137      if (basic_event_) {
138        hr = basic_event_->Invoke(dispid, iid, lcid, flags, params, result,
139                                  excepinfo, arg_err);
140      }
141      break;
142  }
143
144  return hr;
145}
146
147HRESULT ComMessageEvent::GetStringProperty(WORD flags, const wchar_t* value,
148                                           VARIANT* result) {
149  if (!result)
150    return E_INVALIDARG;
151
152  HRESULT hr;
153  if (flags & DISPATCH_PROPERTYGET) {
154    result->vt = VT_BSTR;
155    result->bstrVal = ::SysAllocString(value);
156    hr = S_OK;
157  } else {
158    hr = DISP_E_TYPEMISMATCH;
159  }
160  return hr;
161}
162