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