Bug 834760: Null-check mAttachedWidgetListener. r=mwu a=blocking-b2g
[gecko.git] / widget / xpwidgets / PuppetWidget.cpp
bloba5088041b96b67386399bf3f4c301e41065ed677
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: sw=2 ts=8 et :
3 */
4 /* This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 #include "base/basictypes.h"
10 #include "BasicLayers.h"
11 #include "gfxPlatform.h"
12 #if defined(MOZ_ENABLE_D3D10_LAYER)
13 # include "LayerManagerD3D10.h"
14 #endif
15 #include "mozilla/dom/TabChild.h"
16 #include "mozilla/Hal.h"
17 #include "mozilla/layers/CompositorChild.h"
18 #include "mozilla/layers/PLayersChild.h"
19 #include "PuppetWidget.h"
20 #include "nsIWidgetListener.h"
22 using namespace mozilla::dom;
23 using namespace mozilla::hal;
24 using namespace mozilla::layers;
25 using namespace mozilla::widget;
27 static void
28 InvalidateRegion(nsIWidget* aWidget, const nsIntRegion& aRegion)
30 nsIntRegionRectIterator it(aRegion);
31 while(const nsIntRect* r = it.Next()) {
32 aWidget->Invalidate(*r);
36 /*static*/ already_AddRefed<nsIWidget>
37 nsIWidget::CreatePuppetWidget(TabChild* aTabChild)
39 NS_ABORT_IF_FALSE(nsIWidget::UsePuppetWidgets(),
40 "PuppetWidgets not allowed in this configuration");
42 nsCOMPtr<nsIWidget> widget = new PuppetWidget(aTabChild);
43 return widget.forget();
46 namespace mozilla {
47 namespace widget {
49 static bool
50 IsPopup(const nsWidgetInitData* aInitData)
52 return aInitData && aInitData->mWindowType == eWindowType_popup;
55 static bool
56 MightNeedIMEFocus(const nsWidgetInitData* aInitData)
58 // In the puppet-widget world, popup widgets are just dummies and
59 // shouldn't try to mess with IME state.
60 return !IsPopup(aInitData);
64 // Arbitrary, fungible.
65 const size_t PuppetWidget::kMaxDimension = 4000;
67 NS_IMPL_ISUPPORTS_INHERITED1(PuppetWidget, nsBaseWidget,
68 nsISupportsWeakReference)
70 PuppetWidget::PuppetWidget(TabChild* aTabChild)
71 : mTabChild(aTabChild)
72 , mDPI(-1)
74 MOZ_COUNT_CTOR(PuppetWidget);
77 PuppetWidget::~PuppetWidget()
79 MOZ_COUNT_DTOR(PuppetWidget);
82 NS_IMETHODIMP
83 PuppetWidget::Create(nsIWidget *aParent,
84 nsNativeWidget aNativeParent,
85 const nsIntRect &aRect,
86 nsDeviceContext *aContext,
87 nsWidgetInitData *aInitData)
89 NS_ABORT_IF_FALSE(!aNativeParent, "got a non-Puppet native parent");
91 BaseCreate(nullptr, aRect, aContext, aInitData);
93 mBounds = aRect;
94 mEnabled = true;
95 mVisible = true;
97 mSurface = gfxPlatform::GetPlatform()
98 ->CreateOffscreenSurface(gfxIntSize(1, 1),
99 gfxASurface::ContentFromFormat(gfxASurface::ImageFormatARGB32));
101 mIMEComposing = false;
102 mNeedIMEStateInit = MightNeedIMEFocus(aInitData);
104 PuppetWidget* parent = static_cast<PuppetWidget*>(aParent);
105 if (parent) {
106 parent->SetChild(this);
107 mLayerManager = parent->GetLayerManager();
109 else {
110 Resize(mBounds.x, mBounds.y, mBounds.width, mBounds.height, false);
113 return NS_OK;
116 void
117 PuppetWidget::InitIMEState()
119 if (mNeedIMEStateInit) {
120 uint32_t chromeSeqno;
121 mTabChild->SendNotifyIMEFocus(false, &mIMEPreference, &chromeSeqno);
122 mIMELastBlurSeqno = mIMELastReceivedSeqno = chromeSeqno;
123 mNeedIMEStateInit = false;
127 already_AddRefed<nsIWidget>
128 PuppetWidget::CreateChild(const nsIntRect &aRect,
129 nsDeviceContext *aContext,
130 nsWidgetInitData *aInitData,
131 bool aForceUseIWidgetParent)
133 bool isPopup = IsPopup(aInitData);
134 nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(mTabChild);
135 return ((widget &&
136 NS_SUCCEEDED(widget->Create(isPopup ? nullptr: this, nullptr, aRect,
137 aContext, aInitData))) ?
138 widget.forget() : nullptr);
141 NS_IMETHODIMP
142 PuppetWidget::Destroy()
144 Base::OnDestroy();
145 Base::Destroy();
146 mPaintTask.Revoke();
147 mChild = nullptr;
148 if (mLayerManager) {
149 mLayerManager->Destroy();
151 mLayerManager = nullptr;
152 mTabChild = nullptr;
153 return NS_OK;
156 NS_IMETHODIMP
157 PuppetWidget::Show(bool aState)
159 NS_ASSERTION(mEnabled,
160 "does it make sense to Show()/Hide() a disabled widget?");
162 bool wasVisible = mVisible;
163 mVisible = aState;
165 if (mChild) {
166 mChild->mVisible = aState;
169 if (!mVisible && mLayerManager) {
170 mLayerManager->ClearCachedResources();
173 if (!wasVisible && mVisible) {
174 Resize(mBounds.width, mBounds.height, false);
175 Invalidate(mBounds);
178 return NS_OK;
181 NS_IMETHODIMP
182 PuppetWidget::Resize(int32_t aWidth,
183 int32_t aHeight,
184 bool aRepaint)
186 nsIntRect oldBounds = mBounds;
187 mBounds.SizeTo(nsIntSize(aWidth, aHeight));
189 if (mChild) {
190 return mChild->Resize(aWidth, aHeight, aRepaint);
193 // XXX: roc says that |aRepaint| dictates whether or not to
194 // invalidate the expanded area
195 if (oldBounds.Size() < mBounds.Size() && aRepaint) {
196 nsIntRegion dirty(mBounds);
197 dirty.Sub(dirty, oldBounds);
198 InvalidateRegion(this, dirty);
201 if (!oldBounds.IsEqualEdges(mBounds) && mAttachedWidgetListener) {
202 mAttachedWidgetListener->WindowResized(this, mBounds.width, mBounds.height);
205 return NS_OK;
208 NS_IMETHODIMP
209 PuppetWidget::SetFocus(bool aRaise)
211 // XXX/cjones: someone who knows about event handling needs to
212 // decide how this should work.
213 return NS_OK;
216 NS_IMETHODIMP
217 PuppetWidget::Invalidate(const nsIntRect& aRect)
219 #ifdef DEBUG
220 debug_DumpInvalidate(stderr, this, &aRect,
221 nsAutoCString("PuppetWidget"), 0);
222 #endif
224 if (mChild) {
225 return mChild->Invalidate(aRect);
228 mDirtyRegion.Or(mDirtyRegion, aRect);
230 if (!mDirtyRegion.IsEmpty() && !mPaintTask.IsPending()) {
231 mPaintTask = new PaintTask(this);
232 return NS_DispatchToCurrentThread(mPaintTask.get());
235 return NS_OK;
238 void
239 PuppetWidget::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint)
241 if (nullptr == aPoint) {
242 event.refPoint.x = 0;
243 event.refPoint.y = 0;
245 else {
246 // use the point override if provided
247 event.refPoint.x = aPoint->x;
248 event.refPoint.y = aPoint->y;
250 event.time = PR_Now() / 1000;
253 NS_IMETHODIMP
254 PuppetWidget::DispatchEvent(nsGUIEvent* event, nsEventStatus& aStatus)
256 #ifdef DEBUG
257 debug_DumpEvent(stdout, event->widget, event,
258 nsAutoCString("PuppetWidget"), 0);
259 #endif
261 NS_ABORT_IF_FALSE(!mChild || mChild->mWindowType == eWindowType_popup,
262 "Unexpected event dispatch!");
264 aStatus = nsEventStatus_eIgnore;
266 if (event->message == NS_COMPOSITION_START) {
267 mIMEComposing = true;
269 switch (event->eventStructType) {
270 case NS_COMPOSITION_EVENT:
271 mIMELastReceivedSeqno = static_cast<nsCompositionEvent*>(event)->seqno;
272 if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
273 return NS_OK;
274 break;
275 case NS_TEXT_EVENT:
276 mIMELastReceivedSeqno = static_cast<nsTextEvent*>(event)->seqno;
277 if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
278 return NS_OK;
279 break;
280 case NS_SELECTION_EVENT:
281 mIMELastReceivedSeqno = static_cast<nsSelectionEvent*>(event)->seqno;
282 if (mIMELastReceivedSeqno < mIMELastBlurSeqno)
283 return NS_OK;
284 break;
287 if (mAttachedWidgetListener) {
288 aStatus = mAttachedWidgetListener->HandleEvent(event, mUseAttachedEvents);
291 if (event->message == NS_COMPOSITION_END) {
292 mIMEComposing = false;
295 return NS_OK;
298 LayerManager*
299 PuppetWidget::GetLayerManager(PLayersChild* aShadowManager,
300 LayersBackend aBackendHint,
301 LayerManagerPersistence aPersistence,
302 bool* aAllowRetaining)
304 if (!mLayerManager) {
305 // The backend hint is a temporary placeholder until Azure, when
306 // all content-process layer managers will be BasicLayerManagers.
307 #if defined(MOZ_ENABLE_D3D10_LAYER)
308 if (mozilla::layers::LAYERS_D3D10 == aBackendHint) {
309 nsRefPtr<LayerManagerD3D10> m = new LayerManagerD3D10(this);
310 m->AsShadowForwarder()->SetShadowManager(aShadowManager);
311 if (m->Initialize()) {
312 mLayerManager = m;
315 #endif
316 if (!mLayerManager) {
317 mLayerManager = new BasicShadowLayerManager(this);
318 mLayerManager->AsShadowForwarder()->SetShadowManager(aShadowManager);
321 if (aAllowRetaining) {
322 *aAllowRetaining = true;
324 return mLayerManager;
327 gfxASurface*
328 PuppetWidget::GetThebesSurface()
330 return mSurface;
333 nsresult
334 PuppetWidget::IMEEndComposition(bool aCancel)
336 nsEventStatus status;
337 nsTextEvent textEvent(true, NS_TEXT_TEXT, this);
338 InitEvent(textEvent, nullptr);
339 textEvent.seqno = mIMELastReceivedSeqno;
340 // SendEndIMEComposition is always called since ResetInputState
341 // should always be called even if we aren't composing something.
342 if (!mTabChild ||
343 !mTabChild->SendEndIMEComposition(aCancel, &textEvent.theText)) {
344 return NS_ERROR_FAILURE;
347 if (!mIMEComposing)
348 return NS_OK;
350 DispatchEvent(&textEvent, status);
352 nsCompositionEvent compEvent(true, NS_COMPOSITION_END, this);
353 InitEvent(compEvent, nullptr);
354 compEvent.seqno = mIMELastReceivedSeqno;
355 DispatchEvent(&compEvent, status);
356 return NS_OK;
359 NS_IMETHODIMP
360 PuppetWidget::ResetInputState()
362 return IMEEndComposition(false);
365 NS_IMETHODIMP
366 PuppetWidget::CancelComposition()
368 return IMEEndComposition(true);
371 NS_IMETHODIMP_(void)
372 PuppetWidget::SetInputContext(const InputContext& aContext,
373 const InputContextAction& aAction)
375 if (!mTabChild) {
376 return;
378 mTabChild->SendSetInputContext(
379 static_cast<int32_t>(aContext.mIMEState.mEnabled),
380 static_cast<int32_t>(aContext.mIMEState.mOpen),
381 aContext.mHTMLInputType,
382 aContext.mHTMLInputInputmode,
383 aContext.mActionHint,
384 static_cast<int32_t>(aAction.mCause),
385 static_cast<int32_t>(aAction.mFocusChange));
388 NS_IMETHODIMP_(InputContext)
389 PuppetWidget::GetInputContext()
391 InputContext context;
392 if (mTabChild) {
393 int32_t enabled, open;
394 mTabChild->SendGetInputContext(&enabled, &open);
395 context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(enabled);
396 context.mIMEState.mOpen = static_cast<IMEState::Open>(open);
398 return context;
401 NS_IMETHODIMP
402 PuppetWidget::OnIMEFocusChange(bool aFocus)
404 if (!mTabChild)
405 return NS_ERROR_FAILURE;
407 if (aFocus) {
408 nsEventStatus status;
409 nsQueryContentEvent queryEvent(true, NS_QUERY_TEXT_CONTENT, this);
410 InitEvent(queryEvent, nullptr);
411 // Query entire content
412 queryEvent.InitForQueryTextContent(0, UINT32_MAX);
413 DispatchEvent(&queryEvent, status);
415 if (queryEvent.mSucceeded) {
416 mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
418 } else {
419 // ResetInputState might not have been called yet
420 ResetInputState();
423 uint32_t chromeSeqno;
424 mIMEPreference.mWantUpdates = false;
425 mIMEPreference.mWantHints = false;
426 if (!mTabChild->SendNotifyIMEFocus(aFocus, &mIMEPreference, &chromeSeqno))
427 return NS_ERROR_FAILURE;
429 if (aFocus) {
430 if (!mIMEPreference.mWantUpdates && !mIMEPreference.mWantHints)
431 // call OnIMEFocusChange on blur but no other updates
432 return NS_SUCCESS_IME_NO_UPDATES;
433 OnIMESelectionChange(); // Update selection
434 } else {
435 mIMELastBlurSeqno = chromeSeqno;
437 return NS_OK;
440 NS_IMETHODIMP
441 PuppetWidget::OnIMETextChange(uint32_t aStart, uint32_t aEnd, uint32_t aNewEnd)
443 if (!mTabChild)
444 return NS_ERROR_FAILURE;
446 if (mIMEPreference.mWantHints) {
447 nsEventStatus status;
448 nsQueryContentEvent queryEvent(true, NS_QUERY_TEXT_CONTENT, this);
449 InitEvent(queryEvent, nullptr);
450 queryEvent.InitForQueryTextContent(0, UINT32_MAX);
451 DispatchEvent(&queryEvent, status);
453 if (queryEvent.mSucceeded) {
454 mTabChild->SendNotifyIMETextHint(queryEvent.mReply.mString);
457 if (mIMEPreference.mWantUpdates) {
458 mTabChild->SendNotifyIMETextChange(aStart, aEnd, aNewEnd);
460 return NS_OK;
463 NS_IMETHODIMP
464 PuppetWidget::OnIMESelectionChange(void)
466 if (!mTabChild)
467 return NS_ERROR_FAILURE;
469 if (mIMEPreference.mWantUpdates) {
470 nsEventStatus status;
471 nsQueryContentEvent queryEvent(true, NS_QUERY_SELECTED_TEXT, this);
472 InitEvent(queryEvent, nullptr);
473 DispatchEvent(&queryEvent, status);
475 if (queryEvent.mSucceeded) {
476 mTabChild->SendNotifyIMESelection(mIMELastReceivedSeqno,
477 queryEvent.GetSelectionStart(),
478 queryEvent.GetSelectionEnd());
481 return NS_OK;
484 NS_IMETHODIMP
485 PuppetWidget::SetCursor(nsCursor aCursor)
487 if (mCursor == aCursor) {
488 return NS_OK;
491 if (!mTabChild ||
492 !mTabChild->SendSetCursor(aCursor)) {
493 return NS_ERROR_FAILURE;
496 mCursor = aCursor;
498 return NS_OK;
501 nsresult
502 PuppetWidget::Paint()
504 NS_ABORT_IF_FALSE(!mDirtyRegion.IsEmpty(), "paint event logic messed up");
506 if (!mAttachedWidgetListener)
507 return NS_OK;
509 nsIntRegion region = mDirtyRegion;
511 // reset repaint tracking
512 mDirtyRegion.SetEmpty();
513 mPaintTask.Revoke();
516 #ifdef DEBUG
517 debug_DumpPaintEvent(stderr, this, region,
518 nsAutoCString("PuppetWidget"), 0);
519 #endif
521 if (mozilla::layers::LAYERS_D3D10 == mLayerManager->GetBackendType()) {
522 mAttachedWidgetListener->PaintWindow(this, region, false, true);
523 } else {
524 nsRefPtr<gfxContext> ctx = new gfxContext(mSurface);
525 ctx->Rectangle(gfxRect(0,0,0,0));
526 ctx->Clip();
527 AutoLayerManagerSetup setupLayerManager(this, ctx,
528 BUFFER_NONE);
529 mAttachedWidgetListener->PaintWindow(this, region, false, true);
530 mTabChild->NotifyPainted();
534 if (mAttachedWidgetListener) {
535 mAttachedWidgetListener->DidPaintWindow();
538 return NS_OK;
541 void
542 PuppetWidget::SetChild(PuppetWidget* aChild)
544 NS_ABORT_IF_FALSE(this != aChild, "can't parent a widget to itself");
545 NS_ABORT_IF_FALSE(!aChild->mChild,
546 "fake widget 'hierarchy' only expected to have one level");
548 mChild = aChild;
551 NS_IMETHODIMP
552 PuppetWidget::PaintTask::Run()
554 if (mWidget) {
555 mWidget->Paint();
557 return NS_OK;
560 bool
561 PuppetWidget::NeedsPaint()
563 return mVisible;
566 float
567 PuppetWidget::GetDPI()
569 if (mDPI < 0) {
570 NS_ABORT_IF_FALSE(mTabChild, "Need TabChild to get the DPI from!");
571 mTabChild->GetDPI(&mDPI);
574 return mDPI;
577 void*
578 PuppetWidget::GetNativeData(uint32_t aDataType)
580 switch (aDataType) {
581 case NS_NATIVE_SHAREABLE_WINDOW: {
582 NS_ABORT_IF_FALSE(mTabChild, "Need TabChild to get the nativeWindow from!");
583 mozilla::WindowsHandle nativeData = 0;
584 mTabChild->SendGetWidgetNativeData(&nativeData);
585 return (void*)nativeData;
587 case NS_NATIVE_WINDOW:
588 case NS_NATIVE_DISPLAY:
589 case NS_NATIVE_PLUGIN_PORT:
590 case NS_NATIVE_GRAPHIC:
591 case NS_NATIVE_SHELLWIDGET:
592 case NS_NATIVE_WIDGET:
593 NS_WARNING("nsWindow::GetNativeData not implemented for this type");
594 break;
595 default:
596 NS_WARNING("nsWindow::GetNativeData called with bad value");
597 break;
599 return nullptr;
602 PuppetScreen::PuppetScreen(void *nativeScreen)
606 PuppetScreen::~PuppetScreen()
610 static ScreenConfiguration
611 ScreenConfig()
613 ScreenConfiguration config;
614 hal::GetCurrentScreenConfiguration(&config);
615 return config;
618 NS_IMETHODIMP
619 PuppetScreen::GetRect(int32_t *outLeft, int32_t *outTop,
620 int32_t *outWidth, int32_t *outHeight)
622 nsIntRect r = ScreenConfig().rect();
623 *outLeft = r.x;
624 *outTop = r.y;
625 *outWidth = r.width;
626 *outHeight = r.height;
627 return NS_OK;
630 NS_IMETHODIMP
631 PuppetScreen::GetAvailRect(int32_t *outLeft, int32_t *outTop,
632 int32_t *outWidth, int32_t *outHeight)
634 return GetRect(outLeft, outTop, outWidth, outHeight);
638 NS_IMETHODIMP
639 PuppetScreen::GetPixelDepth(int32_t *aPixelDepth)
641 *aPixelDepth = ScreenConfig().pixelDepth();
642 return NS_OK;
645 NS_IMETHODIMP
646 PuppetScreen::GetColorDepth(int32_t *aColorDepth)
648 *aColorDepth = ScreenConfig().colorDepth();
649 return NS_OK;
652 NS_IMETHODIMP
653 PuppetScreen::GetRotation(uint32_t* aRotation)
655 NS_WARNING("Attempt to get screen rotation through nsIScreen::GetRotation(). Nothing should know or care this in sandboxed contexts. If you want *orientation*, use hal.");
656 return NS_ERROR_NOT_AVAILABLE;
659 NS_IMETHODIMP
660 PuppetScreen::SetRotation(uint32_t aRotation)
662 NS_WARNING("Attempt to set screen rotation through nsIScreen::GetRotation(). Nothing should know or care this in sandboxed contexts. If you want *orientation*, use hal.");
663 return NS_ERROR_NOT_AVAILABLE;
666 NS_IMPL_ISUPPORTS1(PuppetScreenManager, nsIScreenManager)
668 PuppetScreenManager::PuppetScreenManager()
670 mOneScreen = new PuppetScreen(nullptr);
673 PuppetScreenManager::~PuppetScreenManager()
677 NS_IMETHODIMP
678 PuppetScreenManager::GetPrimaryScreen(nsIScreen** outScreen)
680 NS_IF_ADDREF(*outScreen = mOneScreen.get());
681 return NS_OK;
684 NS_IMETHODIMP
685 PuppetScreenManager::ScreenForRect(int32_t inLeft,
686 int32_t inTop,
687 int32_t inWidth,
688 int32_t inHeight,
689 nsIScreen** outScreen)
691 return GetPrimaryScreen(outScreen);
694 NS_IMETHODIMP
695 PuppetScreenManager::ScreenForNativeWidget(void* aWidget,
696 nsIScreen** outScreen)
698 return GetPrimaryScreen(outScreen);
701 NS_IMETHODIMP
702 PuppetScreenManager::GetNumberOfScreens(uint32_t* aNumberOfScreens)
704 *aNumberOfScreens = 1;
705 return NS_OK;
708 } // namespace widget
709 } // namespace mozilla