// Copyright (c) 2006-2008 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 "webkit/activex_shim/web_activex_site.h" #include #include #include #include "base/string_util.h" #include "webkit/activex_shim/activex_plugin.h" #include "webkit/activex_shim/npp_impl.h" #include "webkit/activex_shim/web_activex_container.h" namespace activex_shim { // WebActiveXSite WebActiveXSite::WebActiveXSite() : container_(NULL), control_(NULL), inplace_activated_(false), has_capture_(false) { rect_.left = 0; rect_.top = 0; rect_.right = 0; rect_.bottom = 0; } WebActiveXSite::~WebActiveXSite() { // Don't do anything here. Do everything in FinalRelease. } void WebActiveXSite::Init(WebActiveXContainer* container, IUnknown* control) { container_ = container; control_.Attach(control); dispatch_ = control; ole_object_ = control; inplace_object_ = control; view_object_ = control; inplace_object_windowless_ = control; } void WebActiveXSite::FinalRelease() { // We must release everything here instead of leaving it to the destructor. // Otherwise crash is possible. if (control_ != NULL) { dispatch_.Release(); view_object_.Release(); inplace_object_windowless_.Release(); if (inplace_object_ != NULL) { if (inplace_activated_) { // If we just deactivate without checking whether the control has been // inplace activated, the control may behave irratically. Flash will // decrease its reference count during deactivation. Thus causing // crash when we try to release it later. inplace_object_->InPlaceDeactivate(); inplace_activated_ = false; } inplace_object_.Release(); } if (ole_object_ != NULL) { ole_object_->SetClientSite(NULL); ole_object_->Close(OLECLOSE_NOSAVE); ole_object_.Release(); } long ref = control_.Detach()->Release(); // It should be 0 otherwise we have incorrect ref counting. // Shockwave is known to have ref counting problems. All other controls // behave well. DCHECK(ref == 0 || container_->plugin()->activex_type() == ACTIVEX_SHOCKWAVE); } } HRESULT WebActiveXSite::ActivateControl( int x, int y, int width, int height, const std::vector& params) { // Set the rect size of site first before SetClientSite. Otherwise the // control may query the site for such information during SetClientSite. rect_.left = x; rect_.top = y; rect_.right = rect_.left + width; rect_.bottom = rect_.top + height; HRESULT hr; if (ole_object_ != NULL) { hr = ole_object_->SetClientSite(static_cast(this)); if (FAILED(hr)) return hr; } SetExtent(width, height); // Set initial properties. CComQIPtr persist_property_bag = control_; CComQIPtr persist_property_bag2 = control_; if (persist_property_bag2 != NULL || persist_property_bag != NULL) { // Use property bag for initialization. This is the preferred way. initial_params_ = params; // Use bag2 first. if (persist_property_bag2 != NULL) { persist_property_bag2->InitNew(); hr = persist_property_bag2->Load(this, NULL); DCHECK(SUCCEEDED(hr)); } else { persist_property_bag->InitNew(); hr = persist_property_bag->Load(this, NULL); DCHECK(SUCCEEDED(hr)); } // We don't need this anymore. initial_params_.clear(); } else if (dispatch_ != NULL) { // Use the dispatch interface to set the initial properties. This is // less efficient for most controls. for (unsigned int i = 0; i < params.size(); ++i) { const ControlParam& param = params[i]; VARIANT vtvalue; // TODO(ruijiang): Think about type conversion. vtvalue.vt = VT_BSTR; vtvalue.bstrVal = SysAllocString(param.value.c_str()); DispSetProperty(dispatch_, param.name.c_str(), vtvalue); VariantClear(&vtvalue); } } // In place activate it if it is able to. if (inplace_object_ != NULL) { hr = DoVerb(OLEIVERB_INPLACEACTIVATE); if (FAILED(hr)) return hr; } return S_OK; } HRESULT WebActiveXSite::DoVerb(long verb) { if (ole_object_ != NULL) { HRESULT hr = ole_object_->DoVerb(verb, NULL, static_cast(this), 0, container_->container_wnd(), &rect_); if (verb == OLEIVERB_INPLACEACTIVATE && SUCCEEDED(hr)) inplace_activated_ = true; return hr; } else { return E_UNEXPECTED; } } HRESULT WebActiveXSite::SetExtent(int width, int height) { if (ole_object_ != NULL) { SIZEL size; if (width < 0) width = 0; if (height < 0) height = 0; ScreenToHimetric(width, height, &size); return ole_object_->SetExtent(DVASPECT_CONTENT, &size); } else { return E_UNEXPECTED; } } void WebActiveXSite::SetRect(const RECT* rect) { if (EqualRect(&rect_, rect)) return; SetExtent(rect->right - rect->left, rect->bottom - rect->top); if (inplace_object_ != NULL) { inplace_object_->SetObjectRects(rect, rect); rect_ = *rect; } } // IUnknown HRESULT STDMETHODCALLTYPE WebActiveXSite::QueryInterface(REFIID iid, void** object) { *object = NULL; if (iid == IID_IUnknown) { // Avoid ambiguous resolution of IUnknown. *object = static_cast(static_cast(this)); } else if (iid == IID_IDispatch) { *object = static_cast(this); } else if (iid == IID_IOleClientSite) { *object = static_cast(this); } else if (iid == IID_IOleControlSite) { *object = static_cast(this); } else if (iid == IID_IOleInPlaceSite) { *object = static_cast(this); } else if (iid == IID_IOleInPlaceSiteEx) { *object = static_cast(this); } else if (iid == IID_IOleInPlaceSiteWindowless) { if (container_->plugin()->windowless()) *object = static_cast(this); } else if (iid == IID_IServiceProvider) { *object = static_cast(this); } else if (iid == IID_IPropertyBag) { *object = static_cast(this); } else if (iid == IID_IPropertyBag2) { *object = static_cast(this); } TRACK_QUERY_INTERFACE(iid, *object != NULL); return (*object != NULL) ? S_OK : E_NOINTERFACE; } // IOleClientSite HRESULT STDMETHODCALLTYPE WebActiveXSite::SaveObject() { // Do not support saving object to persistant storage. return E_NOTIMPL; } // Even though Flash will call this method to get the url, it will not use // the url to resolve its movie path. // However, according to http://support.microsoft.com/kb/181678, this is // a valid way of getting url from ActiveX control. HRESULT STDMETHODCALLTYPE WebActiveXSite::GetMoniker( DWORD assign, DWORD which_moniker, IMoniker** moniker) { TRACK_METHOD(); if (which_moniker == OLEWHICHMK_CONTAINER) { std::wstring url = container_->plugin()->GetCurrentURL(); HRESULT hr = CreateURLMoniker(NULL, url.c_str(), moniker); return hr; } else { return E_FAIL; } } HRESULT STDMETHODCALLTYPE WebActiveXSite::GetContainer( IOleContainer** container) { *container = container_; return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::ShowObject() { TRACK_METHOD(); // The control asks us to show the object which we already did. return S_OK; }; HRESULT STDMETHODCALLTYPE WebActiveXSite::OnShowWindow(BOOL show) { TRACK_METHOD(); // Doesn't apply to us. return S_OK; }; HRESULT STDMETHODCALLTYPE WebActiveXSite::RequestNewObjectLayout() { TRACK_METHOD(); // As MSDN says: "Currently, there is no standard mechanism by which // a container can negotiate how much room an object would like. When // such a negotiation is defined, responding to this method will be // optional for containers." return E_NOTIMPL; } // IOleControlSite HRESULT STDMETHODCALLTYPE WebActiveXSite::OnControlInfoChanged() { TRACK_METHOD(); // As we do not support mnemonics, we do not need to retrieve control info. // This may change in the future though. return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::LockInPlaceActive(BOOL lock) { TRACK_METHOD(); // We don't support this. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::GetExtendedControl(IDispatch** disp) { TRACK_METHOD(); // We do not support extended control. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::TransformCoords(POINTL* ptl_himetric, POINTF* ptf_container, DWORD flags) { TRACK_METHOD(); // TODO(ruijiang): so far haven't found anyone use this yet. Be aware and // add support if needed. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::TranslateAccelerator( MSG* msg, DWORD modifiers) { TRACK_METHOD(); // It would be nice if controls call this and let me process accelerator // first. But unfortunately all of them I tested don't. So let's ignore // and just keep an eye on it. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnFocus(BOOL got_focus) { TRACK_METHOD(); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::ShowPropertyFrame() { TRACK_METHOD(); // No we don't want to show property sheet. return E_NOTIMPL; } // IOleWindow HRESULT STDMETHODCALLTYPE WebActiveXSite::GetWindow(HWND* wnd) { *wnd = container_->container_wnd(); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::ContextSensitiveHelp( BOOL enter_mode) { // Do not support this. return E_NOTIMPL; } // IOleInPlaceSite HRESULT STDMETHODCALLTYPE WebActiveXSite::CanInPlaceActivate() { TRACK_METHOD(); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnInPlaceActivate() { TRACK_METHOD(); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnInPlaceDeactivate() { TRACK_METHOD(); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnUIActivate() { TRACK_METHOD(); // If we have multiple sites in a container we may deactivate the previous // active control. This is not a requirement though. return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnUIDeactivate(BOOL undoable) { TRACK_METHOD(); // Some controls will call this when they lose focus. Right now we don't need // to do anything about it. return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::GetWindowContext( IOleInPlaceFrame** frame, IOleInPlaceUIWindow** doc, LPRECT pos, LPRECT clip, LPOLEINPLACEFRAMEINFO frame_info) { TRACK_METHOD(); if (frame) { *frame = container_; } if (doc) *doc = NULL; if (pos) *pos = rect_; if (clip) *clip = rect_; if (frame_info) { frame_info->fMDIApp = FALSE; frame_info->hwndFrame = container_->container_wnd(); frame_info->haccel = NULL; frame_info->cAccelEntries = 0; } return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::Scroll(SIZE scroll_extant) { TRACK_METHOD(); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::DiscardUndoState() { TRACK_METHOD(); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::DeactivateAndUndo() { TRACK_METHOD(); // Just let the object know that it's deactivated. if (inplace_object_ != NULL) inplace_object_->UIDeactivate(); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnPosRectChange(LPCRECT pos) { TRACK_METHOD(); // We do not let the control move/resize itself. It should be controled // by the container/browser. return E_UNEXPECTED; } // IOleInPlaceSiteEx HRESULT STDMETHODCALLTYPE WebActiveXSite::OnInPlaceActivateEx( BOOL* no_redraw, DWORD flags) { TRACK_METHOD(); // Redraw doesn't hurt. if (no_redraw) *no_redraw = FALSE; if (flags & ACTIVATE_WINDOWLESS) { // TODO(ruijiang): At this point we know for sure the object is activated // as windowless. Revisit when we implement windowless controls. } return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnInPlaceDeactivateEx( BOOL no_redraw) { TRACK_METHOD(); // See also: OnInPlaceDeactivate return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::RequestUIActivate() { TRACK_METHOD(); return S_OK; } // IOleInPlaceSiteWindowless HRESULT STDMETHODCALLTYPE WebActiveXSite::CanWindowlessActivate() { TRACK_METHOD(); // Yes, we prefer windowless activation. return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::GetCapture() { TRACK_METHOD(); return has_capture_ ? S_OK : S_FALSE; } HRESULT STDMETHODCALLTYPE WebActiveXSite::SetCapture(BOOL capture) { TRACK_METHOD(); // TODO(ruijiang): for now, let's cheat the control that it can always get // what it wants (capture). if (capture) { has_capture_ = true; return S_OK; } else { has_capture_ = false; return S_OK; } } HRESULT STDMETHODCALLTYPE WebActiveXSite::GetFocus() { TRACK_METHOD(); // TODO(ruijiang): handle it. return S_FALSE; } HRESULT STDMETHODCALLTYPE WebActiveXSite::SetFocus(BOOL focus) { TRACK_METHOD(); // TODO(ruijiang): handle it. if (focus) { return S_FALSE; } else { return S_OK; } } HRESULT STDMETHODCALLTYPE WebActiveXSite::GetDC( LPCRECT rect, DWORD flags, HDC* dc) { // It's probably not wise to get the dc of Chrome window and return it, // because we always draw onto a memory dc. Thus we may have to disappoint // the caller. // TODO(ruijiang): We may enable this for other browsers like FireFox. return E_FAIL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::ReleaseDC(HDC dc) { // TODO(ruijiang): We may enable this for other browsers like FireFox. return E_FAIL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::InvalidateRect( LPCRECT rect, BOOL erase) { // This would be the rect (in client coord of the control) that we will // invalidate. RECT rc; // Control's client area, start from top-left corner as 0. RECT client = rect_; OffsetRect(&client, -rect_.left, -rect_.top); if (rect) { RECT rc_in_client = *rect; OffsetRect(&rc_in_client, -rect_.left, -rect_.top); if (!IntersectRect(&rc, &rc_in_client, &client)) return S_OK; } else { rc = client; } // Convert it to NPRect. Now rc is relative to the upper-left corner of // control. This is the requirement of NPN_InvalidateRect. NPRect npr; npr.left = static_cast(rc.left); npr.top = static_cast(rc.top); npr.right = static_cast(rc.right); npr.bottom = static_cast(rc.bottom); g_browser->invalidaterect(container_->plugin()->npp(), &npr); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::InvalidateRgn( HRGN rgn, BOOL erase) { TRACK_METHOD(); if (rgn == NULL) { return InvalidateRect(NULL, erase); } else { // TODO(ruijiang): So far no one is using this function yet. So let's just // invalidate the whole area. Optimize this when we need to. return InvalidateRect(NULL, erase); } } HRESULT STDMETHODCALLTYPE WebActiveXSite::ScrollRect( INT dx, INT dy, LPCRECT scroll, LPCRECT clip) { TRACK_METHOD(); // TODO(ruijiang): revisit. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::AdjustRect(LPRECT rc) { TRACK_METHOD(); // TODO(ruijiang): revisit. return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::OnDefWindowMessage( UINT msg, WPARAM wparam, LPARAM lparam, LRESULT* result) { TRACK_METHOD(); // TODO(ruijiang): handle it later. return E_NOTIMPL; } // IServiceProvider HRESULT STDMETHODCALLTYPE WebActiveXSite::QueryService( REFGUID guid, REFIID riid, void** object) { HRESULT hr = E_FAIL; // TODO(ruijiang): We may need to support SID_SWebBrowserApp and // IID_IHTMLWindow2 in the future. if (guid == IID_IBindHost || guid == IID_IWebBrowserApp) hr = container_->QueryInterface(riid, object); TRACK_QUERY_INTERFACE(riid, *object != NULL); return hr; } // IPropertyBag HRESULT STDMETHODCALLTYPE WebActiveXSite::Read(LPCOLESTR prop_name, VARIANT* var, IErrorLog* err_log) { unsigned int i; for (i = 0; i < initial_params_.size(); ++i) { if (_wcsicmp(prop_name, initial_params_[i].name.c_str()) == 0) break; } if (i >= initial_params_.size()) return E_INVALIDARG; if (var->vt == VT_EMPTY || var->vt == VT_BSTR) { // We don't need to do any conversion in this case. var->vt = VT_BSTR; var->bstrVal = ::SysAllocString(initial_params_[i].value.c_str()); return S_OK; } else { // We need to try type conversion. ScopedVariant org; org.vt = VT_BSTR; org.bstrVal = ::SysAllocString(initial_params_[i].value.c_str()); HRESULT hr = VariantChangeType(var, &org, 0, var->vt); return FAILED(hr) ? E_FAIL : S_OK; } } HRESULT STDMETHODCALLTYPE WebActiveXSite::Write(LPCOLESTR prop_name, VARIANT* var) { TRACK_METHOD(); return E_NOTIMPL; } // IPropertyBag2 HRESULT STDMETHODCALLTYPE WebActiveXSite::Read(ULONG c_properties, PROPBAG2* prop_bag, IErrorLog* err_log, VARIANT* value, HRESULT* error) { if (!prop_bag) return E_INVALIDARG; for (unsigned int i = 0; i < c_properties; ++i) { PROPBAG2* p = prop_bag + i; value->vt = p->vt; HRESULT hr = Read(p->pstrName, value + i, err_log); if (error) error[i] = hr; } return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::Write(ULONG c_properties, PROPBAG2* prop_bag, VARIANT* value) { TRACK_METHOD(); return E_NOTIMPL; } HRESULT STDMETHODCALLTYPE WebActiveXSite::CountProperties( ULONG* pc_properties) { if (!pc_properties) return E_INVALIDARG; *pc_properties = static_cast(initial_params_.size()); return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::GetPropertyInfo( ULONG iproperty, ULONG c_properties, PROPBAG2* prop_bag, ULONG* properties_returned) { if (!prop_bag) return E_INVALIDARG; if (iproperty >= initial_params_.size()) return E_INVALIDARG; unsigned int i; for (i = iproperty; i < iproperty + c_properties && i < initial_params_.size(); ++i) { PROPBAG2* p = prop_bag + (i - iproperty); memset(p, 0, sizeof(PROPBAG2)); p->dwType = PROPBAG2_TYPE_DATA; p->vt = VT_BSTR; p->cfType = CF_TEXT; p->dwHint = iproperty; // According to the document of IPropertyBag2::GetPropertyInfo, here // requires a string allocated by CoTaskMemAlloc. p->pstrName = CoTaskMemAllocString(initial_params_[i].name); } if (properties_returned) *properties_returned = i - iproperty; return S_OK; } HRESULT STDMETHODCALLTYPE WebActiveXSite::LoadObject(LPCOLESTR name, DWORD hint, IUnknown* unk_object, IErrorLog* err_log) { TRACK_METHOD(); return E_NOTIMPL; } } // namespace activex_shim