/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=8 et : */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at: * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Code. * * The Initial Developer of the Original Code is * The Mozilla Foundation * Portions created by the Initial Developer are Copyright (C) 2010 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Chris Jones * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ #include "mozilla/dom/PBrowserChild.h" #include "BasicLayers.h" #if defined(MOZ_ENABLE_D3D10_LAYER) # include "LayerManagerD3D10.h" #endif #include "gfxPlatform.h" #include "PuppetWidget.h" using namespace mozilla::layers; using namespace mozilla::widget; using namespace mozilla::dom; static void InvalidateRegion(nsIWidget* aWidget, const nsIntRegion& aRegion) { nsIntRegionRectIterator it(aRegion); while(const nsIntRect* r = it.Next()) { aWidget->Invalidate(*r); } } /*static*/ already_AddRefed nsIWidget::CreatePuppetWidget(PBrowserChild *aTabChild) { NS_ABORT_IF_FALSE(nsIWidget::UsePuppetWidgets(), "PuppetWidgets not allowed in this configuration"); nsCOMPtr widget = new PuppetWidget(aTabChild); return widget.forget(); } namespace mozilla { namespace widget { static bool IsPopup(const nsWidgetInitData* aInitData) { return aInitData && aInitData->mWindowType == eWindowType_popup; } static bool MightNeedIMEFocus(const nsWidgetInitData* aInitData) { // In the puppet-widget world, popup widgets are just dummies and // shouldn't try to mess with IME state. return !IsPopup(aInitData); } // Arbitrary, fungible. const size_t PuppetWidget::kMaxDimension = 4000; NS_IMPL_ISUPPORTS_INHERITED1(PuppetWidget, nsBaseWidget, nsISupportsWeakReference) PuppetWidget::PuppetWidget(PBrowserChild *aTabChild) : mTabChild(aTabChild) , mDPI(-1) { MOZ_COUNT_CTOR(PuppetWidget); } PuppetWidget::~PuppetWidget() { MOZ_COUNT_DTOR(PuppetWidget); } NS_IMETHODIMP PuppetWidget::Create(nsIWidget *aParent, nsNativeWidget aNativeParent, const nsIntRect &aRect, EVENT_CALLBACK aHandleEventFunction, nsDeviceContext *aContext, nsWidgetInitData *aInitData) { NS_ABORT_IF_FALSE(!aNativeParent, "got a non-Puppet native parent"); BaseCreate(nsnull, aRect, aHandleEventFunction, aContext, aInitData); mBounds = aRect; mEnabled = true; mVisible = true; mSurface = gfxPlatform::GetPlatform() ->CreateOffscreenSurface(gfxIntSize(1, 1), gfxASurface::ContentFromFormat(gfxASurface::ImageFormatARGB32)); mIMEComposing = false; if (MightNeedIMEFocus(aInitData)) { PRUint32 chromeSeqno; mTabChild->SendNotifyIMEFocus(false, &mIMEPreference, &chromeSeqno); mIMELastBlurSeqno = mIMELastReceivedSeqno = chromeSeqno; } PuppetWidget* parent = static_cast(aParent); if (parent) { parent->SetChild(this); mLayerManager = parent->GetLayerManager(); } else { Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false); } return NS_OK; } already_AddRefed PuppetWidget::CreateChild(const nsIntRect &aRect, EVENT_CALLBACK aHandleEventFunction, nsDeviceContext *aContext, nsWidgetInitData *aInitData, bool aForceUseIWidgetParent) { bool isPopup = IsPopup(aInitData); nsCOMPtr widget = nsIWidget::CreatePuppetWidget(mTabChild); return ((widget && NS_SUCCEEDED(widget->Create(isPopup ? nsnull: this, nsnull, aRect, aHandleEventFunction, aContext, aInitData))) ? widget.forget() : nsnull); } NS_IMETHODIMP PuppetWidget::Destroy() { Base::OnDestroy(); Base::Destroy(); mPaintTask.Revoke(); mChild = nsnull; if (mLayerManager) { mLayerManager->Destroy(); } mLayerManager = nsnull; mTabChild = nsnull; return NS_OK; } NS_IMETHODIMP PuppetWidget::Show(bool aState) { NS_ASSERTION(mEnabled, "does it make sense to Show()/Hide() a disabled widget?"); bool wasVisible = mVisible; mVisible = aState; if (!wasVisible && mVisible) { Resize(mBounds.width, mBounds.height, false); } return NS_OK; } NS_IMETHODIMP PuppetWidget::Resize(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint) { nsIntRect oldBounds = mBounds; mBounds.SizeTo(nsIntSize(aWidth, aHeight)); if (mChild) { return mChild->Resize(aWidth, aHeight, aRepaint); } // XXX: roc says that |aRepaint| dictates whether or not to // invalidate the expanded area if (oldBounds.Size() < mBounds.Size() && aRepaint) { nsIntRegion dirty(mBounds); dirty.Sub(dirty, oldBounds); InvalidateRegion(this, dirty); } if (!oldBounds.IsEqualEdges(mBounds)) { DispatchResizeEvent(); } return NS_OK; } NS_IMETHODIMP PuppetWidget::SetFocus(bool aRaise) { // XXX/cjones: someone who knows about event handling needs to // decide how this should work. return NS_OK; } NS_IMETHODIMP PuppetWidget::Invalidate(const nsIntRect& aRect) { #ifdef DEBUG debug_DumpInvalidate(stderr, this, &aRect, nsCAutoString("PuppetWidget"), nsnull); #endif if (mChild) { return mChild->Invalidate(aRect); } mDirtyRegion.Or(mDirtyRegion, aRect); if (!mDirtyRegion.IsEmpty() && !mPaintTask.IsPending()) { mPaintTask = new PaintTask(this); return NS_DispatchToCurrentThread(mPaintTask.get()); } return NS_OK; } void PuppetWidget::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint) { if (nsnull == aPoint) { event.refPoint.x = 0; event.refPoint.y = 0; } else { // use the point override if provided event.refPoint.x = aPoint->x; event.refPoint.y = aPoint->y; } event.time = PR_Now() / 1000; } NS_IMETHODIMP PuppetWidget::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus) { #ifdef DEBUG debug_DumpEvent(stdout, event->widget, event, nsCAutoString("PuppetWidget"), nsnull); #endif NS_ABORT_IF_FALSE(!mChild || mChild->mWindowType == eWindowType_popup, "Unexpected event dispatch!"); aStatus = nsEventStatus_eIgnore; NS_ABORT_IF_FALSE(mViewCallback, "No view callback!"); if (event->message == NS_COMPOSITION_START) { mIMEComposing = true; } switch (event->eventStructType) { case NS_COMPOSITION_EVENT: mIMELastReceivedSeqno = static_cast(event)->seqno; if (mIMELastReceivedSeqno < mIMELastBlurSeqno) return NS_OK; break; case NS_TEXT_EVENT: mIMELastReceivedSeqno = static_cast(event)->seqno; if (mIMELastReceivedSeqno < mIMELastBlurSeqno) return NS_OK; break; case NS_SELECTION_EVENT: mIMELastReceivedSeqno = static_cast(event)->seqno; if (mIMELastReceivedSeqno < mIMELastBlurSeqno) return NS_OK; break; } aStatus = (*mViewCallback)(event); if (event->message == NS_COMPOSITION_END) { mIMEComposing = false; } return NS_OK; } LayerManager* PuppetWidget::GetLayerManager(PLayersChild* aShadowManager, LayersBackend aBackendHint, LayerManagerPersistence aPersistence, bool* aAllowRetaining) { if (!mLayerManager) { // The backend hint is a temporary placeholder until Azure, when // all content-process layer managers will be BasicLayerManagers. #if defined(MOZ_ENABLE_D3D10_LAYER) if (LayerManager::LAYERS_D3D10 == aBackendHint) { nsRefPtr m = new LayerManagerD3D10(this); m->AsShadowForwarder()->SetShadowManager(aShadowManager); if (m->Initialize()) { mLayerManager = m; } } #endif if (!mLayerManager) { mLayerManager = new BasicShadowLayerManager(this); mLayerManager->AsShadowForwarder()->SetShadowManager(aShadowManager); } } if (aAllowRetaining) { *aAllowRetaining = true; } return mLayerManager; } gfxASurface* PuppetWidget::GetThebesSurface() { return mSurface; } nsresult PuppetWidget::IMEEndComposition(bool aCancel) { nsEventStatus status; nsTextEvent textEvent(true, NS_TEXT_TEXT, this); InitEvent(textEvent, nsnull); textEvent.seqno = mIMELastReceivedSeqno; // SendEndIMEComposition is always called since ResetInputState // should always be called even if we aren't composing something. if (!mTabChild || !mTabChild->SendEndIMEComposition(aCancel, &textEvent.theText)) { return NS_ERROR_FAILURE; } if (!mIMEComposing) return NS_OK; DispatchEvent(&textEvent, status); nsCompositionEvent compEvent(true, NS_COMPOSITION_END, this); InitEvent(compEvent, nsnull); compEvent.seqno = mIMELastReceivedSeqno; DispatchEvent(&compEvent, status); return NS_OK; } NS_IMETHODIMP PuppetWidget::ResetInputState() { return IMEEndComposition(false); } NS_IMETHODIMP PuppetWidget::CancelComposition() { return IMEEndComposition(true); } NS_IMETHODIMP_(void) PuppetWidget::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { if (!mTabChild) { return; } mTabChild->SendSetInputContext( static_cast(aContext.mIMEState.mEnabled), static_cast(aContext.mIMEState.mOpen), aContext.mHTMLInputType, aContext.mActionHint, static_cast(aAction.mCause), static_cast(aAction.mFocusChange)); } NS_IMETHODIMP_(InputContext) PuppetWidget::GetInputContext() { InputContext context; if (mTabChild) { PRInt32 enabled, open; mTabChild->SendGetInputContext(&enabled, &open); context.mIMEState.mEnabled = static_cast(enabled); context.mIMEState.mOpen = static_cast(open); } return context; } NS_IMETHODIMP PuppetWidget::OnIMEFocusChange(bool aFocus) { if (!mTabChild) return NS_ERROR_FAILURE; if (aFocus) { nsEventStatus status; nsQueryContentEvent queryEvent(true, NS_QUERY_TEXT_CONTENT, this); InitEvent(queryEvent, nsnull); // Query entire content queryEvent.InitForQueryTextContent(0, PR_UINT32_MAX); DispatchEvent(&queryEvent, status); if (queryEvent.mSucceeded) { mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString); } } else { // ResetInputState might not have been called yet ResetInputState(); } PRUint32 chromeSeqno; mIMEPreference.mWantUpdates = false; mIMEPreference.mWantHints = false; if (!mTabChild->SendNotifyIMEFocus(aFocus, &mIMEPreference, &chromeSeqno)) return NS_ERROR_FAILURE; if (aFocus) { if (!mIMEPreference.mWantUpdates && !mIMEPreference.mWantHints) // call OnIMEFocusChange on blur but no other updates return NS_SUCCESS_IME_NO_UPDATES; OnIMESelectionChange(); // Update selection } else { mIMELastBlurSeqno = chromeSeqno; } return NS_OK; } NS_IMETHODIMP PuppetWidget::OnIMETextChange(PRUint32 aStart, PRUint32 aEnd, PRUint32 aNewEnd) { if (!mTabChild) return NS_ERROR_FAILURE; if (mIMEPreference.mWantHints) { nsEventStatus status; nsQueryContentEvent queryEvent(true, NS_QUERY_TEXT_CONTENT, this); InitEvent(queryEvent, nsnull); queryEvent.InitForQueryTextContent(0, PR_UINT32_MAX); DispatchEvent(&queryEvent, status); if (queryEvent.mSucceeded) { mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString); } } if (mIMEPreference.mWantUpdates) { mTabChild->SendNotifyIMETextChange(aStart, aEnd, aNewEnd); } return NS_OK; } NS_IMETHODIMP PuppetWidget::OnIMESelectionChange(void) { if (!mTabChild) return NS_ERROR_FAILURE; if (mIMEPreference.mWantUpdates) { nsEventStatus status; nsQueryContentEvent queryEvent(true, NS_QUERY_SELECTED_TEXT, this); InitEvent(queryEvent, nsnull); DispatchEvent(&queryEvent, status); if (queryEvent.mSucceeded) { mTabChild->SendNotifyIMESelection(mIMELastReceivedSeqno, queryEvent.GetSelectionStart(), queryEvent.GetSelectionEnd()); } } return NS_OK; } NS_IMETHODIMP PuppetWidget::SetCursor(nsCursor aCursor) { if (!mTabChild || !mTabChild->SendSetCursor(aCursor)) { return NS_ERROR_FAILURE; } return NS_OK; } nsresult PuppetWidget::DispatchPaintEvent() { NS_ABORT_IF_FALSE(!mDirtyRegion.IsEmpty(), "paint event logic messed up"); nsIntRect dirtyRect = mDirtyRegion.GetBounds(); nsPaintEvent event(true, NS_PAINT, this); event.refPoint.x = dirtyRect.x; event.refPoint.y = dirtyRect.y; event.region = mDirtyRegion; event.willSendDidPaint = true; // reset repaint tracking mDirtyRegion.SetEmpty(); mPaintTask.Revoke(); nsEventStatus status; { #ifdef DEBUG debug_DumpPaintEvent(stderr, this, &event, nsCAutoString("PuppetWidget"), nsnull); #endif if (LayerManager::LAYERS_D3D10 == mLayerManager->GetBackendType()) { DispatchEvent(&event, status); } else { nsRefPtr ctx = new gfxContext(mSurface); AutoLayerManagerSetup setupLayerManager(this, ctx, BasicLayerManager::BUFFER_NONE); DispatchEvent(&event, status); } } nsPaintEvent didPaintEvent(true, NS_DID_PAINT, this); DispatchEvent(&didPaintEvent, status); return NS_OK; } nsresult PuppetWidget::DispatchResizeEvent() { nsSizeEvent event(true, NS_SIZE, this); nsIntRect rect = mBounds; // copy in case something messes with it event.windowSize = ▭ event.refPoint.x = rect.x; event.refPoint.y = rect.y; event.mWinWidth = rect.width; event.mWinHeight = rect.height; nsEventStatus status; return DispatchEvent(&event, status); } void PuppetWidget::SetChild(PuppetWidget* aChild) { NS_ABORT_IF_FALSE(this != aChild, "can't parent a widget to itself"); NS_ABORT_IF_FALSE(!aChild->mChild, "fake widget 'hierarchy' only expected to have one level"); mChild = aChild; } NS_IMETHODIMP PuppetWidget::PaintTask::Run() { if (mWidget) { mWidget->DispatchPaintEvent(); } return NS_OK; } float PuppetWidget::GetDPI() { if (mDPI < 0) { NS_ABORT_IF_FALSE(mTabChild, "Need TabChild to get the DPI from!"); mTabChild->SendGetDPI(&mDPI); } return mDPI; } void* PuppetWidget::GetNativeData(PRUint32 aDataType) { switch (aDataType) { case NS_NATIVE_SHAREABLE_WINDOW: { NS_ABORT_IF_FALSE(mTabChild, "Need TabChild to get the nativeWindow from!"); mozilla::WindowsHandle nativeData = nsnull; mTabChild->SendGetWidgetNativeData(&nativeData); return (void*)nativeData; } case NS_NATIVE_WINDOW: case NS_NATIVE_DISPLAY: case NS_NATIVE_PLUGIN_PORT: case NS_NATIVE_GRAPHIC: case NS_NATIVE_SHELLWIDGET: case NS_NATIVE_WIDGET: NS_WARNING("nsWindow::GetNativeData not implemented for this type"); break; default: NS_WARNING("nsWindow::GetNativeData called with bad value"); break; } return nsnull; } } // namespace widget } // namespace mozilla