|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsView.h" |
|
7 |
|
8 #include "mozilla/Attributes.h" |
|
9 #include "mozilla/BasicEvents.h" |
|
10 #include "mozilla/DebugOnly.h" |
|
11 #include "mozilla/IntegerPrintfMacros.h" |
|
12 #include "mozilla/Likely.h" |
|
13 #include "mozilla/Poison.h" |
|
14 #include "nsIWidget.h" |
|
15 #include "nsViewManager.h" |
|
16 #include "nsIFrame.h" |
|
17 #include "nsPresArena.h" |
|
18 #include "nsXULPopupManager.h" |
|
19 #include "nsIWidgetListener.h" |
|
20 #include "nsContentUtils.h" // for nsAutoScriptBlocker |
|
21 |
|
22 using namespace mozilla; |
|
23 |
|
24 nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility) |
|
25 { |
|
26 MOZ_COUNT_CTOR(nsView); |
|
27 |
|
28 mVis = aVisibility; |
|
29 // Views should be transparent by default. Not being transparent is |
|
30 // a promise that the view will paint all its pixels opaquely. Views |
|
31 // should make this promise explicitly by calling |
|
32 // SetViewContentTransparency. |
|
33 mVFlags = 0; |
|
34 mViewManager = aViewManager; |
|
35 mDirtyRegion = nullptr; |
|
36 mWidgetIsTopLevel = false; |
|
37 } |
|
38 |
|
39 void nsView::DropMouseGrabbing() |
|
40 { |
|
41 nsIPresShell* presShell = mViewManager->GetPresShell(); |
|
42 if (presShell) |
|
43 presShell->ClearMouseCaptureOnView(this); |
|
44 } |
|
45 |
|
46 nsView::~nsView() |
|
47 { |
|
48 MOZ_COUNT_DTOR(nsView); |
|
49 |
|
50 while (GetFirstChild()) |
|
51 { |
|
52 nsView* child = GetFirstChild(); |
|
53 if (child->GetViewManager() == mViewManager) { |
|
54 child->Destroy(); |
|
55 } else { |
|
56 // just unhook it. Someone else will want to destroy this. |
|
57 RemoveChild(child); |
|
58 } |
|
59 } |
|
60 |
|
61 if (mViewManager) |
|
62 { |
|
63 DropMouseGrabbing(); |
|
64 |
|
65 nsView *rootView = mViewManager->GetRootView(); |
|
66 |
|
67 if (rootView) |
|
68 { |
|
69 // Root views can have parents! |
|
70 if (mParent) |
|
71 { |
|
72 mViewManager->RemoveChild(this); |
|
73 } |
|
74 |
|
75 if (rootView == this) |
|
76 { |
|
77 // Inform the view manager that the root view has gone away... |
|
78 mViewManager->SetRootView(nullptr); |
|
79 } |
|
80 } |
|
81 else if (mParent) |
|
82 { |
|
83 mParent->RemoveChild(this); |
|
84 } |
|
85 |
|
86 mViewManager = nullptr; |
|
87 } |
|
88 else if (mParent) |
|
89 { |
|
90 mParent->RemoveChild(this); |
|
91 } |
|
92 |
|
93 // Destroy and release the widget |
|
94 DestroyWidget(); |
|
95 |
|
96 delete mDirtyRegion; |
|
97 } |
|
98 |
|
99 class DestroyWidgetRunnable : public nsRunnable { |
|
100 public: |
|
101 NS_DECL_NSIRUNNABLE |
|
102 |
|
103 explicit DestroyWidgetRunnable(nsIWidget* aWidget) : mWidget(aWidget) {} |
|
104 |
|
105 private: |
|
106 nsCOMPtr<nsIWidget> mWidget; |
|
107 }; |
|
108 |
|
109 NS_IMETHODIMP DestroyWidgetRunnable::Run() |
|
110 { |
|
111 mWidget->Destroy(); |
|
112 mWidget = nullptr; |
|
113 return NS_OK; |
|
114 } |
|
115 |
|
116 |
|
117 void nsView::DestroyWidget() |
|
118 { |
|
119 if (mWindow) |
|
120 { |
|
121 // If we are not attached to a base window, we're going to tear down our |
|
122 // widget here. However, if we're attached to somebody elses widget, we |
|
123 // want to leave the widget alone: don't reset the client data or call |
|
124 // Destroy. Just clear our event view ptr and free our reference to it. |
|
125 if (mWidgetIsTopLevel) { |
|
126 mWindow->SetAttachedWidgetListener(nullptr); |
|
127 } |
|
128 else { |
|
129 mWindow->SetWidgetListener(nullptr); |
|
130 |
|
131 nsCOMPtr<nsIRunnable> widgetDestroyer = |
|
132 new DestroyWidgetRunnable(mWindow); |
|
133 |
|
134 NS_DispatchToMainThread(widgetDestroyer); |
|
135 } |
|
136 |
|
137 NS_RELEASE(mWindow); |
|
138 } |
|
139 } |
|
140 |
|
141 nsView* nsView::GetViewFor(nsIWidget* aWidget) |
|
142 { |
|
143 NS_PRECONDITION(nullptr != aWidget, "null widget ptr"); |
|
144 |
|
145 nsIWidgetListener* listener = aWidget->GetWidgetListener(); |
|
146 if (listener) { |
|
147 nsView* view = listener->GetView(); |
|
148 if (view) |
|
149 return view; |
|
150 } |
|
151 |
|
152 listener = aWidget->GetAttachedWidgetListener(); |
|
153 return listener ? listener->GetView() : nullptr; |
|
154 } |
|
155 |
|
156 void nsView::Destroy() |
|
157 { |
|
158 this->~nsView(); |
|
159 mozWritePoison(this, sizeof(*this)); |
|
160 nsView::operator delete(this); |
|
161 } |
|
162 |
|
163 void nsView::SetPosition(nscoord aX, nscoord aY) |
|
164 { |
|
165 mDimBounds.x += aX - mPosX; |
|
166 mDimBounds.y += aY - mPosY; |
|
167 mPosX = aX; |
|
168 mPosY = aY; |
|
169 |
|
170 NS_ASSERTION(GetParent() || (aX == 0 && aY == 0), |
|
171 "Don't try to move the root widget to something non-zero"); |
|
172 |
|
173 ResetWidgetBounds(true, false); |
|
174 } |
|
175 |
|
176 void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync) |
|
177 { |
|
178 if (mWindow) { |
|
179 if (!aForceSync) { |
|
180 // Don't change widget geometry synchronously, since that can |
|
181 // cause synchronous painting. |
|
182 mViewManager->PostPendingUpdate(); |
|
183 } else { |
|
184 DoResetWidgetBounds(false, true); |
|
185 } |
|
186 return; |
|
187 } |
|
188 |
|
189 if (aRecurse) { |
|
190 // reposition any widgets under this view |
|
191 for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) { |
|
192 v->ResetWidgetBounds(true, aForceSync); |
|
193 } |
|
194 } |
|
195 } |
|
196 |
|
197 bool nsView::IsEffectivelyVisible() |
|
198 { |
|
199 for (nsView* v = this; v; v = v->mParent) { |
|
200 if (v->GetVisibility() == nsViewVisibility_kHide) |
|
201 return false; |
|
202 } |
|
203 return true; |
|
204 } |
|
205 |
|
206 nsIntRect nsView::CalcWidgetBounds(nsWindowType aType) |
|
207 { |
|
208 int32_t p2a = mViewManager->AppUnitsPerDevPixel(); |
|
209 |
|
210 nsRect viewBounds(mDimBounds); |
|
211 |
|
212 nsView* parent = GetParent(); |
|
213 nsIWidget* parentWidget = nullptr; |
|
214 if (parent) { |
|
215 nsPoint offset; |
|
216 parentWidget = parent->GetNearestWidget(&offset, p2a); |
|
217 // make viewBounds be relative to the parent widget, in appunits |
|
218 viewBounds += offset; |
|
219 |
|
220 if (parentWidget && aType == eWindowType_popup && |
|
221 IsEffectivelyVisible()) { |
|
222 // put offset into screen coordinates. (based on client area origin) |
|
223 nsIntPoint screenPoint = parentWidget->WidgetToScreenOffset(); |
|
224 viewBounds += nsPoint(NSIntPixelsToAppUnits(screenPoint.x, p2a), |
|
225 NSIntPixelsToAppUnits(screenPoint.y, p2a)); |
|
226 } |
|
227 } |
|
228 |
|
229 // Compute widget bounds in device pixels |
|
230 nsIntRect newBounds = viewBounds.ToNearestPixels(p2a); |
|
231 |
|
232 #ifdef XP_MACOSX |
|
233 // cocoa rounds widget coordinates to the nearest global "display pixel" |
|
234 // integer value. So we avoid fractional display pixel values by rounding |
|
235 // to the nearest value that won't yield a fractional display pixel. |
|
236 nsIWidget* widget = parentWidget ? parentWidget : mWindow; |
|
237 uint32_t round; |
|
238 if (aType == eWindowType_popup && widget && |
|
239 ((round = widget->RoundsWidgetCoordinatesTo()) > 1)) { |
|
240 nsIntSize pixelRoundedSize = newBounds.Size(); |
|
241 // round the top left and bottom right to the nearest round pixel |
|
242 newBounds.x = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.x, p2a) / round) * round; |
|
243 newBounds.y = NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.y, p2a) / round) * round; |
|
244 newBounds.width = |
|
245 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.XMost(), p2a) / round) * round - newBounds.x; |
|
246 newBounds.height = |
|
247 NSToIntRoundUp(NSAppUnitsToDoublePixels(viewBounds.YMost(), p2a) / round) * round - newBounds.y; |
|
248 // but if that makes the widget larger then our frame may not paint the |
|
249 // extra pixels, so reduce the size to the nearest round value |
|
250 if (newBounds.width > pixelRoundedSize.width) { |
|
251 newBounds.width -= round; |
|
252 } |
|
253 if (newBounds.height > pixelRoundedSize.height) { |
|
254 newBounds.height -= round; |
|
255 } |
|
256 } |
|
257 #endif |
|
258 |
|
259 // Compute where the top-left of our widget ended up relative to the parent |
|
260 // widget, in appunits. |
|
261 nsPoint roundedOffset(NSIntPixelsToAppUnits(newBounds.x, p2a), |
|
262 NSIntPixelsToAppUnits(newBounds.y, p2a)); |
|
263 |
|
264 // mViewToWidgetOffset is added to coordinates relative to the view origin |
|
265 // to get coordinates relative to the widget. |
|
266 // The view origin, relative to the parent widget, is at |
|
267 // (mPosX,mPosY) - mDimBounds.TopLeft() + viewBounds.TopLeft(). |
|
268 // Our widget, relative to the parent widget, is roundedOffset. |
|
269 mViewToWidgetOffset = nsPoint(mPosX, mPosY) |
|
270 - mDimBounds.TopLeft() + viewBounds.TopLeft() - roundedOffset; |
|
271 |
|
272 return newBounds; |
|
273 } |
|
274 |
|
275 void nsView::DoResetWidgetBounds(bool aMoveOnly, |
|
276 bool aInvalidateChangedSize) { |
|
277 // The geometry of a root view's widget is controlled externally, |
|
278 // NOT by sizing or positioning the view |
|
279 if (mViewManager->GetRootView() == this) { |
|
280 return; |
|
281 } |
|
282 |
|
283 NS_PRECONDITION(mWindow, "Why was this called??"); |
|
284 |
|
285 // Hold this ref to make sure it stays alive. |
|
286 nsCOMPtr<nsIWidget> widget = mWindow; |
|
287 |
|
288 // Stash a copy of these and use them so we can handle this being deleted (say |
|
289 // from sync painting/flushing from Show/Move/Resize on the widget). |
|
290 nsIntRect newBounds; |
|
291 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); |
|
292 |
|
293 nsWindowType type = widget->WindowType(); |
|
294 |
|
295 nsIntRect curBounds; |
|
296 widget->GetClientBounds(curBounds); |
|
297 bool invisiblePopup = type == eWindowType_popup && |
|
298 ((curBounds.IsEmpty() && mDimBounds.IsEmpty()) || |
|
299 mVis == nsViewVisibility_kHide); |
|
300 |
|
301 if (invisiblePopup) { |
|
302 // We're going to hit the early exit below, avoid calling CalcWidgetBounds. |
|
303 } else { |
|
304 newBounds = CalcWidgetBounds(type); |
|
305 } |
|
306 |
|
307 bool curVisibility = widget->IsVisible(); |
|
308 bool newVisibility = IsEffectivelyVisible(); |
|
309 if (curVisibility && !newVisibility) { |
|
310 widget->Show(false); |
|
311 } |
|
312 |
|
313 if (invisiblePopup) { |
|
314 // Don't manipulate empty or hidden popup widgets. For example there's no |
|
315 // point moving hidden comboboxes around, or doing X server roundtrips |
|
316 // to compute their true screen position. This could mean that WidgetToScreen |
|
317 // operations on these widgets don't return up-to-date values, but popup |
|
318 // positions aren't reliable anyway because of correction to be on or off-screen. |
|
319 return; |
|
320 } |
|
321 |
|
322 bool changedPos = curBounds.TopLeft() != newBounds.TopLeft(); |
|
323 bool changedSize = curBounds.Size() != newBounds.Size(); |
|
324 |
|
325 // Child views are never attached to top level widgets, this is safe. |
|
326 |
|
327 // Coordinates are converted to display pixels for window Move/Resize APIs, |
|
328 // because of the potential for device-pixel coordinate spaces for mixed |
|
329 // hidpi/lodpi screens to overlap each other and result in bad placement |
|
330 // (bug 814434). |
|
331 double invScale; |
|
332 |
|
333 // Bug 861270: for correct widget manipulation at arbitrary scale factors, |
|
334 // prefer to base scaling on widget->GetDefaultScale(). But only do this if |
|
335 // it matches the view manager's device context scale after allowing for the |
|
336 // quantization to app units, because of OS X multiscreen issues (where the |
|
337 // only two scales are 1.0 or 2.0, and so the quantization doesn't actually |
|
338 // cause problems anyhow). |
|
339 // In the case of a mismatch, fall back to scaling based on the dev context's |
|
340 // unscaledAppUnitsPerDevPixel value. On platforms where the device-pixel |
|
341 // scale is uniform across all displays (currently all except OS X), we'll |
|
342 // always use the precise value from mWindow->GetDefaultScale here. |
|
343 CSSToLayoutDeviceScale scale = widget->GetDefaultScale(); |
|
344 if (NSToIntRound(60.0 / scale.scale) == dx->UnscaledAppUnitsPerDevPixel()) { |
|
345 invScale = 1.0 / scale.scale; |
|
346 } else { |
|
347 invScale = dx->UnscaledAppUnitsPerDevPixel() / 60.0; |
|
348 } |
|
349 |
|
350 if (changedPos) { |
|
351 if (changedSize && !aMoveOnly) { |
|
352 widget->ResizeClient(newBounds.x * invScale, |
|
353 newBounds.y * invScale, |
|
354 newBounds.width * invScale, |
|
355 newBounds.height * invScale, |
|
356 aInvalidateChangedSize); |
|
357 } else { |
|
358 widget->MoveClient(newBounds.x * invScale, |
|
359 newBounds.y * invScale); |
|
360 } |
|
361 } else { |
|
362 if (changedSize && !aMoveOnly) { |
|
363 widget->ResizeClient(newBounds.width * invScale, |
|
364 newBounds.height * invScale, |
|
365 aInvalidateChangedSize); |
|
366 } // else do nothing! |
|
367 } |
|
368 |
|
369 if (!curVisibility && newVisibility) { |
|
370 widget->Show(true); |
|
371 } |
|
372 } |
|
373 |
|
374 void nsView::SetDimensions(const nsRect& aRect, bool aPaint, bool aResizeWidget) |
|
375 { |
|
376 nsRect dims = aRect; |
|
377 dims.MoveBy(mPosX, mPosY); |
|
378 |
|
379 // Don't use nsRect's operator== here, since it returns true when |
|
380 // both rects are empty even if they have different widths and we |
|
381 // have cases where that sort of thing matters to us. |
|
382 if (mDimBounds.TopLeft() == dims.TopLeft() && |
|
383 mDimBounds.Size() == dims.Size()) { |
|
384 return; |
|
385 } |
|
386 |
|
387 mDimBounds = dims; |
|
388 |
|
389 if (aResizeWidget) { |
|
390 ResetWidgetBounds(false, false); |
|
391 } |
|
392 } |
|
393 |
|
394 void nsView::NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible) |
|
395 { |
|
396 if (!aEffectivelyVisible) |
|
397 { |
|
398 DropMouseGrabbing(); |
|
399 } |
|
400 |
|
401 SetForcedRepaint(true); |
|
402 |
|
403 if (nullptr != mWindow) |
|
404 { |
|
405 ResetWidgetBounds(false, false); |
|
406 } |
|
407 |
|
408 for (nsView* child = mFirstChild; child; child = child->mNextSibling) { |
|
409 if (child->mVis == nsViewVisibility_kHide) { |
|
410 // It was effectively hidden and still is |
|
411 continue; |
|
412 } |
|
413 // Our child is visible if we are |
|
414 child->NotifyEffectiveVisibilityChanged(aEffectivelyVisible); |
|
415 } |
|
416 } |
|
417 |
|
418 void nsView::SetVisibility(nsViewVisibility aVisibility) |
|
419 { |
|
420 mVis = aVisibility; |
|
421 NotifyEffectiveVisibilityChanged(IsEffectivelyVisible()); |
|
422 } |
|
423 |
|
424 void nsView::SetFloating(bool aFloatingView) |
|
425 { |
|
426 if (aFloatingView) |
|
427 mVFlags |= NS_VIEW_FLAG_FLOATING; |
|
428 else |
|
429 mVFlags &= ~NS_VIEW_FLAG_FLOATING; |
|
430 } |
|
431 |
|
432 void nsView::InvalidateHierarchy(nsViewManager *aViewManagerParent) |
|
433 { |
|
434 if (mViewManager->GetRootView() == this) |
|
435 mViewManager->InvalidateHierarchy(); |
|
436 |
|
437 for (nsView *child = mFirstChild; child; child = child->GetNextSibling()) |
|
438 child->InvalidateHierarchy(aViewManagerParent); |
|
439 } |
|
440 |
|
441 void nsView::InsertChild(nsView *aChild, nsView *aSibling) |
|
442 { |
|
443 NS_PRECONDITION(nullptr != aChild, "null ptr"); |
|
444 |
|
445 if (nullptr != aChild) |
|
446 { |
|
447 if (nullptr != aSibling) |
|
448 { |
|
449 #ifdef DEBUG |
|
450 NS_ASSERTION(aSibling->GetParent() == this, "tried to insert view with invalid sibling"); |
|
451 #endif |
|
452 //insert after sibling |
|
453 aChild->SetNextSibling(aSibling->GetNextSibling()); |
|
454 aSibling->SetNextSibling(aChild); |
|
455 } |
|
456 else |
|
457 { |
|
458 aChild->SetNextSibling(mFirstChild); |
|
459 mFirstChild = aChild; |
|
460 } |
|
461 aChild->SetParent(this); |
|
462 |
|
463 // If we just inserted a root view, then update the RootViewManager |
|
464 // on all view managers in the new subtree. |
|
465 |
|
466 nsViewManager *vm = aChild->GetViewManager(); |
|
467 if (vm->GetRootView() == aChild) |
|
468 { |
|
469 aChild->InvalidateHierarchy(nullptr); // don't care about releasing grabs |
|
470 } |
|
471 } |
|
472 } |
|
473 |
|
474 void nsView::RemoveChild(nsView *child) |
|
475 { |
|
476 NS_PRECONDITION(nullptr != child, "null ptr"); |
|
477 |
|
478 if (nullptr != child) |
|
479 { |
|
480 nsView* prevKid = nullptr; |
|
481 nsView* kid = mFirstChild; |
|
482 DebugOnly<bool> found = false; |
|
483 while (nullptr != kid) { |
|
484 if (kid == child) { |
|
485 if (nullptr != prevKid) { |
|
486 prevKid->SetNextSibling(kid->GetNextSibling()); |
|
487 } else { |
|
488 mFirstChild = kid->GetNextSibling(); |
|
489 } |
|
490 child->SetParent(nullptr); |
|
491 found = true; |
|
492 break; |
|
493 } |
|
494 prevKid = kid; |
|
495 kid = kid->GetNextSibling(); |
|
496 } |
|
497 NS_ASSERTION(found, "tried to remove non child"); |
|
498 |
|
499 // If we just removed a root view, then update the RootViewManager |
|
500 // on all view managers in the removed subtree. |
|
501 |
|
502 nsViewManager *vm = child->GetViewManager(); |
|
503 if (vm->GetRootView() == child) |
|
504 { |
|
505 child->InvalidateHierarchy(GetViewManager()); |
|
506 } |
|
507 } |
|
508 } |
|
509 |
|
510 // Native widgets ultimately just can't deal with the awesome power of |
|
511 // CSS2 z-index. However, we set the z-index on the widget anyway |
|
512 // because in many simple common cases the widgets do end up in the |
|
513 // right order. We set each widget's z-index to the z-index of the |
|
514 // nearest ancestor that has non-auto z-index. |
|
515 static void UpdateNativeWidgetZIndexes(nsView* aView, int32_t aZIndex) |
|
516 { |
|
517 if (aView->HasWidget()) { |
|
518 nsIWidget* widget = aView->GetWidget(); |
|
519 if (widget->GetZIndex() != aZIndex) { |
|
520 widget->SetZIndex(aZIndex); |
|
521 } |
|
522 } else { |
|
523 for (nsView* v = aView->GetFirstChild(); v; v = v->GetNextSibling()) { |
|
524 if (v->GetZIndexIsAuto()) { |
|
525 UpdateNativeWidgetZIndexes(v, aZIndex); |
|
526 } |
|
527 } |
|
528 } |
|
529 } |
|
530 |
|
531 static int32_t FindNonAutoZIndex(nsView* aView) |
|
532 { |
|
533 while (aView) { |
|
534 if (!aView->GetZIndexIsAuto()) { |
|
535 return aView->GetZIndex(); |
|
536 } |
|
537 aView = aView->GetParent(); |
|
538 } |
|
539 return 0; |
|
540 } |
|
541 |
|
542 struct DefaultWidgetInitData : public nsWidgetInitData { |
|
543 DefaultWidgetInitData() : nsWidgetInitData() |
|
544 { |
|
545 mWindowType = eWindowType_child; |
|
546 clipChildren = true; |
|
547 clipSiblings = true; |
|
548 } |
|
549 }; |
|
550 |
|
551 nsresult nsView::CreateWidget(nsWidgetInitData *aWidgetInitData, |
|
552 bool aEnableDragDrop, |
|
553 bool aResetVisibility) |
|
554 { |
|
555 AssertNoWindow(); |
|
556 NS_ABORT_IF_FALSE(!aWidgetInitData || |
|
557 aWidgetInitData->mWindowType != eWindowType_popup, |
|
558 "Use CreateWidgetForPopup"); |
|
559 |
|
560 DefaultWidgetInitData defaultInitData; |
|
561 bool initDataPassedIn = !!aWidgetInitData; |
|
562 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData; |
|
563 defaultInitData.mListenForResizes = |
|
564 (!initDataPassedIn && GetParent() && |
|
565 GetParent()->GetViewManager() != mViewManager); |
|
566 |
|
567 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType); |
|
568 |
|
569 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); |
|
570 |
|
571 nsIWidget* parentWidget = |
|
572 GetParent() ? GetParent()->GetNearestWidget(nullptr) : nullptr; |
|
573 if (!parentWidget) { |
|
574 NS_ERROR("nsView::CreateWidget without suitable parent widget??"); |
|
575 return NS_ERROR_FAILURE; |
|
576 } |
|
577 |
|
578 // XXX: using aForceUseIWidgetParent=true to preserve previous |
|
579 // semantics. It's not clear that it's actually needed. |
|
580 mWindow = parentWidget->CreateChild(trect, dx, aWidgetInitData, |
|
581 true).take(); |
|
582 if (!mWindow) { |
|
583 return NS_ERROR_FAILURE; |
|
584 } |
|
585 |
|
586 InitializeWindow(aEnableDragDrop, aResetVisibility); |
|
587 |
|
588 return NS_OK; |
|
589 } |
|
590 |
|
591 nsresult nsView::CreateWidgetForParent(nsIWidget* aParentWidget, |
|
592 nsWidgetInitData *aWidgetInitData, |
|
593 bool aEnableDragDrop, |
|
594 bool aResetVisibility) |
|
595 { |
|
596 AssertNoWindow(); |
|
597 NS_ABORT_IF_FALSE(!aWidgetInitData || |
|
598 aWidgetInitData->mWindowType != eWindowType_popup, |
|
599 "Use CreateWidgetForPopup"); |
|
600 NS_ABORT_IF_FALSE(aParentWidget, "Parent widget required"); |
|
601 |
|
602 DefaultWidgetInitData defaultInitData; |
|
603 aWidgetInitData = aWidgetInitData ? aWidgetInitData : &defaultInitData; |
|
604 |
|
605 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType); |
|
606 |
|
607 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); |
|
608 |
|
609 mWindow = |
|
610 aParentWidget->CreateChild(trect, dx, aWidgetInitData).take(); |
|
611 if (!mWindow) { |
|
612 return NS_ERROR_FAILURE; |
|
613 } |
|
614 |
|
615 InitializeWindow(aEnableDragDrop, aResetVisibility); |
|
616 |
|
617 return NS_OK; |
|
618 } |
|
619 |
|
620 nsresult nsView::CreateWidgetForPopup(nsWidgetInitData *aWidgetInitData, |
|
621 nsIWidget* aParentWidget, |
|
622 bool aEnableDragDrop, |
|
623 bool aResetVisibility) |
|
624 { |
|
625 AssertNoWindow(); |
|
626 NS_ABORT_IF_FALSE(aWidgetInitData, "Widget init data required"); |
|
627 NS_ABORT_IF_FALSE(aWidgetInitData->mWindowType == eWindowType_popup, |
|
628 "Use one of the other CreateWidget methods"); |
|
629 |
|
630 nsIntRect trect = CalcWidgetBounds(aWidgetInitData->mWindowType); |
|
631 |
|
632 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); |
|
633 |
|
634 // XXX/cjones: having these two separate creation cases seems ... um |
|
635 // ... unnecessary, but it's the way the old code did it. Please |
|
636 // unify them by first finding a suitable parent nsIWidget, then |
|
637 // getting rid of aForceUseIWidgetParent. |
|
638 if (aParentWidget) { |
|
639 // XXX: using aForceUseIWidgetParent=true to preserve previous |
|
640 // semantics. It's not clear that it's actually needed. |
|
641 mWindow = aParentWidget->CreateChild(trect, dx, aWidgetInitData, |
|
642 true).take(); |
|
643 } |
|
644 else { |
|
645 nsIWidget* nearestParent = GetParent() ? GetParent()->GetNearestWidget(nullptr) |
|
646 : nullptr; |
|
647 if (!nearestParent) { |
|
648 // Without a parent, we can't make a popup. This can happen |
|
649 // when printing |
|
650 return NS_ERROR_FAILURE; |
|
651 } |
|
652 |
|
653 mWindow = |
|
654 nearestParent->CreateChild(trect, dx, aWidgetInitData).take(); |
|
655 } |
|
656 if (!mWindow) { |
|
657 return NS_ERROR_FAILURE; |
|
658 } |
|
659 |
|
660 InitializeWindow(aEnableDragDrop, aResetVisibility); |
|
661 |
|
662 return NS_OK; |
|
663 } |
|
664 |
|
665 void |
|
666 nsView::InitializeWindow(bool aEnableDragDrop, bool aResetVisibility) |
|
667 { |
|
668 NS_ABORT_IF_FALSE(mWindow, "Must have a window to initialize"); |
|
669 |
|
670 mWindow->SetWidgetListener(this); |
|
671 |
|
672 if (aEnableDragDrop) { |
|
673 mWindow->EnableDragDrop(true); |
|
674 } |
|
675 |
|
676 // propagate the z-index to the widget. |
|
677 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this)); |
|
678 |
|
679 //make sure visibility state is accurate |
|
680 |
|
681 if (aResetVisibility) { |
|
682 SetVisibility(GetVisibility()); |
|
683 } |
|
684 } |
|
685 |
|
686 // Attach to a top level widget and start receiving mirrored events. |
|
687 nsresult nsView::AttachToTopLevelWidget(nsIWidget* aWidget) |
|
688 { |
|
689 NS_PRECONDITION(nullptr != aWidget, "null widget ptr"); |
|
690 /// XXXjimm This is a temporary workaround to an issue w/document |
|
691 // viewer (bug 513162). |
|
692 nsIWidgetListener* listener = aWidget->GetAttachedWidgetListener(); |
|
693 if (listener) { |
|
694 nsView *oldView = listener->GetView(); |
|
695 if (oldView) { |
|
696 oldView->DetachFromTopLevelWidget(); |
|
697 } |
|
698 } |
|
699 |
|
700 nsRefPtr<nsDeviceContext> dx = mViewManager->GetDeviceContext(); |
|
701 |
|
702 // Note, the previous device context will be released. Detaching |
|
703 // will not restore the old one. |
|
704 nsresult rv = aWidget->AttachViewToTopLevel(!nsIWidget::UsePuppetWidgets(), dx); |
|
705 if (NS_FAILED(rv)) |
|
706 return rv; |
|
707 |
|
708 mWindow = aWidget; |
|
709 NS_ADDREF(mWindow); |
|
710 |
|
711 mWindow->SetAttachedWidgetListener(this); |
|
712 mWindow->EnableDragDrop(true); |
|
713 mWidgetIsTopLevel = true; |
|
714 |
|
715 // Refresh the view bounds |
|
716 CalcWidgetBounds(mWindow->WindowType()); |
|
717 |
|
718 return NS_OK; |
|
719 } |
|
720 |
|
721 // Detach this view from an attached widget. |
|
722 nsresult nsView::DetachFromTopLevelWidget() |
|
723 { |
|
724 NS_PRECONDITION(mWidgetIsTopLevel, "Not attached currently!"); |
|
725 NS_PRECONDITION(mWindow, "null mWindow for DetachFromTopLevelWidget!"); |
|
726 |
|
727 mWindow->SetAttachedWidgetListener(nullptr); |
|
728 NS_RELEASE(mWindow); |
|
729 |
|
730 mWidgetIsTopLevel = false; |
|
731 |
|
732 return NS_OK; |
|
733 } |
|
734 |
|
735 void nsView::SetZIndex(bool aAuto, int32_t aZIndex) |
|
736 { |
|
737 bool oldIsAuto = GetZIndexIsAuto(); |
|
738 mVFlags = (mVFlags & ~NS_VIEW_FLAG_AUTO_ZINDEX) | (aAuto ? NS_VIEW_FLAG_AUTO_ZINDEX : 0); |
|
739 mZIndex = aZIndex; |
|
740 |
|
741 if (HasWidget() || !oldIsAuto || !aAuto) { |
|
742 UpdateNativeWidgetZIndexes(this, FindNonAutoZIndex(this)); |
|
743 } |
|
744 } |
|
745 |
|
746 void nsView::AssertNoWindow() |
|
747 { |
|
748 // XXX: it would be nice to make this a strong assert |
|
749 if (MOZ_UNLIKELY(mWindow)) { |
|
750 NS_ERROR("We already have a window for this view? BAD"); |
|
751 mWindow->SetWidgetListener(nullptr); |
|
752 mWindow->Destroy(); |
|
753 NS_RELEASE(mWindow); |
|
754 } |
|
755 } |
|
756 |
|
757 // |
|
758 // internal window creation functions |
|
759 // |
|
760 void nsView::AttachWidgetEventHandler(nsIWidget* aWidget) |
|
761 { |
|
762 #ifdef DEBUG |
|
763 NS_ASSERTION(!aWidget->GetWidgetListener(), "Already have a widget listener"); |
|
764 #endif |
|
765 |
|
766 aWidget->SetWidgetListener(this); |
|
767 } |
|
768 |
|
769 void nsView::DetachWidgetEventHandler(nsIWidget* aWidget) |
|
770 { |
|
771 NS_ASSERTION(!aWidget->GetWidgetListener() || |
|
772 aWidget->GetWidgetListener()->GetView() == this, "Wrong view"); |
|
773 aWidget->SetWidgetListener(nullptr); |
|
774 } |
|
775 |
|
776 #ifdef DEBUG |
|
777 void nsView::List(FILE* out, int32_t aIndent) const |
|
778 { |
|
779 int32_t i; |
|
780 for (i = aIndent; --i >= 0; ) fputs(" ", out); |
|
781 fprintf(out, "%p ", (void*)this); |
|
782 if (nullptr != mWindow) { |
|
783 nscoord p2a = mViewManager->AppUnitsPerDevPixel(); |
|
784 nsIntRect rect; |
|
785 mWindow->GetClientBounds(rect); |
|
786 nsRect windowBounds = rect.ToAppUnits(p2a); |
|
787 mWindow->GetBounds(rect); |
|
788 nsRect nonclientBounds = rect.ToAppUnits(p2a); |
|
789 nsrefcnt widgetRefCnt = mWindow->AddRef() - 1; |
|
790 mWindow->Release(); |
|
791 int32_t Z = mWindow->GetZIndex(); |
|
792 fprintf(out, "(widget=%p[%" PRIuPTR "] z=%d pos={%d,%d,%d,%d}) ", |
|
793 (void*)mWindow, widgetRefCnt, Z, |
|
794 nonclientBounds.x, nonclientBounds.y, |
|
795 windowBounds.width, windowBounds.height); |
|
796 } |
|
797 nsRect brect = GetBounds(); |
|
798 fprintf(out, "{%d,%d,%d,%d}", |
|
799 brect.x, brect.y, brect.width, brect.height); |
|
800 fprintf(out, " z=%d vis=%d frame=%p <\n", |
|
801 mZIndex, mVis, static_cast<void*>(mFrame)); |
|
802 for (nsView* kid = mFirstChild; kid; kid = kid->GetNextSibling()) { |
|
803 NS_ASSERTION(kid->GetParent() == this, "incorrect parent"); |
|
804 kid->List(out, aIndent + 1); |
|
805 } |
|
806 for (i = aIndent; --i >= 0; ) fputs(" ", out); |
|
807 fputs(">\n", out); |
|
808 } |
|
809 #endif // DEBUG |
|
810 |
|
811 nsPoint nsView::GetOffsetTo(const nsView* aOther) const |
|
812 { |
|
813 return GetOffsetTo(aOther, GetViewManager()->AppUnitsPerDevPixel()); |
|
814 } |
|
815 |
|
816 nsPoint nsView::GetOffsetTo(const nsView* aOther, const int32_t aAPD) const |
|
817 { |
|
818 NS_ABORT_IF_FALSE(GetParent() || !aOther || aOther->GetParent() || |
|
819 this == aOther, "caller of (outer) GetOffsetTo must not " |
|
820 "pass unrelated views"); |
|
821 // We accumulate the final result in offset |
|
822 nsPoint offset(0, 0); |
|
823 // The offset currently accumulated at the current APD |
|
824 nsPoint docOffset(0, 0); |
|
825 const nsView* v = this; |
|
826 nsViewManager* currVM = v->GetViewManager(); |
|
827 int32_t currAPD = currVM->AppUnitsPerDevPixel(); |
|
828 const nsView* root = nullptr; |
|
829 for ( ; v != aOther && v; root = v, v = v->GetParent()) { |
|
830 nsViewManager* newVM = v->GetViewManager(); |
|
831 if (newVM != currVM) { |
|
832 int32_t newAPD = newVM->AppUnitsPerDevPixel(); |
|
833 if (newAPD != currAPD) { |
|
834 offset += docOffset.ConvertAppUnits(currAPD, aAPD); |
|
835 docOffset.x = docOffset.y = 0; |
|
836 currAPD = newAPD; |
|
837 } |
|
838 currVM = newVM; |
|
839 } |
|
840 docOffset += v->GetPosition(); |
|
841 } |
|
842 offset += docOffset.ConvertAppUnits(currAPD, aAPD); |
|
843 |
|
844 if (v != aOther) { |
|
845 // Looks like aOther wasn't an ancestor of |this|. So now we have |
|
846 // the root-VM-relative position of |this| in |offset|. Get the |
|
847 // root-VM-relative position of aOther and subtract it. |
|
848 nsPoint negOffset = aOther->GetOffsetTo(root, aAPD); |
|
849 offset -= negOffset; |
|
850 } |
|
851 |
|
852 return offset; |
|
853 } |
|
854 |
|
855 nsPoint nsView::GetOffsetToWidget(nsIWidget* aWidget) const |
|
856 { |
|
857 nsPoint pt; |
|
858 // Get the view for widget |
|
859 nsView* widgetView = GetViewFor(aWidget); |
|
860 if (!widgetView) { |
|
861 return pt; |
|
862 } |
|
863 |
|
864 // Get the offset to the widget view in the widget view's APD |
|
865 // We get the offset in the widget view's APD first and then convert to our |
|
866 // APD afterwards so that we can include the widget view's ViewToWidgetOffset |
|
867 // in the sum in its native APD, and then convert the whole thing to our APD |
|
868 // so that we don't have to convert the APD of the relatively small |
|
869 // ViewToWidgetOffset by itself with a potentially large relative rounding |
|
870 // error. |
|
871 pt = -widgetView->GetOffsetTo(this); |
|
872 // Add in the offset to the widget. |
|
873 pt += widgetView->ViewToWidgetOffset(); |
|
874 |
|
875 // Convert to our appunits. |
|
876 int32_t widgetAPD = widgetView->GetViewManager()->AppUnitsPerDevPixel(); |
|
877 int32_t ourAPD = GetViewManager()->AppUnitsPerDevPixel(); |
|
878 pt = pt.ConvertAppUnits(widgetAPD, ourAPD); |
|
879 return pt; |
|
880 } |
|
881 |
|
882 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset) const |
|
883 { |
|
884 return GetNearestWidget(aOffset, GetViewManager()->AppUnitsPerDevPixel()); |
|
885 } |
|
886 |
|
887 nsIWidget* nsView::GetNearestWidget(nsPoint* aOffset, const int32_t aAPD) const |
|
888 { |
|
889 // aOffset is based on the view's position, which ignores any chrome on |
|
890 // attached parent widgets. |
|
891 |
|
892 // We accumulate the final result in pt |
|
893 nsPoint pt(0, 0); |
|
894 // The offset currently accumulated at the current APD |
|
895 nsPoint docPt(0,0); |
|
896 const nsView* v = this; |
|
897 nsViewManager* currVM = v->GetViewManager(); |
|
898 int32_t currAPD = currVM->AppUnitsPerDevPixel(); |
|
899 for ( ; v && !v->HasWidget(); v = v->GetParent()) { |
|
900 nsViewManager* newVM = v->GetViewManager(); |
|
901 if (newVM != currVM) { |
|
902 int32_t newAPD = newVM->AppUnitsPerDevPixel(); |
|
903 if (newAPD != currAPD) { |
|
904 pt += docPt.ConvertAppUnits(currAPD, aAPD); |
|
905 docPt.x = docPt.y = 0; |
|
906 currAPD = newAPD; |
|
907 } |
|
908 currVM = newVM; |
|
909 } |
|
910 docPt += v->GetPosition(); |
|
911 } |
|
912 if (!v) { |
|
913 if (aOffset) { |
|
914 pt += docPt.ConvertAppUnits(currAPD, aAPD); |
|
915 *aOffset = pt; |
|
916 } |
|
917 return nullptr; |
|
918 } |
|
919 |
|
920 // pt is now the offset from v's origin to this view's origin. |
|
921 // We add the ViewToWidgetOffset to get the offset to the widget. |
|
922 if (aOffset) { |
|
923 docPt += v->ViewToWidgetOffset(); |
|
924 pt += docPt.ConvertAppUnits(currAPD, aAPD); |
|
925 *aOffset = pt; |
|
926 } |
|
927 return v->GetWidget(); |
|
928 } |
|
929 |
|
930 bool nsView::IsRoot() const |
|
931 { |
|
932 NS_ASSERTION(mViewManager != nullptr," View manager is null in nsView::IsRoot()"); |
|
933 return mViewManager->GetRootView() == this; |
|
934 } |
|
935 |
|
936 nsRect |
|
937 nsView::GetBoundsInParentUnits() const |
|
938 { |
|
939 nsView* parent = GetParent(); |
|
940 nsViewManager* VM = GetViewManager(); |
|
941 if (this != VM->GetRootView() || !parent) { |
|
942 return mDimBounds; |
|
943 } |
|
944 int32_t ourAPD = VM->AppUnitsPerDevPixel(); |
|
945 int32_t parentAPD = parent->GetViewManager()->AppUnitsPerDevPixel(); |
|
946 return mDimBounds.ConvertAppUnitsRoundOut(ourAPD, parentAPD); |
|
947 } |
|
948 |
|
949 nsPoint |
|
950 nsView::ConvertFromParentCoords(nsPoint aPt) const |
|
951 { |
|
952 const nsView* parent = GetParent(); |
|
953 if (parent) { |
|
954 aPt = aPt.ConvertAppUnits(parent->GetViewManager()->AppUnitsPerDevPixel(), |
|
955 GetViewManager()->AppUnitsPerDevPixel()); |
|
956 } |
|
957 aPt -= GetPosition(); |
|
958 return aPt; |
|
959 } |
|
960 |
|
961 static bool |
|
962 IsPopupWidget(nsIWidget* aWidget) |
|
963 { |
|
964 return (aWidget->WindowType() == eWindowType_popup); |
|
965 } |
|
966 |
|
967 nsIPresShell* |
|
968 nsView::GetPresShell() |
|
969 { |
|
970 return GetViewManager()->GetPresShell(); |
|
971 } |
|
972 |
|
973 bool |
|
974 nsView::WindowMoved(nsIWidget* aWidget, int32_t x, int32_t y) |
|
975 { |
|
976 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
977 if (pm && IsPopupWidget(aWidget)) { |
|
978 pm->PopupMoved(mFrame, nsIntPoint(x, y)); |
|
979 return true; |
|
980 } |
|
981 |
|
982 return false; |
|
983 } |
|
984 |
|
985 bool |
|
986 nsView::WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) |
|
987 { |
|
988 // The root view may not be set if this is the resize associated with |
|
989 // window creation |
|
990 SetForcedRepaint(true); |
|
991 if (this == mViewManager->GetRootView()) { |
|
992 nsRefPtr<nsDeviceContext> devContext = mViewManager->GetDeviceContext(); |
|
993 // ensure DPI is up-to-date, in case of window being opened and sized |
|
994 // on a non-default-dpi display (bug 829963) |
|
995 devContext->CheckDPIChange(); |
|
996 int32_t p2a = devContext->AppUnitsPerDevPixel(); |
|
997 mViewManager->SetWindowDimensions(NSIntPixelsToAppUnits(aWidth, p2a), |
|
998 NSIntPixelsToAppUnits(aHeight, p2a)); |
|
999 |
|
1000 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1001 if (pm) { |
|
1002 nsIPresShell* presShell = mViewManager->GetPresShell(); |
|
1003 if (presShell && presShell->GetDocument()) { |
|
1004 pm->AdjustPopupsOnWindowChange(presShell); |
|
1005 } |
|
1006 } |
|
1007 |
|
1008 return true; |
|
1009 } |
|
1010 else if (IsPopupWidget(aWidget)) { |
|
1011 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1012 if (pm) { |
|
1013 pm->PopupResized(mFrame, nsIntSize(aWidth, aHeight)); |
|
1014 return true; |
|
1015 } |
|
1016 } |
|
1017 |
|
1018 return false; |
|
1019 } |
|
1020 |
|
1021 bool |
|
1022 nsView::RequestWindowClose(nsIWidget* aWidget) |
|
1023 { |
|
1024 if (mFrame && IsPopupWidget(aWidget) && |
|
1025 mFrame->GetType() == nsGkAtoms::menuPopupFrame) { |
|
1026 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); |
|
1027 if (pm) { |
|
1028 pm->HidePopup(mFrame->GetContent(), false, true, false, false); |
|
1029 return true; |
|
1030 } |
|
1031 } |
|
1032 |
|
1033 return false; |
|
1034 } |
|
1035 |
|
1036 void |
|
1037 nsView::WillPaintWindow(nsIWidget* aWidget) |
|
1038 { |
|
1039 nsRefPtr<nsViewManager> vm = mViewManager; |
|
1040 vm->WillPaintWindow(aWidget); |
|
1041 } |
|
1042 |
|
1043 bool |
|
1044 nsView::PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) |
|
1045 { |
|
1046 NS_ASSERTION(this == nsView::GetViewFor(aWidget), "wrong view for widget?"); |
|
1047 |
|
1048 nsRefPtr<nsViewManager> vm = mViewManager; |
|
1049 bool result = vm->PaintWindow(aWidget, aRegion); |
|
1050 return result; |
|
1051 } |
|
1052 |
|
1053 void |
|
1054 nsView::DidPaintWindow() |
|
1055 { |
|
1056 nsRefPtr<nsViewManager> vm = mViewManager; |
|
1057 vm->DidPaintWindow(); |
|
1058 } |
|
1059 |
|
1060 void |
|
1061 nsView::DidCompositeWindow() |
|
1062 { |
|
1063 nsIPresShell* presShell = mViewManager->GetPresShell(); |
|
1064 if (presShell) { |
|
1065 nsAutoScriptBlocker scriptBlocker; |
|
1066 presShell->GetPresContext()->GetDisplayRootPresContext()->GetRootPresContext()->NotifyDidPaintForSubtree(nsIPresShell::PAINT_COMPOSITE); |
|
1067 } |
|
1068 } |
|
1069 |
|
1070 void |
|
1071 nsView::RequestRepaint() |
|
1072 { |
|
1073 nsIPresShell* presShell = mViewManager->GetPresShell(); |
|
1074 if (presShell) { |
|
1075 presShell->ScheduleViewManagerFlush(); |
|
1076 } |
|
1077 } |
|
1078 |
|
1079 nsEventStatus |
|
1080 nsView::HandleEvent(WidgetGUIEvent* aEvent, |
|
1081 bool aUseAttachedEvents) |
|
1082 { |
|
1083 NS_PRECONDITION(nullptr != aEvent->widget, "null widget ptr"); |
|
1084 |
|
1085 nsEventStatus result = nsEventStatus_eIgnore; |
|
1086 nsView* view; |
|
1087 if (aUseAttachedEvents) { |
|
1088 nsIWidgetListener* listener = aEvent->widget->GetAttachedWidgetListener(); |
|
1089 view = listener ? listener->GetView() : nullptr; |
|
1090 } |
|
1091 else { |
|
1092 view = GetViewFor(aEvent->widget); |
|
1093 } |
|
1094 |
|
1095 if (view) { |
|
1096 nsRefPtr<nsViewManager> vm = view->GetViewManager(); |
|
1097 vm->DispatchEvent(aEvent, view, &result); |
|
1098 } |
|
1099 |
|
1100 return result; |
|
1101 } |