|
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 /* rendering object for replaced elements with bitmap image data */ |
|
7 |
|
8 #include "nsImageFrame.h" |
|
9 |
|
10 #include "mozilla/DebugOnly.h" |
|
11 #include "mozilla/EventStates.h" |
|
12 #include "mozilla/MouseEvents.h" |
|
13 |
|
14 #include "nsCOMPtr.h" |
|
15 #include "nsIImageLoadingContent.h" |
|
16 #include "nsString.h" |
|
17 #include "nsPrintfCString.h" |
|
18 #include "nsPresContext.h" |
|
19 #include "nsRenderingContext.h" |
|
20 #include "nsIPresShell.h" |
|
21 #include "nsGkAtoms.h" |
|
22 #include "nsIDocument.h" |
|
23 #include "nsContentUtils.h" |
|
24 #include "nsCSSAnonBoxes.h" |
|
25 #include "nsStyleContext.h" |
|
26 #include "nsStyleConsts.h" |
|
27 #include "nsStyleCoord.h" |
|
28 #include "nsTransform2D.h" |
|
29 #include "nsImageMap.h" |
|
30 #include "nsIIOService.h" |
|
31 #include "nsILoadGroup.h" |
|
32 #include "nsISupportsPriority.h" |
|
33 #include "nsNetUtil.h" |
|
34 #include "nsCSSRendering.h" |
|
35 #include "nsIDOMHTMLAnchorElement.h" |
|
36 #include "nsNameSpaceManager.h" |
|
37 #include <algorithm> |
|
38 #ifdef ACCESSIBILITY |
|
39 #include "nsAccessibilityService.h" |
|
40 #endif |
|
41 #include "nsIDOMNode.h" |
|
42 #include "nsLayoutUtils.h" |
|
43 #include "nsDisplayList.h" |
|
44 |
|
45 #include "imgIContainer.h" |
|
46 #include "imgLoader.h" |
|
47 #include "imgRequestProxy.h" |
|
48 |
|
49 #include "nsCSSFrameConstructor.h" |
|
50 #include "nsIDOMRange.h" |
|
51 |
|
52 #include "nsError.h" |
|
53 #include "nsBidiUtils.h" |
|
54 #include "nsBidiPresUtils.h" |
|
55 #include "mozIThirdPartyUtil.h" |
|
56 |
|
57 #include "gfxRect.h" |
|
58 #include "ImageLayers.h" |
|
59 #include "ImageContainer.h" |
|
60 #include "nsStyleSet.h" |
|
61 #include "nsBlockFrame.h" |
|
62 #include "nsStyleStructInlines.h" |
|
63 |
|
64 #include "mozilla/Preferences.h" |
|
65 |
|
66 #include "mozilla/dom/Link.h" |
|
67 |
|
68 using namespace mozilla; |
|
69 |
|
70 // sizes (pixels) for image icon, padding and border frame |
|
71 #define ICON_SIZE (16) |
|
72 #define ICON_PADDING (3) |
|
73 #define ALT_BORDER_WIDTH (1) |
|
74 |
|
75 |
|
76 //we must add hooks soon |
|
77 #define IMAGE_EDITOR_CHECK 1 |
|
78 |
|
79 // Default alignment value (so we can tell an unset value from a set value) |
|
80 #define ALIGN_UNSET uint8_t(-1) |
|
81 |
|
82 using namespace mozilla::layers; |
|
83 using namespace mozilla::dom; |
|
84 |
|
85 // static icon information |
|
86 nsImageFrame::IconLoad* nsImageFrame::gIconLoad = nullptr; |
|
87 |
|
88 // cached IO service for loading icons |
|
89 nsIIOService* nsImageFrame::sIOService; |
|
90 |
|
91 // test if the width and height are fixed, looking at the style data |
|
92 static bool HaveFixedSize(const nsStylePosition* aStylePosition) |
|
93 { |
|
94 // check the width and height values in the reflow state's style struct |
|
95 // - if width and height are specified as either coord or percentage, then |
|
96 // the size of the image frame is constrained |
|
97 return aStylePosition->mWidth.IsCoordPercentCalcUnit() && |
|
98 aStylePosition->mHeight.IsCoordPercentCalcUnit(); |
|
99 } |
|
100 // use the data in the reflow state to decide if the image has a constrained size |
|
101 // (i.e. width and height that are based on the containing block size and not the image size) |
|
102 // so we can avoid animated GIF related reflows |
|
103 inline bool HaveFixedSize(const nsHTMLReflowState& aReflowState) |
|
104 { |
|
105 NS_ASSERTION(aReflowState.mStylePosition, "crappy reflowState - null stylePosition"); |
|
106 // when an image has percent css style height or width, but ComputedHeight() |
|
107 // or ComputedWidth() of reflow state is NS_UNCONSTRAINEDSIZE |
|
108 // it needs to return false to cause an incremental reflow later |
|
109 // if an image is inside table like bug 156731 simple testcase III, |
|
110 // during pass 1 reflow, ComputedWidth() is NS_UNCONSTRAINEDSIZE |
|
111 // in pass 2 reflow, ComputedWidth() is 0, it also needs to return false |
|
112 // see bug 156731 |
|
113 const nsStyleCoord &height = aReflowState.mStylePosition->mHeight; |
|
114 const nsStyleCoord &width = aReflowState.mStylePosition->mWidth; |
|
115 return ((height.HasPercent() && |
|
116 NS_UNCONSTRAINEDSIZE == aReflowState.ComputedHeight()) || |
|
117 (width.HasPercent() && |
|
118 (NS_UNCONSTRAINEDSIZE == aReflowState.ComputedWidth() || |
|
119 0 == aReflowState.ComputedWidth()))) |
|
120 ? false |
|
121 : HaveFixedSize(aReflowState.mStylePosition); |
|
122 } |
|
123 |
|
124 nsIFrame* |
|
125 NS_NewImageFrame(nsIPresShell* aPresShell, nsStyleContext* aContext) |
|
126 { |
|
127 return new (aPresShell) nsImageFrame(aContext); |
|
128 } |
|
129 |
|
130 NS_IMPL_FRAMEARENA_HELPERS(nsImageFrame) |
|
131 |
|
132 |
|
133 nsImageFrame::nsImageFrame(nsStyleContext* aContext) : |
|
134 ImageFrameSuper(aContext), |
|
135 mComputedSize(0, 0), |
|
136 mIntrinsicRatio(0, 0), |
|
137 mDisplayingIcon(false), |
|
138 mFirstFrameComplete(false), |
|
139 mReflowCallbackPosted(false) |
|
140 { |
|
141 // We assume our size is not constrained and we haven't gotten an |
|
142 // initial reflow yet, so don't touch those flags. |
|
143 mIntrinsicSize.width.SetCoordValue(0); |
|
144 mIntrinsicSize.height.SetCoordValue(0); |
|
145 } |
|
146 |
|
147 nsImageFrame::~nsImageFrame() |
|
148 { |
|
149 } |
|
150 |
|
151 NS_QUERYFRAME_HEAD(nsImageFrame) |
|
152 NS_QUERYFRAME_ENTRY(nsImageFrame) |
|
153 NS_QUERYFRAME_TAIL_INHERITING(ImageFrameSuper) |
|
154 |
|
155 #ifdef ACCESSIBILITY |
|
156 a11y::AccType |
|
157 nsImageFrame::AccessibleType() |
|
158 { |
|
159 // Don't use GetImageMap() to avoid reentrancy into accessibility. |
|
160 if (HasImageMap()) { |
|
161 return a11y::eHTMLImageMapType; |
|
162 } |
|
163 |
|
164 return a11y::eImageType; |
|
165 } |
|
166 #endif |
|
167 |
|
168 void |
|
169 nsImageFrame::DisconnectMap() |
|
170 { |
|
171 if (mImageMap) { |
|
172 mImageMap->Destroy(); |
|
173 NS_RELEASE(mImageMap); |
|
174 |
|
175 #ifdef ACCESSIBILITY |
|
176 nsAccessibilityService* accService = GetAccService(); |
|
177 if (accService) { |
|
178 accService->RecreateAccessible(PresContext()->PresShell(), mContent); |
|
179 } |
|
180 #endif |
|
181 } |
|
182 } |
|
183 |
|
184 void |
|
185 nsImageFrame::DestroyFrom(nsIFrame* aDestructRoot) |
|
186 { |
|
187 if (mReflowCallbackPosted) { |
|
188 PresContext()->PresShell()->CancelReflowCallback(this); |
|
189 mReflowCallbackPosted = false; |
|
190 } |
|
191 |
|
192 // Tell our image map, if there is one, to clean up |
|
193 // This causes the nsImageMap to unregister itself as |
|
194 // a DOM listener. |
|
195 DisconnectMap(); |
|
196 |
|
197 // set the frame to null so we don't send messages to a dead object. |
|
198 if (mListener) { |
|
199 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
200 if (imageLoader) { |
|
201 // Notify our image loading content that we are going away so it can |
|
202 // deregister with our refresh driver. |
|
203 imageLoader->FrameDestroyed(this); |
|
204 |
|
205 imageLoader->RemoveObserver(mListener); |
|
206 } |
|
207 |
|
208 reinterpret_cast<nsImageListener*>(mListener.get())->SetFrame(nullptr); |
|
209 } |
|
210 |
|
211 mListener = nullptr; |
|
212 |
|
213 // If we were displaying an icon, take ourselves off the list |
|
214 if (mDisplayingIcon) |
|
215 gIconLoad->RemoveIconObserver(this); |
|
216 |
|
217 nsSplittableFrame::DestroyFrom(aDestructRoot); |
|
218 } |
|
219 |
|
220 |
|
221 |
|
222 void |
|
223 nsImageFrame::Init(nsIContent* aContent, |
|
224 nsIFrame* aParent, |
|
225 nsIFrame* aPrevInFlow) |
|
226 { |
|
227 nsSplittableFrame::Init(aContent, aParent, aPrevInFlow); |
|
228 |
|
229 mListener = new nsImageListener(this); |
|
230 |
|
231 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(aContent); |
|
232 if (!imageLoader) { |
|
233 NS_RUNTIMEABORT("Why do we have an nsImageFrame here at all?"); |
|
234 } |
|
235 |
|
236 imageLoader->AddObserver(mListener); |
|
237 |
|
238 nsPresContext *aPresContext = PresContext(); |
|
239 |
|
240 if (!gIconLoad) |
|
241 LoadIcons(aPresContext); |
|
242 |
|
243 // We have a PresContext now, so we need to notify the image content node |
|
244 // that it can register images. |
|
245 imageLoader->FrameCreated(this); |
|
246 |
|
247 // Give image loads associated with an image frame a small priority boost! |
|
248 nsCOMPtr<imgIRequest> currentRequest; |
|
249 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
|
250 getter_AddRefs(currentRequest)); |
|
251 nsCOMPtr<nsISupportsPriority> p = do_QueryInterface(currentRequest); |
|
252 if (p) |
|
253 p->AdjustPriority(-1); |
|
254 |
|
255 // If we already have an image container, OnStartContainer won't be called |
|
256 if (currentRequest) { |
|
257 nsCOMPtr<imgIContainer> image; |
|
258 currentRequest->GetImage(getter_AddRefs(image)); |
|
259 OnStartContainer(currentRequest, image); |
|
260 } |
|
261 } |
|
262 |
|
263 bool |
|
264 nsImageFrame::UpdateIntrinsicSize(imgIContainer* aImage) |
|
265 { |
|
266 NS_PRECONDITION(aImage, "null image"); |
|
267 if (!aImage) |
|
268 return false; |
|
269 |
|
270 IntrinsicSize oldIntrinsicSize = mIntrinsicSize; |
|
271 mIntrinsicSize = IntrinsicSize(); |
|
272 |
|
273 // Set intrinsic size to match aImage's reported intrinsic width & height. |
|
274 nsSize intrinsicSize; |
|
275 if (NS_SUCCEEDED(aImage->GetIntrinsicSize(&intrinsicSize))) { |
|
276 // If the image has no intrinsic width, intrinsicSize.width will be -1, and |
|
277 // we can leave mIntrinsicSize.width at its default value of eStyleUnit_None. |
|
278 // Otherwise we use intrinsicSize.width. Height works the same way. |
|
279 if (intrinsicSize.width != -1) |
|
280 mIntrinsicSize.width.SetCoordValue(intrinsicSize.width); |
|
281 if (intrinsicSize.height != -1) |
|
282 mIntrinsicSize.height.SetCoordValue(intrinsicSize.height); |
|
283 } else { |
|
284 // Failure means that the image hasn't loaded enough to report a result. We |
|
285 // treat this case as if the image's intrinsic size was 0x0. |
|
286 mIntrinsicSize.width.SetCoordValue(0); |
|
287 mIntrinsicSize.height.SetCoordValue(0); |
|
288 } |
|
289 |
|
290 return mIntrinsicSize != oldIntrinsicSize; |
|
291 } |
|
292 |
|
293 bool |
|
294 nsImageFrame::UpdateIntrinsicRatio(imgIContainer* aImage) |
|
295 { |
|
296 NS_PRECONDITION(aImage, "null image"); |
|
297 |
|
298 if (!aImage) |
|
299 return false; |
|
300 |
|
301 nsSize oldIntrinsicRatio = mIntrinsicRatio; |
|
302 |
|
303 // Set intrinsic ratio to match aImage's reported intrinsic ratio. |
|
304 if (NS_FAILED(aImage->GetIntrinsicRatio(&mIntrinsicRatio))) |
|
305 mIntrinsicRatio.SizeTo(0, 0); |
|
306 |
|
307 return mIntrinsicRatio != oldIntrinsicRatio; |
|
308 } |
|
309 |
|
310 bool |
|
311 nsImageFrame::GetSourceToDestTransform(nsTransform2D& aTransform) |
|
312 { |
|
313 // Set the translation components. |
|
314 // XXXbz does this introduce rounding errors because of the cast to |
|
315 // float? Should we just manually add that stuff in every time |
|
316 // instead? |
|
317 nsRect innerArea = GetInnerArea(); |
|
318 aTransform.SetToTranslate(float(innerArea.x), |
|
319 float(innerArea.y - GetContinuationOffset())); |
|
320 |
|
321 // Set the scale factors. |
|
322 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && |
|
323 mIntrinsicSize.width.GetCoordValue() != 0 && |
|
324 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord && |
|
325 mIntrinsicSize.height.GetCoordValue() != 0 && |
|
326 mIntrinsicSize.width.GetCoordValue() != mComputedSize.width && |
|
327 mIntrinsicSize.height.GetCoordValue() != mComputedSize.height) { |
|
328 |
|
329 aTransform.SetScale(float(mComputedSize.width) / |
|
330 float(mIntrinsicSize.width.GetCoordValue()), |
|
331 float(mComputedSize.height) / |
|
332 float(mIntrinsicSize.height.GetCoordValue())); |
|
333 return true; |
|
334 } |
|
335 |
|
336 return false; |
|
337 } |
|
338 |
|
339 /* |
|
340 * These two functions basically do the same check. The first one |
|
341 * checks that the given request is the current request for our |
|
342 * mContent. The second checks that the given image container the |
|
343 * same as the image container on the current request for our |
|
344 * mContent. |
|
345 */ |
|
346 bool |
|
347 nsImageFrame::IsPendingLoad(imgIRequest* aRequest) const |
|
348 { |
|
349 // Default to pending load in case of errors |
|
350 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent)); |
|
351 NS_ASSERTION(imageLoader, "No image loading content?"); |
|
352 |
|
353 int32_t requestType = nsIImageLoadingContent::UNKNOWN_REQUEST; |
|
354 imageLoader->GetRequestType(aRequest, &requestType); |
|
355 |
|
356 return requestType != nsIImageLoadingContent::CURRENT_REQUEST; |
|
357 } |
|
358 |
|
359 bool |
|
360 nsImageFrame::IsPendingLoad(imgIContainer* aContainer) const |
|
361 { |
|
362 // default to pending load in case of errors |
|
363 if (!aContainer) { |
|
364 NS_ERROR("No image container!"); |
|
365 return true; |
|
366 } |
|
367 |
|
368 nsCOMPtr<nsIImageLoadingContent> imageLoader(do_QueryInterface(mContent)); |
|
369 NS_ASSERTION(imageLoader, "No image loading content?"); |
|
370 |
|
371 nsCOMPtr<imgIRequest> currentRequest; |
|
372 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
|
373 getter_AddRefs(currentRequest)); |
|
374 if (!currentRequest) { |
|
375 NS_ERROR("No current request"); |
|
376 return true; |
|
377 } |
|
378 |
|
379 nsCOMPtr<imgIContainer> currentContainer; |
|
380 currentRequest->GetImage(getter_AddRefs(currentContainer)); |
|
381 |
|
382 return currentContainer != aContainer; |
|
383 |
|
384 } |
|
385 |
|
386 nsRect |
|
387 nsImageFrame::SourceRectToDest(const nsIntRect& aRect) |
|
388 { |
|
389 // When scaling the image, row N of the source image may (depending on |
|
390 // the scaling function) be used to draw any row in the destination image |
|
391 // between floor(F * (N-1)) and ceil(F * (N+1)), where F is the |
|
392 // floating-point scaling factor. The same holds true for columns. |
|
393 // So, we start by computing that bound without the floor and ceiling. |
|
394 |
|
395 nsRect r(nsPresContext::CSSPixelsToAppUnits(aRect.x - 1), |
|
396 nsPresContext::CSSPixelsToAppUnits(aRect.y - 1), |
|
397 nsPresContext::CSSPixelsToAppUnits(aRect.width + 2), |
|
398 nsPresContext::CSSPixelsToAppUnits(aRect.height + 2)); |
|
399 |
|
400 nsTransform2D sourceToDest; |
|
401 if (!GetSourceToDestTransform(sourceToDest)) { |
|
402 // Failed to generate transform matrix. Return our whole inner area, |
|
403 // to be on the safe side (since this method is used for generating |
|
404 // invalidation rects). |
|
405 return GetInnerArea(); |
|
406 } |
|
407 |
|
408 sourceToDest.TransformCoord(&r.x, &r.y, &r.width, &r.height); |
|
409 |
|
410 // Now, round the edges out to the pixel boundary. |
|
411 nscoord scale = nsPresContext::CSSPixelsToAppUnits(1); |
|
412 nscoord right = r.x + r.width; |
|
413 nscoord bottom = r.y + r.height; |
|
414 |
|
415 r.x -= (scale + (r.x % scale)) % scale; |
|
416 r.y -= (scale + (r.y % scale)) % scale; |
|
417 r.width = right + ((scale - (right % scale)) % scale) - r.x; |
|
418 r.height = bottom + ((scale - (bottom % scale)) % scale) - r.y; |
|
419 |
|
420 return r; |
|
421 } |
|
422 |
|
423 // Note that we treat NS_EVENT_STATE_SUPPRESSED images as "OK". This means |
|
424 // that we'll construct image frames for them as needed if their display is |
|
425 // toggled from "none" (though we won't paint them, unless their visibility |
|
426 // is changed too). |
|
427 #define BAD_STATES (NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED | \ |
|
428 NS_EVENT_STATE_LOADING) |
|
429 |
|
430 // This is a macro so that we don't evaluate the boolean last arg |
|
431 // unless we have to; it can be expensive |
|
432 #define IMAGE_OK(_state, _loadingOK) \ |
|
433 (!(_state).HasAtLeastOneOfStates(BAD_STATES) || \ |
|
434 (!(_state).HasAtLeastOneOfStates(NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_USERDISABLED) && \ |
|
435 (_state).HasState(NS_EVENT_STATE_LOADING) && (_loadingOK))) |
|
436 |
|
437 /* static */ |
|
438 bool |
|
439 nsImageFrame::ShouldCreateImageFrameFor(Element* aElement, |
|
440 nsStyleContext* aStyleContext) |
|
441 { |
|
442 EventStates state = aElement->State(); |
|
443 if (IMAGE_OK(state, |
|
444 HaveFixedSize(aStyleContext->StylePosition()))) { |
|
445 // Image is fine; do the image frame thing |
|
446 return true; |
|
447 } |
|
448 |
|
449 // Check if we want to use a placeholder box with an icon or just |
|
450 // let the presShell make us into inline text. Decide as follows: |
|
451 // |
|
452 // - if our special "force icons" style is set, show an icon |
|
453 // - else if our "do not show placeholders" pref is set, skip the icon |
|
454 // - else: |
|
455 // - if there is a src attribute, there is no alt attribute, |
|
456 // and this is not an <object> (which could not possibly have |
|
457 // such an attribute), show an icon. |
|
458 // - if QuirksMode, and the IMG has a size show an icon. |
|
459 // - otherwise, skip the icon |
|
460 bool useSizedBox; |
|
461 |
|
462 if (aStyleContext->StyleUIReset()->mForceBrokenImageIcon) { |
|
463 useSizedBox = true; |
|
464 } |
|
465 else if (gIconLoad && gIconLoad->mPrefForceInlineAltText) { |
|
466 useSizedBox = false; |
|
467 } |
|
468 else if (aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::src) && |
|
469 !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::alt) && |
|
470 !aElement->IsHTML(nsGkAtoms::object) && |
|
471 !aElement->IsHTML(nsGkAtoms::input)) { |
|
472 // Use a sized box if we have no alt text. This means no alt attribute |
|
473 // and the node is not an object or an input (since those always have alt |
|
474 // text). |
|
475 useSizedBox = true; |
|
476 } |
|
477 else if (aStyleContext->PresContext()->CompatibilityMode() != |
|
478 eCompatibility_NavQuirks) { |
|
479 useSizedBox = false; |
|
480 } |
|
481 else { |
|
482 // check whether we have fixed size |
|
483 useSizedBox = HaveFixedSize(aStyleContext->StylePosition()); |
|
484 } |
|
485 |
|
486 return useSizedBox; |
|
487 } |
|
488 |
|
489 nsresult |
|
490 nsImageFrame::Notify(imgIRequest* aRequest, int32_t aType, const nsIntRect* aData) |
|
491 { |
|
492 if (aType == imgINotificationObserver::SIZE_AVAILABLE) { |
|
493 nsCOMPtr<imgIContainer> image; |
|
494 aRequest->GetImage(getter_AddRefs(image)); |
|
495 return OnStartContainer(aRequest, image); |
|
496 } |
|
497 |
|
498 if (aType == imgINotificationObserver::FRAME_UPDATE) { |
|
499 return OnDataAvailable(aRequest, aData); |
|
500 } |
|
501 |
|
502 if (aType == imgINotificationObserver::FRAME_COMPLETE) { |
|
503 mFirstFrameComplete = true; |
|
504 } |
|
505 |
|
506 if (aType == imgINotificationObserver::LOAD_COMPLETE) { |
|
507 uint32_t imgStatus; |
|
508 aRequest->GetImageStatus(&imgStatus); |
|
509 nsresult status = |
|
510 imgStatus & imgIRequest::STATUS_ERROR ? NS_ERROR_FAILURE : NS_OK; |
|
511 return OnStopRequest(aRequest, status); |
|
512 } |
|
513 |
|
514 return NS_OK; |
|
515 } |
|
516 |
|
517 static bool |
|
518 SizeIsAvailable(imgIRequest* aRequest) |
|
519 { |
|
520 if (!aRequest) |
|
521 return false; |
|
522 |
|
523 uint32_t imageStatus = 0; |
|
524 nsresult rv = aRequest->GetImageStatus(&imageStatus); |
|
525 |
|
526 return NS_SUCCEEDED(rv) && (imageStatus & imgIRequest::STATUS_SIZE_AVAILABLE); |
|
527 } |
|
528 |
|
529 nsresult |
|
530 nsImageFrame::OnStartContainer(imgIRequest *aRequest, imgIContainer *aImage) |
|
531 { |
|
532 if (!aImage) return NS_ERROR_INVALID_ARG; |
|
533 |
|
534 /* Get requested animation policy from the pres context: |
|
535 * normal = 0 |
|
536 * one frame = 1 |
|
537 * one loop = 2 |
|
538 */ |
|
539 nsPresContext *presContext = PresContext(); |
|
540 aImage->SetAnimationMode(presContext->ImageAnimationMode()); |
|
541 |
|
542 if (IsPendingLoad(aRequest)) { |
|
543 // We don't care |
|
544 return NS_OK; |
|
545 } |
|
546 |
|
547 bool intrinsicSizeChanged = false; |
|
548 if (SizeIsAvailable(aRequest)) { |
|
549 // This is valid and for the current request, so update our stored image |
|
550 // container, orienting according to our style. |
|
551 mImage = nsLayoutUtils::OrientImage(aImage, StyleVisibility()->mImageOrientation); |
|
552 |
|
553 intrinsicSizeChanged = UpdateIntrinsicSize(mImage); |
|
554 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; |
|
555 } else { |
|
556 // We no longer have a valid image, so release our stored image container. |
|
557 mImage = nullptr; |
|
558 |
|
559 // Have to size to 0,0 so that GetDesiredSize recalculates the size. |
|
560 mIntrinsicSize.width.SetCoordValue(0); |
|
561 mIntrinsicSize.height.SetCoordValue(0); |
|
562 mIntrinsicRatio.SizeTo(0, 0); |
|
563 intrinsicSizeChanged = true; |
|
564 } |
|
565 |
|
566 if (intrinsicSizeChanged && (mState & IMAGE_GOTINITIALREFLOW)) { |
|
567 // Now we need to reflow if we have an unconstrained size and have |
|
568 // already gotten the initial reflow |
|
569 if (!(mState & IMAGE_SIZECONSTRAINED)) { |
|
570 nsIPresShell *presShell = presContext->GetPresShell(); |
|
571 NS_ASSERTION(presShell, "No PresShell."); |
|
572 if (presShell) { |
|
573 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange, |
|
574 NS_FRAME_IS_DIRTY); |
|
575 } |
|
576 } |
|
577 } |
|
578 |
|
579 return NS_OK; |
|
580 } |
|
581 |
|
582 nsresult |
|
583 nsImageFrame::OnDataAvailable(imgIRequest *aRequest, |
|
584 const nsIntRect *aRect) |
|
585 { |
|
586 if (mFirstFrameComplete) { |
|
587 nsCOMPtr<imgIContainer> container; |
|
588 aRequest->GetImage(getter_AddRefs(container)); |
|
589 return FrameChanged(aRequest, container); |
|
590 } |
|
591 |
|
592 // XXX do we need to make sure that the reflow from the |
|
593 // OnStartContainer has been processed before we start calling |
|
594 // invalidate? |
|
595 |
|
596 NS_ENSURE_ARG_POINTER(aRect); |
|
597 |
|
598 if (!(mState & IMAGE_GOTINITIALREFLOW)) { |
|
599 // Don't bother to do anything; we have a reflow coming up! |
|
600 return NS_OK; |
|
601 } |
|
602 |
|
603 if (IsPendingLoad(aRequest)) { |
|
604 // We don't care |
|
605 return NS_OK; |
|
606 } |
|
607 |
|
608 #ifdef DEBUG_decode |
|
609 printf("Source rect (%d,%d,%d,%d)\n", |
|
610 aRect->x, aRect->y, aRect->width, aRect->height); |
|
611 #endif |
|
612 |
|
613 if (aRect->IsEqualInterior(nsIntRect::GetMaxSizedIntRect())) { |
|
614 InvalidateFrame(nsDisplayItem::TYPE_IMAGE); |
|
615 InvalidateFrame(nsDisplayItem::TYPE_ALT_FEEDBACK); |
|
616 } else { |
|
617 nsRect invalid = SourceRectToDest(*aRect); |
|
618 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_IMAGE); |
|
619 InvalidateFrameWithRect(invalid, nsDisplayItem::TYPE_ALT_FEEDBACK); |
|
620 } |
|
621 |
|
622 return NS_OK; |
|
623 } |
|
624 |
|
625 nsresult |
|
626 nsImageFrame::OnStopRequest(imgIRequest *aRequest, |
|
627 nsresult aStatus) |
|
628 { |
|
629 // Check what request type we're dealing with |
|
630 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
631 NS_ASSERTION(imageLoader, "Who's notifying us??"); |
|
632 int32_t loadType = nsIImageLoadingContent::UNKNOWN_REQUEST; |
|
633 imageLoader->GetRequestType(aRequest, &loadType); |
|
634 if (loadType != nsIImageLoadingContent::CURRENT_REQUEST && |
|
635 loadType != nsIImageLoadingContent::PENDING_REQUEST) { |
|
636 return NS_ERROR_FAILURE; |
|
637 } |
|
638 |
|
639 NotifyNewCurrentRequest(aRequest, aStatus); |
|
640 return NS_OK; |
|
641 } |
|
642 |
|
643 void |
|
644 nsImageFrame::NotifyNewCurrentRequest(imgIRequest *aRequest, |
|
645 nsresult aStatus) |
|
646 { |
|
647 nsCOMPtr<imgIContainer> image; |
|
648 aRequest->GetImage(getter_AddRefs(image)); |
|
649 NS_ASSERTION(image || NS_FAILED(aStatus), "Successful load with no container?"); |
|
650 |
|
651 // May have to switch sizes here! |
|
652 bool intrinsicSizeChanged = true; |
|
653 if (NS_SUCCEEDED(aStatus) && image && SizeIsAvailable(aRequest)) { |
|
654 // Update our stored image container, orienting according to our style. |
|
655 mImage = nsLayoutUtils::OrientImage(image, StyleVisibility()->mImageOrientation); |
|
656 |
|
657 intrinsicSizeChanged = UpdateIntrinsicSize(mImage); |
|
658 intrinsicSizeChanged = UpdateIntrinsicRatio(mImage) || intrinsicSizeChanged; |
|
659 } else { |
|
660 // We no longer have a valid image, so release our stored image container. |
|
661 mImage = nullptr; |
|
662 |
|
663 // Have to size to 0,0 so that GetDesiredSize recalculates the size |
|
664 mIntrinsicSize.width.SetCoordValue(0); |
|
665 mIntrinsicSize.height.SetCoordValue(0); |
|
666 mIntrinsicRatio.SizeTo(0, 0); |
|
667 } |
|
668 |
|
669 if (mState & IMAGE_GOTINITIALREFLOW) { // do nothing if we haven't gotten the initial reflow yet |
|
670 if (!(mState & IMAGE_SIZECONSTRAINED) && intrinsicSizeChanged) { |
|
671 nsIPresShell *presShell = PresContext()->GetPresShell(); |
|
672 if (presShell) { |
|
673 presShell->FrameNeedsReflow(this, nsIPresShell::eStyleChange, |
|
674 NS_FRAME_IS_DIRTY); |
|
675 } |
|
676 } |
|
677 // Update border+content to account for image change |
|
678 InvalidateFrame(); |
|
679 } |
|
680 } |
|
681 |
|
682 nsresult |
|
683 nsImageFrame::FrameChanged(imgIRequest *aRequest, |
|
684 imgIContainer *aContainer) |
|
685 { |
|
686 if (!StyleVisibility()->IsVisible()) { |
|
687 return NS_OK; |
|
688 } |
|
689 |
|
690 if (IsPendingLoad(aContainer)) { |
|
691 // We don't care about it |
|
692 return NS_OK; |
|
693 } |
|
694 |
|
695 InvalidateLayer(nsDisplayItem::TYPE_IMAGE); |
|
696 return NS_OK; |
|
697 } |
|
698 |
|
699 void |
|
700 nsImageFrame::EnsureIntrinsicSizeAndRatio(nsPresContext* aPresContext) |
|
701 { |
|
702 // If mIntrinsicSize.width and height are 0, then we need to update from the |
|
703 // image container. |
|
704 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && |
|
705 mIntrinsicSize.width.GetCoordValue() == 0 && |
|
706 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord && |
|
707 mIntrinsicSize.height.GetCoordValue() == 0) { |
|
708 |
|
709 if (mImage) { |
|
710 UpdateIntrinsicSize(mImage); |
|
711 UpdateIntrinsicRatio(mImage); |
|
712 } else { |
|
713 // image request is null or image size not known, probably an |
|
714 // invalid image specified |
|
715 // - make the image big enough for the icon (it may not be |
|
716 // used if inline alt expansion is used instead) |
|
717 if (!(GetStateBits() & NS_FRAME_GENERATED_CONTENT)) { |
|
718 nscoord edgeLengthToUse = |
|
719 nsPresContext::CSSPixelsToAppUnits( |
|
720 ICON_SIZE + (2 * (ICON_PADDING + ALT_BORDER_WIDTH))); |
|
721 mIntrinsicSize.width.SetCoordValue(edgeLengthToUse); |
|
722 mIntrinsicSize.height.SetCoordValue(edgeLengthToUse); |
|
723 mIntrinsicRatio.SizeTo(1, 1); |
|
724 } |
|
725 } |
|
726 } |
|
727 } |
|
728 |
|
729 /* virtual */ nsSize |
|
730 nsImageFrame::ComputeSize(nsRenderingContext *aRenderingContext, |
|
731 nsSize aCBSize, nscoord aAvailableWidth, |
|
732 nsSize aMargin, nsSize aBorder, nsSize aPadding, |
|
733 uint32_t aFlags) |
|
734 { |
|
735 nsPresContext *presContext = PresContext(); |
|
736 EnsureIntrinsicSizeAndRatio(presContext); |
|
737 |
|
738 return nsLayoutUtils::ComputeSizeWithIntrinsicDimensions( |
|
739 aRenderingContext, this, |
|
740 mIntrinsicSize, mIntrinsicRatio, aCBSize, |
|
741 aMargin, aBorder, aPadding); |
|
742 } |
|
743 |
|
744 nsRect |
|
745 nsImageFrame::GetInnerArea() const |
|
746 { |
|
747 return GetContentRect() - GetPosition(); |
|
748 } |
|
749 |
|
750 // get the offset into the content area of the image where aImg starts if it is a continuation. |
|
751 nscoord |
|
752 nsImageFrame::GetContinuationOffset() const |
|
753 { |
|
754 nscoord offset = 0; |
|
755 for (nsIFrame *f = GetPrevInFlow(); f; f = f->GetPrevInFlow()) { |
|
756 offset += f->GetContentRect().height; |
|
757 } |
|
758 NS_ASSERTION(offset >= 0, "bogus GetContentRect"); |
|
759 return offset; |
|
760 } |
|
761 |
|
762 /* virtual */ nscoord |
|
763 nsImageFrame::GetMinWidth(nsRenderingContext *aRenderingContext) |
|
764 { |
|
765 // XXX The caller doesn't account for constraints of the height, |
|
766 // min-height, and max-height properties. |
|
767 DebugOnly<nscoord> result; |
|
768 DISPLAY_MIN_WIDTH(this, result); |
|
769 nsPresContext *presContext = PresContext(); |
|
770 EnsureIntrinsicSizeAndRatio(presContext); |
|
771 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ? |
|
772 mIntrinsicSize.width.GetCoordValue() : 0; |
|
773 } |
|
774 |
|
775 /* virtual */ nscoord |
|
776 nsImageFrame::GetPrefWidth(nsRenderingContext *aRenderingContext) |
|
777 { |
|
778 // XXX The caller doesn't account for constraints of the height, |
|
779 // min-height, and max-height properties. |
|
780 DebugOnly<nscoord> result; |
|
781 DISPLAY_PREF_WIDTH(this, result); |
|
782 nsPresContext *presContext = PresContext(); |
|
783 EnsureIntrinsicSizeAndRatio(presContext); |
|
784 // convert from normal twips to scaled twips (printing...) |
|
785 return mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord ? |
|
786 mIntrinsicSize.width.GetCoordValue() : 0; |
|
787 } |
|
788 |
|
789 /* virtual */ IntrinsicSize |
|
790 nsImageFrame::GetIntrinsicSize() |
|
791 { |
|
792 return mIntrinsicSize; |
|
793 } |
|
794 |
|
795 /* virtual */ nsSize |
|
796 nsImageFrame::GetIntrinsicRatio() |
|
797 { |
|
798 return mIntrinsicRatio; |
|
799 } |
|
800 |
|
801 nsresult |
|
802 nsImageFrame::Reflow(nsPresContext* aPresContext, |
|
803 nsHTMLReflowMetrics& aMetrics, |
|
804 const nsHTMLReflowState& aReflowState, |
|
805 nsReflowStatus& aStatus) |
|
806 { |
|
807 DO_GLOBAL_REFLOW_COUNT("nsImageFrame"); |
|
808 DISPLAY_REFLOW(aPresContext, this, aReflowState, aMetrics, aStatus); |
|
809 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, |
|
810 ("enter nsImageFrame::Reflow: availSize=%d,%d", |
|
811 aReflowState.AvailableWidth(), aReflowState.AvailableHeight())); |
|
812 |
|
813 NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow"); |
|
814 |
|
815 aStatus = NS_FRAME_COMPLETE; |
|
816 |
|
817 // see if we have a frozen size (i.e. a fixed width and height) |
|
818 if (HaveFixedSize(aReflowState)) { |
|
819 mState |= IMAGE_SIZECONSTRAINED; |
|
820 } else { |
|
821 mState &= ~IMAGE_SIZECONSTRAINED; |
|
822 } |
|
823 |
|
824 // XXXldb These two bits are almost exact opposites (except in the |
|
825 // middle of the initial reflow); remove IMAGE_GOTINITIALREFLOW. |
|
826 if (GetStateBits() & NS_FRAME_FIRST_REFLOW) { |
|
827 mState |= IMAGE_GOTINITIALREFLOW; |
|
828 } |
|
829 |
|
830 mComputedSize = |
|
831 nsSize(aReflowState.ComputedWidth(), aReflowState.ComputedHeight()); |
|
832 |
|
833 aMetrics.Width() = mComputedSize.width; |
|
834 aMetrics.Height() = mComputedSize.height; |
|
835 |
|
836 // add borders and padding |
|
837 aMetrics.Width() += aReflowState.ComputedPhysicalBorderPadding().LeftRight(); |
|
838 aMetrics.Height() += aReflowState.ComputedPhysicalBorderPadding().TopBottom(); |
|
839 |
|
840 if (GetPrevInFlow()) { |
|
841 aMetrics.Width() = GetPrevInFlow()->GetSize().width; |
|
842 nscoord y = GetContinuationOffset(); |
|
843 aMetrics.Height() -= y + aReflowState.ComputedPhysicalBorderPadding().top; |
|
844 aMetrics.Height() = std::max(0, aMetrics.Height()); |
|
845 } |
|
846 |
|
847 |
|
848 // we have to split images if we are: |
|
849 // in Paginated mode, we need to have a constrained height, and have a height larger than our available height |
|
850 uint32_t loadStatus = imgIRequest::STATUS_NONE; |
|
851 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
852 NS_ASSERTION(imageLoader, "No content node??"); |
|
853 if (imageLoader) { |
|
854 nsCOMPtr<imgIRequest> currentRequest; |
|
855 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
|
856 getter_AddRefs(currentRequest)); |
|
857 if (currentRequest) { |
|
858 currentRequest->GetImageStatus(&loadStatus); |
|
859 } |
|
860 } |
|
861 if (aPresContext->IsPaginated() && |
|
862 ((loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) || (mState & IMAGE_SIZECONSTRAINED)) && |
|
863 NS_UNCONSTRAINEDSIZE != aReflowState.AvailableHeight() && |
|
864 aMetrics.Height() > aReflowState.AvailableHeight()) { |
|
865 // our desired height was greater than 0, so to avoid infinite |
|
866 // splitting, use 1 pixel as the min |
|
867 aMetrics.Height() = std::max(nsPresContext::CSSPixelsToAppUnits(1), aReflowState.AvailableHeight()); |
|
868 aStatus = NS_FRAME_NOT_COMPLETE; |
|
869 } |
|
870 |
|
871 aMetrics.SetOverflowAreasToDesiredBounds(); |
|
872 EventStates contentState = mContent->AsElement()->State(); |
|
873 bool imageOK = IMAGE_OK(contentState, true); |
|
874 |
|
875 // Determine if the size is available |
|
876 bool haveSize = false; |
|
877 if (loadStatus & imgIRequest::STATUS_SIZE_AVAILABLE) { |
|
878 haveSize = true; |
|
879 } |
|
880 |
|
881 if (!imageOK || !haveSize) { |
|
882 nsRect altFeedbackSize(0, 0, |
|
883 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH)), |
|
884 nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+2*(ICON_PADDING+ALT_BORDER_WIDTH))); |
|
885 // We include the altFeedbackSize in our visual overflow, but not in our |
|
886 // scrollable overflow, since it doesn't really need to be scrolled to |
|
887 // outside the image. |
|
888 static_assert(eOverflowType_LENGTH == 2, "Unknown overflow types?"); |
|
889 nsRect& visualOverflow = aMetrics.VisualOverflow(); |
|
890 visualOverflow.UnionRect(visualOverflow, altFeedbackSize); |
|
891 } |
|
892 FinishAndStoreOverflow(&aMetrics); |
|
893 |
|
894 if ((GetStateBits() & NS_FRAME_FIRST_REFLOW) && !mReflowCallbackPosted) { |
|
895 nsIPresShell* shell = PresContext()->PresShell(); |
|
896 mReflowCallbackPosted = true; |
|
897 shell->PostReflowCallback(this); |
|
898 } |
|
899 |
|
900 NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS, |
|
901 ("exit nsImageFrame::Reflow: size=%d,%d", |
|
902 aMetrics.Width(), aMetrics.Height())); |
|
903 NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aMetrics); |
|
904 return NS_OK; |
|
905 } |
|
906 |
|
907 bool |
|
908 nsImageFrame::ReflowFinished() |
|
909 { |
|
910 mReflowCallbackPosted = false; |
|
911 |
|
912 nsLayoutUtils::UpdateImageVisibilityForFrame(this); |
|
913 |
|
914 return false; |
|
915 } |
|
916 |
|
917 void |
|
918 nsImageFrame::ReflowCallbackCanceled() |
|
919 { |
|
920 mReflowCallbackPosted = false; |
|
921 } |
|
922 |
|
923 // Computes the width of the specified string. aMaxWidth specifies the maximum |
|
924 // width available. Once this limit is reached no more characters are measured. |
|
925 // The number of characters that fit within the maximum width are returned in |
|
926 // aMaxFit. NOTE: it is assumed that the fontmetrics have already been selected |
|
927 // into the rendering context before this is called (for performance). MMP |
|
928 nscoord |
|
929 nsImageFrame::MeasureString(const char16_t* aString, |
|
930 int32_t aLength, |
|
931 nscoord aMaxWidth, |
|
932 uint32_t& aMaxFit, |
|
933 nsRenderingContext& aContext) |
|
934 { |
|
935 nscoord totalWidth = 0; |
|
936 aContext.SetTextRunRTL(false); |
|
937 nscoord spaceWidth = aContext.GetWidth(' '); |
|
938 |
|
939 aMaxFit = 0; |
|
940 while (aLength > 0) { |
|
941 // Find the next place we can line break |
|
942 uint32_t len = aLength; |
|
943 bool trailingSpace = false; |
|
944 for (int32_t i = 0; i < aLength; i++) { |
|
945 if (dom::IsSpaceCharacter(aString[i]) && (i > 0)) { |
|
946 len = i; // don't include the space when measuring |
|
947 trailingSpace = true; |
|
948 break; |
|
949 } |
|
950 } |
|
951 |
|
952 // Measure this chunk of text, and see if it fits |
|
953 nscoord width = |
|
954 nsLayoutUtils::GetStringWidth(this, &aContext, aString, len); |
|
955 bool fits = (totalWidth + width) <= aMaxWidth; |
|
956 |
|
957 // If it fits on the line, or it's the first word we've processed then |
|
958 // include it |
|
959 if (fits || (0 == totalWidth)) { |
|
960 // New piece fits |
|
961 totalWidth += width; |
|
962 |
|
963 // If there's a trailing space then see if it fits as well |
|
964 if (trailingSpace) { |
|
965 if ((totalWidth + spaceWidth) <= aMaxWidth) { |
|
966 totalWidth += spaceWidth; |
|
967 } else { |
|
968 // Space won't fit. Leave it at the end but don't include it in |
|
969 // the width |
|
970 fits = false; |
|
971 } |
|
972 |
|
973 len++; |
|
974 } |
|
975 |
|
976 aMaxFit += len; |
|
977 aString += len; |
|
978 aLength -= len; |
|
979 } |
|
980 |
|
981 if (!fits) { |
|
982 break; |
|
983 } |
|
984 } |
|
985 return totalWidth; |
|
986 } |
|
987 |
|
988 // Formats the alt-text to fit within the specified rectangle. Breaks lines |
|
989 // between words if a word would extend past the edge of the rectangle |
|
990 void |
|
991 nsImageFrame::DisplayAltText(nsPresContext* aPresContext, |
|
992 nsRenderingContext& aRenderingContext, |
|
993 const nsString& aAltText, |
|
994 const nsRect& aRect) |
|
995 { |
|
996 // Set font and color |
|
997 aRenderingContext.SetColor(StyleColor()->mColor); |
|
998 nsRefPtr<nsFontMetrics> fm; |
|
999 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm), |
|
1000 nsLayoutUtils::FontSizeInflationFor(this)); |
|
1001 aRenderingContext.SetFont(fm); |
|
1002 |
|
1003 // Format the text to display within the formatting rect |
|
1004 |
|
1005 nscoord maxAscent = fm->MaxAscent(); |
|
1006 nscoord maxDescent = fm->MaxDescent(); |
|
1007 nscoord height = fm->MaxHeight(); |
|
1008 |
|
1009 // XXX It would be nice if there was a way to have the font metrics tell |
|
1010 // use where to break the text given a maximum width. At a minimum we need |
|
1011 // to be able to get the break character... |
|
1012 const char16_t* str = aAltText.get(); |
|
1013 int32_t strLen = aAltText.Length(); |
|
1014 nscoord y = aRect.y; |
|
1015 |
|
1016 if (!aPresContext->BidiEnabled() && HasRTLChars(aAltText)) { |
|
1017 aPresContext->SetBidiEnabled(); |
|
1018 } |
|
1019 |
|
1020 // Always show the first line, even if we have to clip it below |
|
1021 bool firstLine = true; |
|
1022 while ((strLen > 0) && (firstLine || (y + maxDescent) < aRect.YMost())) { |
|
1023 // Determine how much of the text to display on this line |
|
1024 uint32_t maxFit; // number of characters that fit |
|
1025 nscoord strWidth = MeasureString(str, strLen, aRect.width, maxFit, |
|
1026 aRenderingContext); |
|
1027 |
|
1028 // Display the text |
|
1029 nsresult rv = NS_ERROR_FAILURE; |
|
1030 |
|
1031 if (aPresContext->BidiEnabled()) { |
|
1032 const nsStyleVisibility* vis = StyleVisibility(); |
|
1033 if (vis->mDirection == NS_STYLE_DIRECTION_RTL) |
|
1034 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_RTL, |
|
1035 aPresContext, aRenderingContext, |
|
1036 aRenderingContext, |
|
1037 aRect.XMost() - strWidth, y + maxAscent); |
|
1038 else |
|
1039 rv = nsBidiPresUtils::RenderText(str, maxFit, NSBIDI_LTR, |
|
1040 aPresContext, aRenderingContext, |
|
1041 aRenderingContext, |
|
1042 aRect.x, y + maxAscent); |
|
1043 } |
|
1044 if (NS_FAILED(rv)) |
|
1045 aRenderingContext.DrawString(str, maxFit, aRect.x, y + maxAscent); |
|
1046 |
|
1047 // Move to the next line |
|
1048 str += maxFit; |
|
1049 strLen -= maxFit; |
|
1050 y += height; |
|
1051 firstLine = false; |
|
1052 } |
|
1053 } |
|
1054 |
|
1055 struct nsRecessedBorder : public nsStyleBorder { |
|
1056 nsRecessedBorder(nscoord aBorderWidth, nsPresContext* aPresContext) |
|
1057 : nsStyleBorder(aPresContext) |
|
1058 { |
|
1059 NS_FOR_CSS_SIDES(side) { |
|
1060 // Note: use SetBorderColor here because we want to make sure |
|
1061 // the "special" flags are unset. |
|
1062 SetBorderColor(side, NS_RGB(0, 0, 0)); |
|
1063 mBorder.Side(side) = aBorderWidth; |
|
1064 // Note: use SetBorderStyle here because we want to affect |
|
1065 // mComputedBorder |
|
1066 SetBorderStyle(side, NS_STYLE_BORDER_STYLE_INSET); |
|
1067 } |
|
1068 } |
|
1069 }; |
|
1070 |
|
1071 class nsDisplayAltFeedback : public nsDisplayItem { |
|
1072 public: |
|
1073 nsDisplayAltFeedback(nsDisplayListBuilder* aBuilder, nsIFrame* aFrame) |
|
1074 : nsDisplayItem(aBuilder, aFrame) {} |
|
1075 |
|
1076 virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder, |
|
1077 bool* aSnap) MOZ_OVERRIDE |
|
1078 { |
|
1079 *aSnap = false; |
|
1080 return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame(); |
|
1081 } |
|
1082 |
|
1083 virtual void Paint(nsDisplayListBuilder* aBuilder, |
|
1084 nsRenderingContext* aCtx) MOZ_OVERRIDE |
|
1085 { |
|
1086 nsImageFrame* f = static_cast<nsImageFrame*>(mFrame); |
|
1087 EventStates state = f->GetContent()->AsElement()->State(); |
|
1088 f->DisplayAltFeedback(*aCtx, |
|
1089 mVisibleRect, |
|
1090 IMAGE_OK(state, true) |
|
1091 ? nsImageFrame::gIconLoad->mLoadingImage |
|
1092 : nsImageFrame::gIconLoad->mBrokenImage, |
|
1093 ToReferenceFrame()); |
|
1094 |
|
1095 } |
|
1096 |
|
1097 NS_DISPLAY_DECL_NAME("AltFeedback", TYPE_ALT_FEEDBACK) |
|
1098 }; |
|
1099 |
|
1100 void |
|
1101 nsImageFrame::DisplayAltFeedback(nsRenderingContext& aRenderingContext, |
|
1102 const nsRect& aDirtyRect, |
|
1103 imgIRequest* aRequest, |
|
1104 nsPoint aPt) |
|
1105 { |
|
1106 // We should definitely have a gIconLoad here. |
|
1107 NS_ABORT_IF_FALSE(gIconLoad, "How did we succeed in Init then?"); |
|
1108 |
|
1109 // Calculate the inner area |
|
1110 nsRect inner = GetInnerArea() + aPt; |
|
1111 |
|
1112 // Display a recessed one pixel border |
|
1113 nscoord borderEdgeWidth = nsPresContext::CSSPixelsToAppUnits(ALT_BORDER_WIDTH); |
|
1114 |
|
1115 // if inner area is empty, then make it big enough for at least the icon |
|
1116 if (inner.IsEmpty()){ |
|
1117 inner.SizeTo(2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH)), |
|
1118 2*(nsPresContext::CSSPixelsToAppUnits(ICON_SIZE+ICON_PADDING+ALT_BORDER_WIDTH))); |
|
1119 } |
|
1120 |
|
1121 // Make sure we have enough room to actually render the border within |
|
1122 // our frame bounds |
|
1123 if ((inner.width < 2 * borderEdgeWidth) || (inner.height < 2 * borderEdgeWidth)) { |
|
1124 return; |
|
1125 } |
|
1126 |
|
1127 // Paint the border |
|
1128 nsRecessedBorder recessedBorder(borderEdgeWidth, PresContext()); |
|
1129 nsCSSRendering::PaintBorderWithStyleBorder(PresContext(), aRenderingContext, |
|
1130 this, inner, inner, |
|
1131 recessedBorder, mStyleContext); |
|
1132 |
|
1133 // Adjust the inner rect to account for the one pixel recessed border, |
|
1134 // and a six pixel padding on each edge |
|
1135 inner.Deflate(nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH), |
|
1136 nsPresContext::CSSPixelsToAppUnits(ICON_PADDING+ALT_BORDER_WIDTH)); |
|
1137 if (inner.IsEmpty()) { |
|
1138 return; |
|
1139 } |
|
1140 |
|
1141 // Clip so we don't render outside the inner rect |
|
1142 aRenderingContext.PushState(); |
|
1143 aRenderingContext.IntersectClip(inner); |
|
1144 |
|
1145 // Check if we should display image placeholders |
|
1146 if (gIconLoad->mPrefShowPlaceholders) { |
|
1147 const nsStyleVisibility* vis = StyleVisibility(); |
|
1148 nscoord size = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE); |
|
1149 |
|
1150 bool iconUsed = false; |
|
1151 |
|
1152 // If we weren't previously displaying an icon, register ourselves |
|
1153 // as an observer for load and animation updates and flag that we're |
|
1154 // doing so now. |
|
1155 if (aRequest && !mDisplayingIcon) { |
|
1156 gIconLoad->AddIconObserver(this); |
|
1157 mDisplayingIcon = true; |
|
1158 } |
|
1159 |
|
1160 |
|
1161 // If the icon in question is loaded and decoded, draw it |
|
1162 uint32_t imageStatus = 0; |
|
1163 if (aRequest) |
|
1164 aRequest->GetImageStatus(&imageStatus); |
|
1165 if (imageStatus & imgIRequest::STATUS_FRAME_COMPLETE) { |
|
1166 nsCOMPtr<imgIContainer> imgCon; |
|
1167 aRequest->GetImage(getter_AddRefs(imgCon)); |
|
1168 NS_ABORT_IF_FALSE(imgCon, "Frame Complete, but no image container?"); |
|
1169 nsRect dest((vis->mDirection == NS_STYLE_DIRECTION_RTL) ? |
|
1170 inner.XMost() - size : inner.x, |
|
1171 inner.y, size, size); |
|
1172 nsLayoutUtils::DrawSingleImage(&aRenderingContext, imgCon, |
|
1173 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect, |
|
1174 nullptr, imgIContainer::FLAG_NONE); |
|
1175 iconUsed = true; |
|
1176 } |
|
1177 |
|
1178 // if we could not draw the icon, flag that we're waiting for it and |
|
1179 // just draw some graffiti in the mean time |
|
1180 if (!iconUsed) { |
|
1181 nscoord iconXPos = (vis->mDirection == NS_STYLE_DIRECTION_RTL) ? |
|
1182 inner.XMost() - size : inner.x; |
|
1183 nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2); |
|
1184 aRenderingContext.DrawRect(iconXPos, inner.y,size,size); |
|
1185 aRenderingContext.PushState(); |
|
1186 aRenderingContext.SetColor(NS_RGB(0xFF,0,0)); |
|
1187 aRenderingContext.FillEllipse(size/2 + iconXPos, size/2 + inner.y, |
|
1188 size/2 - twoPX, size/2 - twoPX); |
|
1189 aRenderingContext.PopState(); |
|
1190 } |
|
1191 |
|
1192 // Reduce the inner rect by the width of the icon, and leave an |
|
1193 // additional ICON_PADDING pixels for padding |
|
1194 int32_t iconWidth = nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING); |
|
1195 if (vis->mDirection != NS_STYLE_DIRECTION_RTL) |
|
1196 inner.x += iconWidth; |
|
1197 inner.width -= iconWidth; |
|
1198 } |
|
1199 |
|
1200 // If there's still room, display the alt-text |
|
1201 if (!inner.IsEmpty()) { |
|
1202 nsIContent* content = GetContent(); |
|
1203 if (content) { |
|
1204 nsXPIDLString altText; |
|
1205 nsCSSFrameConstructor::GetAlternateTextFor(content, content->Tag(), |
|
1206 altText); |
|
1207 DisplayAltText(PresContext(), aRenderingContext, altText, inner); |
|
1208 } |
|
1209 } |
|
1210 |
|
1211 aRenderingContext.PopState(); |
|
1212 } |
|
1213 |
|
1214 #ifdef DEBUG |
|
1215 static void PaintDebugImageMap(nsIFrame* aFrame, nsRenderingContext* aCtx, |
|
1216 const nsRect& aDirtyRect, nsPoint aPt) { |
|
1217 nsImageFrame* f = static_cast<nsImageFrame*>(aFrame); |
|
1218 nsRect inner = f->GetInnerArea() + aPt; |
|
1219 |
|
1220 aCtx->SetColor(NS_RGB(0, 0, 0)); |
|
1221 aCtx->PushState(); |
|
1222 aCtx->Translate(inner.TopLeft()); |
|
1223 f->GetImageMap()->Draw(aFrame, *aCtx); |
|
1224 aCtx->PopState(); |
|
1225 } |
|
1226 #endif |
|
1227 |
|
1228 void |
|
1229 nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder, |
|
1230 nsRenderingContext* aCtx) { |
|
1231 uint32_t flags = imgIContainer::FLAG_NONE; |
|
1232 if (aBuilder->ShouldSyncDecodeImages()) { |
|
1233 flags |= imgIContainer::FLAG_SYNC_DECODE; |
|
1234 } |
|
1235 if (aBuilder->IsPaintingToWindow()) { |
|
1236 flags |= imgIContainer::FLAG_HIGH_QUALITY_SCALING; |
|
1237 } |
|
1238 static_cast<nsImageFrame*>(mFrame)-> |
|
1239 PaintImage(*aCtx, ToReferenceFrame(), mVisibleRect, mImage, flags); |
|
1240 } |
|
1241 |
|
1242 void |
|
1243 nsDisplayImage::ComputeInvalidationRegion(nsDisplayListBuilder* aBuilder, |
|
1244 const nsDisplayItemGeometry* aGeometry, |
|
1245 nsRegion* aInvalidRegion) |
|
1246 { |
|
1247 if (aBuilder->ShouldSyncDecodeImages() && mImage && !mImage->IsDecoded()) { |
|
1248 bool snap; |
|
1249 aInvalidRegion->Or(*aInvalidRegion, GetBounds(aBuilder, &snap)); |
|
1250 } |
|
1251 |
|
1252 nsDisplayImageContainer::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion); |
|
1253 } |
|
1254 |
|
1255 already_AddRefed<ImageContainer> |
|
1256 nsDisplayImage::GetContainer(LayerManager* aManager, |
|
1257 nsDisplayListBuilder* aBuilder) |
|
1258 { |
|
1259 nsRefPtr<ImageContainer> container; |
|
1260 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container)); |
|
1261 NS_ENSURE_SUCCESS(rv, nullptr); |
|
1262 return container.forget(); |
|
1263 } |
|
1264 |
|
1265 gfxRect |
|
1266 nsDisplayImage::GetDestRect() |
|
1267 { |
|
1268 int32_t factor = mFrame->PresContext()->AppUnitsPerDevPixel(); |
|
1269 nsImageFrame* imageFrame = static_cast<nsImageFrame*>(mFrame); |
|
1270 |
|
1271 nsRect dest = imageFrame->GetInnerArea() + ToReferenceFrame(); |
|
1272 gfxRect destRect(dest.x, dest.y, dest.width, dest.height); |
|
1273 destRect.ScaleInverse(factor); |
|
1274 |
|
1275 return destRect; |
|
1276 } |
|
1277 |
|
1278 LayerState |
|
1279 nsDisplayImage::GetLayerState(nsDisplayListBuilder* aBuilder, |
|
1280 LayerManager* aManager, |
|
1281 const ContainerLayerParameters& aParameters) |
|
1282 { |
|
1283 bool animated = false; |
|
1284 if (!nsLayoutUtils::AnimatedImageLayersEnabled() || |
|
1285 mImage->GetType() != imgIContainer::TYPE_RASTER || |
|
1286 NS_FAILED(mImage->GetAnimated(&animated)) || |
|
1287 !animated) { |
|
1288 if (!aManager->IsCompositingCheap() || |
|
1289 !nsLayoutUtils::GPUImageScalingEnabled()) { |
|
1290 return LAYER_NONE; |
|
1291 } |
|
1292 } |
|
1293 |
|
1294 if (!animated) { |
|
1295 int32_t imageWidth; |
|
1296 int32_t imageHeight; |
|
1297 mImage->GetWidth(&imageWidth); |
|
1298 mImage->GetHeight(&imageHeight); |
|
1299 |
|
1300 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!"); |
|
1301 |
|
1302 gfxRect destRect = GetDestRect(); |
|
1303 |
|
1304 destRect.width *= aParameters.mXScale; |
|
1305 destRect.height *= aParameters.mYScale; |
|
1306 |
|
1307 // Calculate the scaling factor for the frame. |
|
1308 gfxSize scale = gfxSize(destRect.width / imageWidth, |
|
1309 destRect.height / imageHeight); |
|
1310 |
|
1311 // If we are not scaling at all, no point in separating this into a layer. |
|
1312 if (scale.width == 1.0f && scale.height == 1.0f) { |
|
1313 return LAYER_NONE; |
|
1314 } |
|
1315 |
|
1316 // If the target size is pretty small, no point in using a layer. |
|
1317 if (destRect.width * destRect.height < 64 * 64) { |
|
1318 return LAYER_NONE; |
|
1319 } |
|
1320 } |
|
1321 |
|
1322 nsRefPtr<ImageContainer> container; |
|
1323 mImage->GetImageContainer(aManager, getter_AddRefs(container)); |
|
1324 if (!container) { |
|
1325 return LAYER_NONE; |
|
1326 } |
|
1327 |
|
1328 return LAYER_ACTIVE; |
|
1329 } |
|
1330 |
|
1331 already_AddRefed<Layer> |
|
1332 nsDisplayImage::BuildLayer(nsDisplayListBuilder* aBuilder, |
|
1333 LayerManager* aManager, |
|
1334 const ContainerLayerParameters& aParameters) |
|
1335 { |
|
1336 nsRefPtr<ImageContainer> container; |
|
1337 nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container)); |
|
1338 NS_ENSURE_SUCCESS(rv, nullptr); |
|
1339 |
|
1340 nsRefPtr<ImageLayer> layer = static_cast<ImageLayer*> |
|
1341 (aManager->GetLayerBuilder()->GetLeafLayerFor(aBuilder, this)); |
|
1342 if (!layer) { |
|
1343 layer = aManager->CreateImageLayer(); |
|
1344 if (!layer) |
|
1345 return nullptr; |
|
1346 } |
|
1347 layer->SetContainer(container); |
|
1348 ConfigureLayer(layer, aParameters.mOffset); |
|
1349 return layer.forget(); |
|
1350 } |
|
1351 |
|
1352 void |
|
1353 nsDisplayImage::ConfigureLayer(ImageLayer *aLayer, const nsIntPoint& aOffset) |
|
1354 { |
|
1355 aLayer->SetFilter(nsLayoutUtils::GetGraphicsFilterForFrame(mFrame)); |
|
1356 |
|
1357 int32_t imageWidth; |
|
1358 int32_t imageHeight; |
|
1359 mImage->GetWidth(&imageWidth); |
|
1360 mImage->GetHeight(&imageHeight); |
|
1361 |
|
1362 NS_ASSERTION(imageWidth != 0 && imageHeight != 0, "Invalid image size!"); |
|
1363 |
|
1364 const gfxRect destRect = GetDestRect(); |
|
1365 |
|
1366 gfx::Matrix transform; |
|
1367 gfxPoint p = destRect.TopLeft() + aOffset; |
|
1368 transform.Translate(p.x, p.y); |
|
1369 transform.Scale(destRect.Width()/imageWidth, |
|
1370 destRect.Height()/imageHeight); |
|
1371 aLayer->SetBaseTransform(gfx::Matrix4x4::From2D(transform)); |
|
1372 aLayer->SetVisibleRegion(nsIntRect(0, 0, imageWidth, imageHeight)); |
|
1373 } |
|
1374 |
|
1375 void |
|
1376 nsImageFrame::PaintImage(nsRenderingContext& aRenderingContext, nsPoint aPt, |
|
1377 const nsRect& aDirtyRect, imgIContainer* aImage, |
|
1378 uint32_t aFlags) |
|
1379 { |
|
1380 // Render the image into our content area (the area inside |
|
1381 // the borders and padding) |
|
1382 NS_ASSERTION(GetInnerArea().width == mComputedSize.width, "bad width"); |
|
1383 nsRect inner = GetInnerArea() + aPt; |
|
1384 nsRect dest(inner.TopLeft(), mComputedSize); |
|
1385 dest.y -= GetContinuationOffset(); |
|
1386 |
|
1387 nsLayoutUtils::DrawSingleImage(&aRenderingContext, aImage, |
|
1388 nsLayoutUtils::GetGraphicsFilterForFrame(this), dest, aDirtyRect, |
|
1389 nullptr, aFlags); |
|
1390 |
|
1391 nsImageMap* map = GetImageMap(); |
|
1392 if (nullptr != map) { |
|
1393 aRenderingContext.PushState(); |
|
1394 aRenderingContext.Translate(inner.TopLeft()); |
|
1395 aRenderingContext.SetColor(NS_RGB(255, 255, 255)); |
|
1396 aRenderingContext.SetLineStyle(nsLineStyle_kSolid); |
|
1397 map->Draw(this, aRenderingContext); |
|
1398 aRenderingContext.SetColor(NS_RGB(0, 0, 0)); |
|
1399 aRenderingContext.SetLineStyle(nsLineStyle_kDotted); |
|
1400 map->Draw(this, aRenderingContext); |
|
1401 aRenderingContext.PopState(); |
|
1402 } |
|
1403 } |
|
1404 |
|
1405 void |
|
1406 nsImageFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, |
|
1407 const nsRect& aDirtyRect, |
|
1408 const nsDisplayListSet& aLists) |
|
1409 { |
|
1410 if (!IsVisibleForPainting(aBuilder)) |
|
1411 return; |
|
1412 |
|
1413 DisplayBorderBackgroundOutline(aBuilder, aLists); |
|
1414 |
|
1415 DisplayListClipState::AutoClipContainingBlockDescendantsToContentBox |
|
1416 clip(aBuilder, this, DisplayListClipState::ASSUME_DRAWING_RESTRICTED_TO_CONTENT_RECT); |
|
1417 |
|
1418 if (mComputedSize.width != 0 && mComputedSize.height != 0) { |
|
1419 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
1420 NS_ASSERTION(imageLoader, "Not an image loading content?"); |
|
1421 |
|
1422 nsCOMPtr<imgIRequest> currentRequest; |
|
1423 if (imageLoader) { |
|
1424 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
|
1425 getter_AddRefs(currentRequest)); |
|
1426 } |
|
1427 |
|
1428 EventStates contentState = mContent->AsElement()->State(); |
|
1429 bool imageOK = IMAGE_OK(contentState, true); |
|
1430 |
|
1431 // XXX(seth): The SizeIsAvailable check here should not be necessary - the |
|
1432 // intention is that a non-null mImage means we have a size, but there is |
|
1433 // currently some code that violates this invariant. |
|
1434 if (!imageOK || !mImage || !SizeIsAvailable(currentRequest)) { |
|
1435 // No image yet, or image load failed. Draw the alt-text and an icon |
|
1436 // indicating the status |
|
1437 aLists.Content()->AppendNewToTop(new (aBuilder) |
|
1438 nsDisplayAltFeedback(aBuilder, this)); |
|
1439 } else { |
|
1440 aLists.Content()->AppendNewToTop(new (aBuilder) |
|
1441 nsDisplayImage(aBuilder, this, mImage)); |
|
1442 |
|
1443 // If we were previously displaying an icon, we're not anymore |
|
1444 if (mDisplayingIcon) { |
|
1445 gIconLoad->RemoveIconObserver(this); |
|
1446 mDisplayingIcon = false; |
|
1447 } |
|
1448 |
|
1449 #ifdef DEBUG |
|
1450 if (GetShowFrameBorders() && GetImageMap()) { |
|
1451 aLists.Outlines()->AppendNewToTop(new (aBuilder) |
|
1452 nsDisplayGeneric(aBuilder, this, PaintDebugImageMap, "DebugImageMap", |
|
1453 nsDisplayItem::TYPE_DEBUG_IMAGE_MAP)); |
|
1454 } |
|
1455 #endif |
|
1456 } |
|
1457 } |
|
1458 |
|
1459 if (ShouldDisplaySelection()) { |
|
1460 DisplaySelectionOverlay(aBuilder, aLists.Content(), |
|
1461 nsISelectionDisplay::DISPLAY_IMAGES); |
|
1462 } |
|
1463 } |
|
1464 |
|
1465 bool |
|
1466 nsImageFrame::ShouldDisplaySelection() |
|
1467 { |
|
1468 // XXX what on EARTH is this code for? |
|
1469 nsresult result; |
|
1470 nsPresContext* presContext = PresContext(); |
|
1471 int16_t displaySelection = presContext->PresShell()->GetSelectionFlags(); |
|
1472 if (!(displaySelection & nsISelectionDisplay::DISPLAY_IMAGES)) |
|
1473 return false;//no need to check the blue border, we cannot be drawn selected |
|
1474 //insert hook here for image selection drawing |
|
1475 #if IMAGE_EDITOR_CHECK |
|
1476 //check to see if this frame is in an editor context |
|
1477 //isEditor check. this needs to be changed to have better way to check |
|
1478 if (displaySelection == nsISelectionDisplay::DISPLAY_ALL) |
|
1479 { |
|
1480 nsCOMPtr<nsISelectionController> selCon; |
|
1481 result = GetSelectionController(presContext, getter_AddRefs(selCon)); |
|
1482 if (NS_SUCCEEDED(result) && selCon) |
|
1483 { |
|
1484 nsCOMPtr<nsISelection> selection; |
|
1485 result = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); |
|
1486 if (NS_SUCCEEDED(result) && selection) |
|
1487 { |
|
1488 int32_t rangeCount; |
|
1489 selection->GetRangeCount(&rangeCount); |
|
1490 if (rangeCount == 1) //if not one then let code drop to nsFrame::Paint |
|
1491 { |
|
1492 nsCOMPtr<nsIContent> parentContent = mContent->GetParent(); |
|
1493 if (parentContent) |
|
1494 { |
|
1495 int32_t thisOffset = parentContent->IndexOf(mContent); |
|
1496 nsCOMPtr<nsIDOMNode> parentNode = do_QueryInterface(parentContent); |
|
1497 nsCOMPtr<nsIDOMNode> rangeNode; |
|
1498 int32_t rangeOffset; |
|
1499 nsCOMPtr<nsIDOMRange> range; |
|
1500 selection->GetRangeAt(0,getter_AddRefs(range)); |
|
1501 if (range) |
|
1502 { |
|
1503 range->GetStartContainer(getter_AddRefs(rangeNode)); |
|
1504 range->GetStartOffset(&rangeOffset); |
|
1505 |
|
1506 if (parentNode && rangeNode && (rangeNode == parentNode) && rangeOffset == thisOffset) |
|
1507 { |
|
1508 range->GetEndContainer(getter_AddRefs(rangeNode)); |
|
1509 range->GetEndOffset(&rangeOffset); |
|
1510 if ((rangeNode == parentNode) && (rangeOffset == (thisOffset +1))) //+1 since that would mean this whole content is selected only |
|
1511 return false; //do not allow nsFrame do draw any further selection |
|
1512 } |
|
1513 } |
|
1514 } |
|
1515 } |
|
1516 } |
|
1517 } |
|
1518 } |
|
1519 #endif |
|
1520 return true; |
|
1521 } |
|
1522 |
|
1523 nsImageMap* |
|
1524 nsImageFrame::GetImageMap() |
|
1525 { |
|
1526 if (!mImageMap) { |
|
1527 nsIContent* map = GetMapElement(); |
|
1528 if (map) { |
|
1529 mImageMap = new nsImageMap(); |
|
1530 NS_ADDREF(mImageMap); |
|
1531 mImageMap->Init(this, map); |
|
1532 } |
|
1533 } |
|
1534 |
|
1535 return mImageMap; |
|
1536 } |
|
1537 |
|
1538 bool |
|
1539 nsImageFrame::IsServerImageMap() |
|
1540 { |
|
1541 return mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::ismap); |
|
1542 } |
|
1543 |
|
1544 // Translate an point that is relative to our frame |
|
1545 // into a localized pixel coordinate that is relative to the |
|
1546 // content area of this frame (inside the border+padding). |
|
1547 void |
|
1548 nsImageFrame::TranslateEventCoords(const nsPoint& aPoint, |
|
1549 nsIntPoint& aResult) |
|
1550 { |
|
1551 nscoord x = aPoint.x; |
|
1552 nscoord y = aPoint.y; |
|
1553 |
|
1554 // Subtract out border and padding here so that the coordinates are |
|
1555 // now relative to the content area of this frame. |
|
1556 nsRect inner = GetInnerArea(); |
|
1557 x -= inner.x; |
|
1558 y -= inner.y; |
|
1559 |
|
1560 aResult.x = nsPresContext::AppUnitsToIntCSSPixels(x); |
|
1561 aResult.y = nsPresContext::AppUnitsToIntCSSPixels(y); |
|
1562 } |
|
1563 |
|
1564 bool |
|
1565 nsImageFrame::GetAnchorHREFTargetAndNode(nsIURI** aHref, nsString& aTarget, |
|
1566 nsIContent** aNode) |
|
1567 { |
|
1568 bool status = false; |
|
1569 aTarget.Truncate(); |
|
1570 *aHref = nullptr; |
|
1571 *aNode = nullptr; |
|
1572 |
|
1573 // Walk up the content tree, looking for an nsIDOMAnchorElement |
|
1574 for (nsIContent* content = mContent->GetParent(); |
|
1575 content; content = content->GetParent()) { |
|
1576 nsCOMPtr<dom::Link> link(do_QueryInterface(content)); |
|
1577 if (link) { |
|
1578 nsCOMPtr<nsIURI> href = content->GetHrefURI(); |
|
1579 if (href) { |
|
1580 href->Clone(aHref); |
|
1581 } |
|
1582 status = (*aHref != nullptr); |
|
1583 |
|
1584 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(content)); |
|
1585 if (anchor) { |
|
1586 anchor->GetTarget(aTarget); |
|
1587 } |
|
1588 NS_ADDREF(*aNode = content); |
|
1589 break; |
|
1590 } |
|
1591 } |
|
1592 return status; |
|
1593 } |
|
1594 |
|
1595 nsresult |
|
1596 nsImageFrame::GetContentForEvent(WidgetEvent* aEvent, |
|
1597 nsIContent** aContent) |
|
1598 { |
|
1599 NS_ENSURE_ARG_POINTER(aContent); |
|
1600 |
|
1601 nsIFrame* f = nsLayoutUtils::GetNonGeneratedAncestor(this); |
|
1602 if (f != this) { |
|
1603 return f->GetContentForEvent(aEvent, aContent); |
|
1604 } |
|
1605 |
|
1606 // XXX We need to make this special check for area element's capturing the |
|
1607 // mouse due to bug 135040. Remove it once that's fixed. |
|
1608 nsIContent* capturingContent = |
|
1609 aEvent->HasMouseEventMessage() ? nsIPresShell::GetCapturingContent() : |
|
1610 nullptr; |
|
1611 if (capturingContent && capturingContent->GetPrimaryFrame() == this) { |
|
1612 *aContent = capturingContent; |
|
1613 NS_IF_ADDREF(*aContent); |
|
1614 return NS_OK; |
|
1615 } |
|
1616 |
|
1617 nsImageMap* map = GetImageMap(); |
|
1618 |
|
1619 if (nullptr != map) { |
|
1620 nsIntPoint p; |
|
1621 TranslateEventCoords( |
|
1622 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p); |
|
1623 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y); |
|
1624 if (area) { |
|
1625 area.forget(aContent); |
|
1626 return NS_OK; |
|
1627 } |
|
1628 } |
|
1629 |
|
1630 *aContent = GetContent(); |
|
1631 NS_IF_ADDREF(*aContent); |
|
1632 return NS_OK; |
|
1633 } |
|
1634 |
|
1635 // XXX what should clicks on transparent pixels do? |
|
1636 nsresult |
|
1637 nsImageFrame::HandleEvent(nsPresContext* aPresContext, |
|
1638 WidgetGUIEvent* aEvent, |
|
1639 nsEventStatus* aEventStatus) |
|
1640 { |
|
1641 NS_ENSURE_ARG_POINTER(aEventStatus); |
|
1642 |
|
1643 if ((aEvent->message == NS_MOUSE_BUTTON_UP && |
|
1644 aEvent->AsMouseEvent()->button == WidgetMouseEvent::eLeftButton) || |
|
1645 aEvent->message == NS_MOUSE_MOVE) { |
|
1646 nsImageMap* map = GetImageMap(); |
|
1647 bool isServerMap = IsServerImageMap(); |
|
1648 if ((nullptr != map) || isServerMap) { |
|
1649 nsIntPoint p; |
|
1650 TranslateEventCoords( |
|
1651 nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, this), p); |
|
1652 bool inside = false; |
|
1653 // Even though client-side image map triggering happens |
|
1654 // through content, we need to make sure we're not inside |
|
1655 // (in case we deal with a case of both client-side and |
|
1656 // sever-side on the same image - it happens!) |
|
1657 if (nullptr != map) { |
|
1658 inside = !!map->GetArea(p.x, p.y); |
|
1659 } |
|
1660 |
|
1661 if (!inside && isServerMap) { |
|
1662 |
|
1663 // Server side image maps use the href in a containing anchor |
|
1664 // element to provide the basis for the destination url. |
|
1665 nsCOMPtr<nsIURI> uri; |
|
1666 nsAutoString target; |
|
1667 nsCOMPtr<nsIContent> anchorNode; |
|
1668 if (GetAnchorHREFTargetAndNode(getter_AddRefs(uri), target, |
|
1669 getter_AddRefs(anchorNode))) { |
|
1670 // XXX if the mouse is over/clicked in the border/padding area |
|
1671 // we should probably just pretend nothing happened. Nav4 |
|
1672 // keeps the x,y coordinates positive as we do; IE doesn't |
|
1673 // bother. Both of them send the click through even when the |
|
1674 // mouse is over the border. |
|
1675 if (p.x < 0) p.x = 0; |
|
1676 if (p.y < 0) p.y = 0; |
|
1677 nsAutoCString spec; |
|
1678 uri->GetSpec(spec); |
|
1679 spec += nsPrintfCString("?%d,%d", p.x, p.y); |
|
1680 uri->SetSpec(spec); |
|
1681 |
|
1682 bool clicked = false; |
|
1683 if (aEvent->message == NS_MOUSE_BUTTON_UP) { |
|
1684 *aEventStatus = nsEventStatus_eConsumeDoDefault; |
|
1685 clicked = true; |
|
1686 } |
|
1687 nsContentUtils::TriggerLink(anchorNode, aPresContext, uri, target, |
|
1688 clicked, true, true); |
|
1689 } |
|
1690 } |
|
1691 } |
|
1692 } |
|
1693 |
|
1694 return nsSplittableFrame::HandleEvent(aPresContext, aEvent, aEventStatus); |
|
1695 } |
|
1696 |
|
1697 nsresult |
|
1698 nsImageFrame::GetCursor(const nsPoint& aPoint, |
|
1699 nsIFrame::Cursor& aCursor) |
|
1700 { |
|
1701 nsImageMap* map = GetImageMap(); |
|
1702 if (nullptr != map) { |
|
1703 nsIntPoint p; |
|
1704 TranslateEventCoords(aPoint, p); |
|
1705 nsCOMPtr<nsIContent> area = map->GetArea(p.x, p.y); |
|
1706 if (area) { |
|
1707 // Use the cursor from the style of the *area* element. |
|
1708 // XXX Using the image as the parent style context isn't |
|
1709 // technically correct, but it's probably the right thing to do |
|
1710 // here, since it means that areas on which the cursor isn't |
|
1711 // specified will inherit the style from the image. |
|
1712 nsRefPtr<nsStyleContext> areaStyle = |
|
1713 PresContext()->PresShell()->StyleSet()-> |
|
1714 ResolveStyleFor(area->AsElement(), StyleContext()); |
|
1715 FillCursorInformationFromStyle(areaStyle->StyleUserInterface(), |
|
1716 aCursor); |
|
1717 if (NS_STYLE_CURSOR_AUTO == aCursor.mCursor) { |
|
1718 aCursor.mCursor = NS_STYLE_CURSOR_DEFAULT; |
|
1719 } |
|
1720 return NS_OK; |
|
1721 } |
|
1722 } |
|
1723 return nsFrame::GetCursor(aPoint, aCursor); |
|
1724 } |
|
1725 |
|
1726 nsresult |
|
1727 nsImageFrame::AttributeChanged(int32_t aNameSpaceID, |
|
1728 nsIAtom* aAttribute, |
|
1729 int32_t aModType) |
|
1730 { |
|
1731 nsresult rv = nsSplittableFrame::AttributeChanged(aNameSpaceID, |
|
1732 aAttribute, aModType); |
|
1733 if (NS_FAILED(rv)) { |
|
1734 return rv; |
|
1735 } |
|
1736 if (nsGkAtoms::alt == aAttribute) |
|
1737 { |
|
1738 PresContext()->PresShell()->FrameNeedsReflow(this, |
|
1739 nsIPresShell::eStyleChange, |
|
1740 NS_FRAME_IS_DIRTY); |
|
1741 } |
|
1742 |
|
1743 return NS_OK; |
|
1744 } |
|
1745 |
|
1746 nsIAtom* |
|
1747 nsImageFrame::GetType() const |
|
1748 { |
|
1749 return nsGkAtoms::imageFrame; |
|
1750 } |
|
1751 |
|
1752 #ifdef DEBUG_FRAME_DUMP |
|
1753 nsresult |
|
1754 nsImageFrame::GetFrameName(nsAString& aResult) const |
|
1755 { |
|
1756 return MakeFrameName(NS_LITERAL_STRING("ImageFrame"), aResult); |
|
1757 } |
|
1758 |
|
1759 void |
|
1760 nsImageFrame::List(FILE* out, const char* aPrefix, uint32_t aFlags) const |
|
1761 { |
|
1762 nsCString str; |
|
1763 ListGeneric(str, aPrefix, aFlags); |
|
1764 |
|
1765 // output the img src url |
|
1766 nsCOMPtr<nsIImageLoadingContent> imageLoader = do_QueryInterface(mContent); |
|
1767 if (imageLoader) { |
|
1768 nsCOMPtr<imgIRequest> currentRequest; |
|
1769 imageLoader->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, |
|
1770 getter_AddRefs(currentRequest)); |
|
1771 if (currentRequest) { |
|
1772 nsCOMPtr<nsIURI> uri; |
|
1773 currentRequest->GetURI(getter_AddRefs(uri)); |
|
1774 nsAutoCString uristr; |
|
1775 uri->GetAsciiSpec(uristr); |
|
1776 str += nsPrintfCString(" [src=%s]", uristr.get()); |
|
1777 } |
|
1778 } |
|
1779 fprintf_stderr(out, "%s\n", str.get()); |
|
1780 } |
|
1781 #endif |
|
1782 |
|
1783 int |
|
1784 nsImageFrame::GetLogicalSkipSides(const nsHTMLReflowState* aReflowState) const |
|
1785 { |
|
1786 int skip = 0; |
|
1787 if (nullptr != GetPrevInFlow()) { |
|
1788 skip |= LOGICAL_SIDE_B_START; |
|
1789 } |
|
1790 if (nullptr != GetNextInFlow()) { |
|
1791 skip |= LOGICAL_SIDE_B_END; |
|
1792 } |
|
1793 return skip; |
|
1794 } |
|
1795 |
|
1796 nsresult |
|
1797 nsImageFrame::GetIntrinsicImageSize(nsSize& aSize) |
|
1798 { |
|
1799 if (mIntrinsicSize.width.GetUnit() == eStyleUnit_Coord && |
|
1800 mIntrinsicSize.height.GetUnit() == eStyleUnit_Coord) { |
|
1801 aSize.SizeTo(mIntrinsicSize.width.GetCoordValue(), |
|
1802 mIntrinsicSize.height.GetCoordValue()); |
|
1803 return NS_OK; |
|
1804 } |
|
1805 |
|
1806 return NS_ERROR_FAILURE; |
|
1807 } |
|
1808 |
|
1809 nsresult |
|
1810 nsImageFrame::LoadIcon(const nsAString& aSpec, |
|
1811 nsPresContext *aPresContext, |
|
1812 imgRequestProxy** aRequest) |
|
1813 { |
|
1814 nsresult rv = NS_OK; |
|
1815 NS_PRECONDITION(!aSpec.IsEmpty(), "What happened??"); |
|
1816 NS_PRECONDITION(aPresContext, "NULL PresContext"); |
|
1817 |
|
1818 if (!sIOService) { |
|
1819 rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); |
|
1820 NS_ENSURE_SUCCESS(rv, rv); |
|
1821 } |
|
1822 |
|
1823 nsCOMPtr<nsIURI> realURI; |
|
1824 SpecToURI(aSpec, sIOService, getter_AddRefs(realURI)); |
|
1825 |
|
1826 nsRefPtr<imgLoader> il = |
|
1827 nsContentUtils::GetImgLoaderForDocument(aPresContext->Document()); |
|
1828 |
|
1829 nsCOMPtr<nsILoadGroup> loadGroup; |
|
1830 GetLoadGroup(aPresContext, getter_AddRefs(loadGroup)); |
|
1831 |
|
1832 // For icon loads, we don't need to merge with the loadgroup flags |
|
1833 nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL; |
|
1834 |
|
1835 nsCOMPtr<nsIURI> firstPartyIsolationURI; |
|
1836 nsCOMPtr<mozIThirdPartyUtil> thirdPartySvc |
|
1837 = do_GetService(THIRDPARTYUTIL_CONTRACTID); |
|
1838 // XXX: Should we pass the loadgroup, too? Is document ever likely |
|
1839 // to be unset? |
|
1840 thirdPartySvc->GetFirstPartyIsolationURI(nullptr, aPresContext->Document(), |
|
1841 getter_AddRefs(firstPartyIsolationURI)); |
|
1842 |
|
1843 return il->LoadImage(realURI, /* icon URI */ |
|
1844 firstPartyIsolationURI, /* initial document URI; this is only |
|
1845 relevant for cookies, so does not |
|
1846 apply to icons. */ |
|
1847 nullptr, /* referrer (not relevant for icons) */ |
|
1848 nullptr, /* principal (not relevant for icons) */ |
|
1849 loadGroup, |
|
1850 gIconLoad, |
|
1851 nullptr, /* Not associated with any particular document */ |
|
1852 loadFlags, |
|
1853 nullptr, |
|
1854 nullptr, /* channel policy not needed */ |
|
1855 EmptyString(), |
|
1856 aRequest); |
|
1857 } |
|
1858 |
|
1859 void |
|
1860 nsImageFrame::GetDocumentCharacterSet(nsACString& aCharset) const |
|
1861 { |
|
1862 if (mContent) { |
|
1863 NS_ASSERTION(mContent->GetDocument(), |
|
1864 "Frame still alive after content removed from document!"); |
|
1865 aCharset = mContent->GetDocument()->GetDocumentCharacterSet(); |
|
1866 } |
|
1867 } |
|
1868 |
|
1869 void |
|
1870 nsImageFrame::SpecToURI(const nsAString& aSpec, nsIIOService *aIOService, |
|
1871 nsIURI **aURI) |
|
1872 { |
|
1873 nsCOMPtr<nsIURI> baseURI; |
|
1874 if (mContent) { |
|
1875 baseURI = mContent->GetBaseURI(); |
|
1876 } |
|
1877 nsAutoCString charset; |
|
1878 GetDocumentCharacterSet(charset); |
|
1879 NS_NewURI(aURI, aSpec, |
|
1880 charset.IsEmpty() ? nullptr : charset.get(), |
|
1881 baseURI, aIOService); |
|
1882 } |
|
1883 |
|
1884 void |
|
1885 nsImageFrame::GetLoadGroup(nsPresContext *aPresContext, nsILoadGroup **aLoadGroup) |
|
1886 { |
|
1887 if (!aPresContext) |
|
1888 return; |
|
1889 |
|
1890 NS_PRECONDITION(nullptr != aLoadGroup, "null OUT parameter pointer"); |
|
1891 |
|
1892 nsIPresShell *shell = aPresContext->GetPresShell(); |
|
1893 |
|
1894 if (!shell) |
|
1895 return; |
|
1896 |
|
1897 nsIDocument *doc = shell->GetDocument(); |
|
1898 if (!doc) |
|
1899 return; |
|
1900 |
|
1901 *aLoadGroup = doc->GetDocumentLoadGroup().take(); |
|
1902 } |
|
1903 |
|
1904 nsresult nsImageFrame::LoadIcons(nsPresContext *aPresContext) |
|
1905 { |
|
1906 NS_ASSERTION(!gIconLoad, "called LoadIcons twice"); |
|
1907 |
|
1908 NS_NAMED_LITERAL_STRING(loadingSrc,"resource://gre-resources/loading-image.png"); |
|
1909 NS_NAMED_LITERAL_STRING(brokenSrc,"resource://gre-resources/broken-image.png"); |
|
1910 |
|
1911 gIconLoad = new IconLoad(); |
|
1912 NS_ADDREF(gIconLoad); |
|
1913 |
|
1914 nsresult rv; |
|
1915 // create a loader and load the images |
|
1916 rv = LoadIcon(loadingSrc, |
|
1917 aPresContext, |
|
1918 getter_AddRefs(gIconLoad->mLoadingImage)); |
|
1919 if (NS_FAILED(rv)) { |
|
1920 return rv; |
|
1921 } |
|
1922 |
|
1923 rv = LoadIcon(brokenSrc, |
|
1924 aPresContext, |
|
1925 getter_AddRefs(gIconLoad->mBrokenImage)); |
|
1926 return rv; |
|
1927 } |
|
1928 |
|
1929 NS_IMPL_ISUPPORTS(nsImageFrame::IconLoad, nsIObserver, |
|
1930 imgINotificationObserver) |
|
1931 |
|
1932 static const char* kIconLoadPrefs[] = { |
|
1933 "browser.display.force_inline_alttext", |
|
1934 "browser.display.show_image_placeholders", |
|
1935 nullptr |
|
1936 }; |
|
1937 |
|
1938 nsImageFrame::IconLoad::IconLoad() |
|
1939 { |
|
1940 // register observers |
|
1941 Preferences::AddStrongObservers(this, kIconLoadPrefs); |
|
1942 GetPrefs(); |
|
1943 } |
|
1944 |
|
1945 void |
|
1946 nsImageFrame::IconLoad::Shutdown() |
|
1947 { |
|
1948 Preferences::RemoveObservers(this, kIconLoadPrefs); |
|
1949 // in case the pref service releases us later |
|
1950 if (mLoadingImage) { |
|
1951 mLoadingImage->CancelAndForgetObserver(NS_ERROR_FAILURE); |
|
1952 mLoadingImage = nullptr; |
|
1953 } |
|
1954 if (mBrokenImage) { |
|
1955 mBrokenImage->CancelAndForgetObserver(NS_ERROR_FAILURE); |
|
1956 mBrokenImage = nullptr; |
|
1957 } |
|
1958 } |
|
1959 |
|
1960 NS_IMETHODIMP |
|
1961 nsImageFrame::IconLoad::Observe(nsISupports *aSubject, const char* aTopic, |
|
1962 const char16_t* aData) |
|
1963 { |
|
1964 NS_ASSERTION(!nsCRT::strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID), |
|
1965 "wrong topic"); |
|
1966 #ifdef DEBUG |
|
1967 // assert |aData| is one of our prefs. |
|
1968 for (uint32_t i = 0; i < ArrayLength(kIconLoadPrefs) || |
|
1969 (NS_NOTREACHED("wrong pref"), false); ++i) |
|
1970 if (NS_ConvertASCIItoUTF16(kIconLoadPrefs[i]) == nsDependentString(aData)) |
|
1971 break; |
|
1972 #endif |
|
1973 |
|
1974 GetPrefs(); |
|
1975 return NS_OK; |
|
1976 } |
|
1977 |
|
1978 void nsImageFrame::IconLoad::GetPrefs() |
|
1979 { |
|
1980 mPrefForceInlineAltText = |
|
1981 Preferences::GetBool("browser.display.force_inline_alttext"); |
|
1982 |
|
1983 mPrefShowPlaceholders = |
|
1984 Preferences::GetBool("browser.display.show_image_placeholders", true); |
|
1985 } |
|
1986 |
|
1987 NS_IMETHODIMP |
|
1988 nsImageFrame::IconLoad::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) |
|
1989 { |
|
1990 if (aType != imgINotificationObserver::LOAD_COMPLETE && |
|
1991 aType != imgINotificationObserver::FRAME_UPDATE) { |
|
1992 return NS_OK; |
|
1993 } |
|
1994 |
|
1995 nsTObserverArray<nsImageFrame*>::ForwardIterator iter(mIconObservers); |
|
1996 nsImageFrame *frame; |
|
1997 while (iter.HasMore()) { |
|
1998 frame = iter.GetNext(); |
|
1999 frame->InvalidateFrame(); |
|
2000 } |
|
2001 |
|
2002 return NS_OK; |
|
2003 } |
|
2004 |
|
2005 NS_IMPL_ISUPPORTS(nsImageListener, imgINotificationObserver) |
|
2006 |
|
2007 nsImageListener::nsImageListener(nsImageFrame *aFrame) : |
|
2008 mFrame(aFrame) |
|
2009 { |
|
2010 } |
|
2011 |
|
2012 nsImageListener::~nsImageListener() |
|
2013 { |
|
2014 } |
|
2015 |
|
2016 NS_IMETHODIMP |
|
2017 nsImageListener::Notify(imgIRequest *aRequest, int32_t aType, const nsIntRect* aData) |
|
2018 { |
|
2019 if (!mFrame) |
|
2020 return NS_ERROR_FAILURE; |
|
2021 |
|
2022 return mFrame->Notify(aRequest, aType, aData); |
|
2023 } |
|
2024 |
|
2025 static bool |
|
2026 IsInAutoWidthTableCellForQuirk(nsIFrame *aFrame) |
|
2027 { |
|
2028 if (eCompatibility_NavQuirks != aFrame->PresContext()->CompatibilityMode()) |
|
2029 return false; |
|
2030 // Check if the parent of the closest nsBlockFrame has auto width. |
|
2031 nsBlockFrame *ancestor = nsLayoutUtils::FindNearestBlockAncestor(aFrame); |
|
2032 if (ancestor->StyleContext()->GetPseudo() == nsCSSAnonBoxes::cellContent) { |
|
2033 // Assume direct parent is a table cell frame. |
|
2034 nsFrame *grandAncestor = static_cast<nsFrame*>(ancestor->GetParent()); |
|
2035 return grandAncestor && |
|
2036 grandAncestor->StylePosition()->mWidth.GetUnit() == eStyleUnit_Auto; |
|
2037 } |
|
2038 return false; |
|
2039 } |
|
2040 |
|
2041 /* virtual */ void |
|
2042 nsImageFrame::AddInlineMinWidth(nsRenderingContext *aRenderingContext, |
|
2043 nsIFrame::InlineMinWidthData *aData) |
|
2044 { |
|
2045 |
|
2046 NS_ASSERTION(GetParent(), "Must have a parent if we get here!"); |
|
2047 |
|
2048 nsIFrame* parent = GetParent(); |
|
2049 bool canBreak = |
|
2050 !CanContinueTextRun() && |
|
2051 parent->StyleText()->WhiteSpaceCanWrap(parent) && |
|
2052 !IsInAutoWidthTableCellForQuirk(this); |
|
2053 |
|
2054 if (canBreak) |
|
2055 aData->OptionallyBreak(aRenderingContext); |
|
2056 |
|
2057 aData->trailingWhitespace = 0; |
|
2058 aData->skipWhitespace = false; |
|
2059 aData->trailingTextFrame = nullptr; |
|
2060 aData->currentLine += nsLayoutUtils::IntrinsicForContainer(aRenderingContext, |
|
2061 this, nsLayoutUtils::MIN_WIDTH); |
|
2062 aData->atStartOfLine = false; |
|
2063 |
|
2064 if (canBreak) |
|
2065 aData->OptionallyBreak(aRenderingContext); |
|
2066 |
|
2067 } |