git-svn-id: svn://10.0.0.236/trunk@9558 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
kipp%netscape.com 1998-09-08 22:34:40 +00:00
parent 60e776c0b1
commit 429035b95e
12 changed files with 7265 additions and 0 deletions

View File

@ -0,0 +1,141 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsFrame.h"
#include "nsHTMLParts.h"
#include "nsHTMLIIDs.h"
#include "nsIPresContext.h"
#include "nsIInlineReflow.h"
#include "nsCSSLineLayout.h"
#include "nsStyleConsts.h"
#include "nsHTMLAtoms.h"
#include "nsIStyleContext.h"
#include "nsIFontMetrics.h"
#include "nsIRenderingContext.h"
class BRFrame : public nsFrame, public nsIInlineReflow {
public:
BRFrame(nsIContent* aContent, nsIFrame* aParentFrame);
// nsISupports
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
// nsIFrame
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
// nsIInlineReflow
NS_IMETHOD FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand);
NS_IMETHOD InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState);
protected:
virtual ~BRFrame();
};
nsresult
NS_NewBRFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
nsIFrame* frame = new BRFrame(aContent, aParentFrame);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
BRFrame::BRFrame(nsIContent* aContent,
nsIFrame* aParentFrame)
: nsFrame(aContent, aParentFrame)
{
}
BRFrame::~BRFrame()
{
}
NS_IMETHODIMP
BRFrame::QueryInterface(REFNSIID aIID, void** aInstancePtrResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIInlineReflowIID)) {
*aInstancePtrResult = (void*) ((nsIInlineReflow*)this);
return NS_OK;
}
return nsFrame::QueryInterface(aIID, aInstancePtrResult);
}
NS_METHOD
BRFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
if (nsIFrame::GetShowFrameBorders()) {
float p2t = aPresContext.GetPixelsToTwips();
aRenderingContext.SetColor(NS_RGB(0, 255, 255));
aRenderingContext.FillRect(0, 0, NSIntPixelsToTwips(5, p2t), mRect.height);
}
return NS_OK;
}
NS_IMETHODIMP
BRFrame::FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand)
{
aLineLayout.EndTextRun();
return NS_OK;
}
NS_IMETHODIMP
BRFrame::InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aMetrics,
const nsReflowState& aReflowState)
{
if (nsnull != aMetrics.maxElementSize) {
aMetrics.maxElementSize->width = 0;
aMetrics.maxElementSize->height = 0;
}
// We have no width, but we're the height of the default font
const nsStyleFont* font = (const nsStyleFont*)
mStyleContext->GetStyleData(eStyleStruct_Font);
nsIFontMetrics* fm = aLineLayout.mPresContext->GetMetricsFor(font->mFont);
fm->GetMaxAscent(aMetrics.ascent);
fm->GetMaxDescent(aMetrics.descent);
aMetrics.height = aMetrics.ascent + aMetrics.descent;
aMetrics.width = 0;
NS_RELEASE(fm);
// Return our inline reflow status
const nsStyleDisplay* display = (const nsStyleDisplay*)
mStyleContext->GetStyleData(eStyleStruct_Display);
PRUint32 breakType = display->mBreakType;
if (NS_STYLE_CLEAR_NONE == breakType) {
breakType = NS_STYLE_CLEAR_LINE;
}
return NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |
NS_INLINE_MAKE_BREAK_TYPE(breakType);
}

View File

@ -0,0 +1,626 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsHTMLContainer.h"
#include "nsContainerFrame.h"
#include "nsIDocument.h"
#include "nsIReflowCommand.h"
#include "nsIPresContext.h"
#include "nsIStyleContext.h"
#include "nsViewsCID.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsIContentDelegate.h"
#include "nsIWidget.h"
#include "nsHTMLIIDs.h"
#include "nsPageFrame.h"
#include "nsIRenderingContext.h"
#include "nsGUIEvent.h"
#include "nsDOMEvent.h"
#include "nsStyleConsts.h"
#include "nsIViewManager.h"
#include "nsHTMLAtoms.h"
#include "nsIEventStateManager.h"
#include "nsIDeviceContext.h"
class RootFrame : public nsContainerFrame {
public:
RootFrame(nsIContent* aContent);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus);
};
// Pseudo frame created by the root frame
class RootContentFrame : public nsContainerFrame {
public:
RootContentFrame(nsIContent* aContent, nsIFrame* aParent);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
NS_IMETHOD HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus);
protected:
void CreateFirstChild(nsIPresContext* aPresContext);
};
//----------------------------------------------------------------------
nsresult
NS_NewHTMLFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
RootFrame* frame = new RootFrame(aContent);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
RootFrame::RootFrame(nsIContent* aContent)
: nsContainerFrame(aContent, nsnull)
{
}
NS_IMETHODIMP
RootFrame::Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
NS_FRAME_TRACE_REFLOW_IN("RootFrame::Reflow");
#ifdef NS_DEBUG
PreReflowCheck();
#endif
aStatus = NS_FRAME_COMPLETE;
if (eReflowReason_Incremental == aReflowState.reason) {
// We don't expect the target of the reflow command to be the root frame
#ifdef NS_DEBUG
NS_ASSERTION(nsnull != aReflowState.reflowCommand, "no reflow command");
nsIFrame* target;
aReflowState.reflowCommand->GetTarget(target);
NS_ASSERTION(target != this, "root frame is reflow command target");
#endif
// Verify that the next frame in the reflow chain is our pseudo frame
nsIFrame* next;
aReflowState.reflowCommand->GetNext(next);
NS_ASSERTION(next == mFirstChild, "unexpected next reflow command frame");
} else {
// Do we have any children?
if (nsnull == mFirstChild) {
// No. Create a pseudo frame
NS_ASSERTION(eReflowReason_Initial == aReflowState.reason, "unexpected reflow reason");
mFirstChild = new RootContentFrame(mContent, this);
mChildCount = 1;
nsIStyleContext* style = aPresContext.ResolvePseudoStyleContextFor(nsHTMLAtoms::rootContentPseudo, this);
mFirstChild->SetStyleContext(&aPresContext,style);
NS_RELEASE(style);
}
}
// Reflow our pseudo frame. It will choose whetever height its child frame
// wants
if (nsnull != mFirstChild) {
nsReflowMetrics desiredSize(nsnull);
nsReflowState kidReflowState(mFirstChild, aReflowState, aReflowState.maxSize);
mFirstChild->WillReflow(aPresContext);
aStatus = ReflowChild(mFirstChild, &aPresContext, desiredSize, kidReflowState);
// Place and size the child
nsRect rect(0, 0, desiredSize.width, desiredSize.height);
mFirstChild->SetRect(rect);
mFirstChild->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
mLastContentOffset = ((RootContentFrame*)mFirstChild)->GetLastContentOffset();
}
// Return the max size as our desired size
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.height = aReflowState.maxSize.height;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
#ifdef NS_DEBUG
PostReflowCheck(aStatus);
#endif
NS_FRAME_TRACE_REFLOW_OUT("RootFrame::Reflow", aStatus);
return NS_OK;
}
NS_IMETHODIMP
RootFrame::HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus)
{
mContent->HandleDOMEvent(aPresContext, (nsEvent*)aEvent, nsnull, DOM_EVENT_INIT, aEventStatus);
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP ||
aEvent->message == NS_MOUSE_MIDDLE_BUTTON_UP ||
aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP) {
nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
#if 0
if (aEventStatus != nsEventStatus_eConsumeNoDefault) {
switch (aEvent->message) {
case NS_MOUSE_MOVE:
case NS_MOUSE_ENTER:
{
nsIFrame* target = this;
PRInt32 cursor;
GetCursorAndContentAt(aPresContext, aEvent->point, &target, &mContent, cursor);
if (cursor == NS_STYLE_CURSOR_INHERIT) {
cursor = NS_STYLE_CURSOR_DEFAULT;
}
nsCursor c;
switch (cursor) {
default:
case NS_STYLE_CURSOR_DEFAULT:
c = eCursor_standard;
break;
case NS_STYLE_CURSOR_HAND:
c = eCursor_hyperlink;
break;
case NS_STYLE_CURSOR_IBEAM:
c = eCursor_select;
break;
}
nsIWidget* window;
target->GetWindow(window);
window->SetCursor(c);
NS_RELEASE(window);
}
break;
}
}
#endif
return NS_OK;
}
//----------------------------------------------------------------------
RootContentFrame::RootContentFrame(nsIContent* aContent, nsIFrame* aParent)
: nsContainerFrame(aContent, aParent)
{
// Create a view
nsIFrame* parent;
nsIView* view;
GetParentWithView(parent);
NS_ASSERTION(parent, "GetParentWithView failed");
nsIView* parView;
parent->GetView(parView);
NS_ASSERTION(parView, "no parent with view");
// Create a view
static NS_DEFINE_IID(kViewCID, NS_VIEW_CID);
static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
nsresult result = nsRepository::CreateInstance(kViewCID,
nsnull,
kIViewIID,
(void **)&view);
if (NS_OK == result) {
nsIView* rootView = parView;
nsIViewManager* viewManager;
rootView->GetViewManager(viewManager);
// Initialize the view
NS_ASSERTION(nsnull != viewManager, "null view manager");
view->Init(viewManager, mRect, rootView);
viewManager->InsertChild(rootView, view, 0);
NS_RELEASE(viewManager);
// Remember our view
SetView(view);
}
}
void
RootContentFrame::CreateFirstChild(nsIPresContext* aPresContext)
{
// Are we paginated?
if (aPresContext->IsPaginated()) {
// Yes. Create the first page frame
mFirstChild = new nsPageFrame(mContent, this);
mChildCount = 1;
mLastContentOffset = mFirstContentOffset;
} else {
// Create a frame for the body/frameset child
PRInt32 i, n;
mContent->ChildCount(n);
for (i = 0; i < n; i++) {
nsIContent* child;
mContent->ChildAt(i, child);
if (nsnull != child) {
nsIAtom* tag;
child->GetTag(tag);
if ((nsHTMLAtoms::body == tag) || (nsHTMLAtoms::frameset == tag)) {
// Create a frame
nsIContentDelegate* cd = child->GetDelegate(aPresContext);
if (nsnull != cd) {
nsIStyleContext* kidStyleContext =
aPresContext->ResolveStyleContextFor(child, this);
nsresult rv = cd->CreateFrame(aPresContext, child, this,
kidStyleContext, mFirstChild);
NS_RELEASE(kidStyleContext);
if (NS_OK == rv) {
mChildCount = 1;
mFirstContentOffset = i;
mLastContentOffset = i;
}
NS_RELEASE(cd);
}
}
NS_IF_RELEASE(tag);
NS_RELEASE(child);
}
}
}
}
// XXX Hack
#define PAGE_SPACING_TWIPS 100
NS_IMETHODIMP
RootContentFrame::Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
NS_FRAME_TRACE_REFLOW_IN("RootContentFrame::Reflow");
#ifdef NS_DEBUG
PreReflowCheck();
#endif
aStatus = NS_FRAME_COMPLETE;
// XXX Incremental reflow code doesn't handle page mode at all...
if (eReflowReason_Incremental == aReflowState.reason) {
// We don't expect the target of the reflow command to be the root
// content frame
#ifdef NS_DEBUG
nsIFrame* target;
aReflowState.reflowCommand->GetTarget(target);
NS_ASSERTION(target != this, "root content frame is reflow command target");
#endif
// Verify the next frame in the reflow chain is our child frame
nsIFrame* next;
aReflowState.reflowCommand->GetNext(next);
NS_ASSERTION(next == mFirstChild, "unexpected next reflow command frame");
nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE);
nsReflowState kidReflowState(next, aReflowState, maxSize);
// Dispatch the reflow command to our child frame. Allow it to be as high
// as it wants
mFirstChild->WillReflow(aPresContext);
aStatus = ReflowChild(mFirstChild, &aPresContext, aDesiredSize, kidReflowState);
// Place and size the child. Make sure the child is at least as
// tall as our max size (the containing window)
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
} else {
nsReflowReason reflowReason = aReflowState.reason;
// Do we have any children?
if (nsnull == mFirstChild) {
// No, create the first child frame
reflowReason = eReflowReason_Initial;
CreateFirstChild(&aPresContext);
}
// Resize our frames
if (nsnull != mFirstChild) {
if (aPresContext.IsPaginated()) {
nscoord y = PAGE_SPACING_TWIPS;
nsReflowMetrics kidSize(aDesiredSize.maxElementSize);
// Compute the size of each page and the x coordinate within
// ourselves that the pages will be placed at.
nsSize pageSize(aPresContext.GetPageWidth(),
aPresContext.GetPageHeight());
nsIDeviceContext *dx = aPresContext.GetDeviceContext();
float sbWidth, sbHeight;
dx->GetScrollBarDimensions(sbWidth, sbHeight);
PRInt32 extra = aReflowState.maxSize.width - PAGE_SPACING_TWIPS*2 -
pageSize.width - NSToCoordRound(sbWidth);
NS_RELEASE(dx);
// Note: nscoord is an unsigned type so don't combine these
// two statements or the extra will be promoted to unsigned
// and the >0 won't work!
nscoord x = PAGE_SPACING_TWIPS;
if (extra > 0) {
x += extra / 2;
}
// Tile the pages vertically
for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) {
// Reflow the page
nsReflowState kidReflowState(kidFrame, aReflowState, pageSize,
reflowReason);
nsReflowStatus status;
// Place and size the page. If the page is narrower than our
// max width then center it horizontally
kidFrame->WillReflow(aPresContext);
kidFrame->MoveTo(x, y);
status = ReflowChild(kidFrame, &aPresContext, kidSize,
kidReflowState);
kidFrame->SetRect(nsRect(x, y, kidSize.width, kidSize.height));
y += kidSize.height;
// Leave a slight gap between the pages
y += PAGE_SPACING_TWIPS;
// Is the page complete?
nsIFrame* kidNextInFlow;
kidFrame->GetNextInFlow(kidNextInFlow);
if (NS_FRAME_IS_COMPLETE(status)) {
NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
} else if (nsnull == kidNextInFlow) {
// The page isn't complete and it doesn't have a next-in-flow so
// create a continuing page
nsIStyleContext* kidSC;
kidFrame->GetStyleContext(&aPresContext, kidSC);
nsIFrame* continuingPage;
nsresult rv = kidFrame->CreateContinuingFrame(aPresContext, this,
kidSC, continuingPage);
NS_RELEASE(kidSC);
reflowReason = eReflowReason_Initial;
// Add it to our child list
#ifdef NS_DEBUG
nsIFrame* kidNextSibling;
kidFrame->GetNextSibling(kidNextSibling);
NS_ASSERTION(nsnull == kidNextSibling, "unexpected sibling");
#endif
kidFrame->SetNextSibling(continuingPage);
mChildCount++;
}
// Get the next page
kidFrame->GetNextSibling(kidFrame);
}
// Return our desired size
aDesiredSize.height = y;
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
aDesiredSize.width = PAGE_SPACING_TWIPS*2 + pageSize.width;
if (aDesiredSize.width < aReflowState.maxSize.width) {
aDesiredSize.width = aReflowState.maxSize.width;
}
} else {
// Allow the frame to be as wide as our max width, and as high
// as it wants to be.
nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE);
nsReflowState kidReflowState(mFirstChild, aReflowState, maxSize, reflowReason);
// Get the child's desired size. Our child's desired height is our
// desired size
mFirstChild->WillReflow(aPresContext);
aStatus = ReflowChild(mFirstChild, &aPresContext, aDesiredSize, kidReflowState);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
// Place and size the child. Make sure the child is at least as
// tall as our max size (the containing window)
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
// Place and size the child
nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
// Do the necessary repainting
if (eReflowReason_Initial == reflowReason) {
// Repaint the visible area
Invalidate(nsRect(0, 0, aReflowState.maxSize.width, aReflowState.maxSize.height));
} else if (eReflowReason_Resize == aReflowState.reason) {
// Repaint the entire frame
Invalidate(nsRect(0, 0, aReflowState.maxSize.width, aDesiredSize.height));
}
}
}
else {
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.height = aReflowState.maxSize.height;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
if (nsnull != aDesiredSize.maxElementSize) {
aDesiredSize.maxElementSize->width = 0;
aDesiredSize.maxElementSize->height = 0;
}
}
}
// We are always a pseudo-frame; make sure our content offset is
// properly pushed upwards
nsContainerFrame* parent = (nsContainerFrame*) mGeometricParent;
parent->PropagateContentOffsets(this, mFirstContentOffset,
mLastContentOffset, mLastContentIsComplete);
#ifdef NS_DEBUG
PostReflowCheck(aStatus);
#endif
NS_FRAME_TRACE_REFLOW_OUT("RootContentFrame::Reflow", aStatus);
return NS_OK;
}
NS_IMETHODIMP
RootContentFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
// If we're paginated then fill the dirty rect with white
if (aPresContext.IsPaginated()) {
// Cross hatching would be nicer...
aRenderingContext.SetColor(NS_RGB(255,255,255));
aRenderingContext.FillRect(aDirtyRect);
}
nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
return NS_OK;
}
NS_IMETHODIMP
RootContentFrame::HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus)
{
#if 0
mContent->HandleDOMEvent(aPresContext, (nsEvent*)aEvent, nsnull, DOM_EVENT_INIT, aEventStatus);
#else
nsContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
#endif
if (aEventStatus != nsEventStatus_eConsumeNoDefault) {
switch (aEvent->message) {
case NS_MOUSE_MOVE:
case NS_MOUSE_ENTER:
{
nsIFrame* target = this;
nsIContent* mTargetContent = mContent;
PRInt32 cursor;
GetCursorAndContentAt(aPresContext, aEvent->point, &target, &mTargetContent, cursor);
if (cursor == NS_STYLE_CURSOR_INHERIT) {
cursor = NS_STYLE_CURSOR_DEFAULT;
}
nsCursor c;
switch (cursor) {
default:
case NS_STYLE_CURSOR_DEFAULT:
c = eCursor_standard;
break;
case NS_STYLE_CURSOR_HAND:
c = eCursor_hyperlink;
break;
case NS_STYLE_CURSOR_IBEAM:
c = eCursor_select;
break;
}
nsIWidget* window;
target->GetWindow(window);
window->SetCursor(c);
NS_RELEASE(window);
//If the content object under the cursor has changed, fire a mouseover/out
nsIEventStateManager *mStateManager;
nsIContent *mLastContent;
if (NS_OK == aPresContext.GetEventStateManager(&mStateManager)) {
mStateManager->GetLastMouseOverContent(&mLastContent);
if (mLastContent != mTargetContent) {
if (nsnull != mLastContent) {
//fire mouseout
nsEventStatus mStatus = nsEventStatus_eIgnore;
nsMouseEvent mEvent;
mEvent.eventStructType = NS_MOUSE_EVENT;
mEvent.message = NS_MOUSE_EXIT;
mLastContent->HandleDOMEvent(aPresContext, &mEvent, nsnull, DOM_EVENT_INIT, mStatus);
}
//fire mouseover
nsEventStatus mStatus = nsEventStatus_eIgnore;
nsMouseEvent mEvent;
mEvent.eventStructType = NS_MOUSE_EVENT;
mEvent.message = NS_MOUSE_ENTER;
mTargetContent->HandleDOMEvent(aPresContext, &mEvent, nsnull, DOM_EVENT_INIT, mStatus);
mStateManager->SetLastMouseOverContent(mTargetContent);
}
NS_RELEASE(mStateManager);
NS_IF_RELEASE(mLastContent);
}
}
break;
case NS_MOUSE_EXIT:
//Don't know if this is actually hooked up.
{
//Fire of mouseout to the last content object.
nsIEventStateManager *mStateManager;
nsIContent *mLastContent;
if (NS_OK == aPresContext.GetEventStateManager(&mStateManager)) {
mStateManager->GetLastMouseOverContent(&mLastContent);
if (nsnull != mLastContent) {
//fire mouseout
nsEventStatus mStatus = nsEventStatus_eIgnore;
nsMouseEvent mEvent;
mEvent.eventStructType = NS_MOUSE_EVENT;
mEvent.message = NS_MOUSE_EXIT;
mLastContent->HandleDOMEvent(aPresContext, &mEvent, nsnull, DOM_EVENT_INIT, mStatus);
mStateManager->SetLastMouseOverContent(nsnull);
NS_RELEASE(mLastContent);
}
NS_RELEASE(mStateManager);
}
}
break;
case NS_MOUSE_LEFT_BUTTON_UP:
{
nsIEventStateManager *mStateManager;
if (NS_OK == aPresContext.GetEventStateManager(&mStateManager)) {
mStateManager->SetActiveLink(nsnull);
NS_RELEASE(mStateManager);
}
}
break;
}
}
return NS_OK;
}

View File

@ -0,0 +1,928 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsHTMLImage.h"
#include "nsHTMLTagContent.h"
#include "nsString.h"
#include "nsLeafFrame.h"
#include "nsIPresContext.h"
#include "nsIRenderingContext.h"
#include "nsIFrameImageLoader.h"
#include "nsIPresShell.h"
#include "nsHTMLIIDs.h"
#include "nsIImage.h"
#include "nsIWidget.h"
#include "nsHTMLAtoms.h"
#include "nsIHTMLAttributes.h"
#include "nsIDocument.h"
#include "nsIHTMLDocument.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIImageMap.h"
#include "nsILinkHandler.h"
#include "nsIURL.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsCSSLayout.h"
#include "nsHTMLBase.h"
#include "prprf.h"
#include "nsISizeOfHandler.h"
#include "nsIFontMetrics.h"
#include "nsCSSRendering.h"
#include "nsIDOMHTMLImageElement.h"
#define BROKEN_IMAGE_URL "resource:/res/html/broken-image.gif"
#define XP_IS_SPACE(_ch) \
(((_ch) == ' ') || ((_ch) == '\t') || ((_ch) == '\n'))
// XXX image frame layout can be 100% decoupled from the content
// object; all it needs are attributes to work properly
static NS_DEFINE_IID(kIHTMLDocumentIID, NS_IHTMLDOCUMENT_IID);
#if 0
#define nsHTMLImageSuper nsHTMLTagContent
class nsHTMLImage : public nsHTMLImageSuper, public nsIDOMHTMLImageElement {
public:
nsHTMLImage(nsIAtom* aTag);
NS_DECL_ISUPPORTS
NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler) const;
NS_IMETHOD CreateFrame(nsIPresContext* aPresContext,
nsIFrame* aParentFrame,
nsIStyleContext* aStyleContext,
nsIFrame*& aResult);
NS_IMETHOD SetAttribute(nsIAtom* aAttribute, const nsString& aValue,
PRBool aNotify);
NS_IMETHOD MapAttributesInto(nsIStyleContext* aContext,
nsIPresContext* aPresContext);
NS_IMETHOD AttributeToString(nsIAtom* aAttribute,
nsHTMLValue& aValue,
nsString& aResult) const;
NS_FORWARD_IDOMNODE(nsHTMLImageSuper::)
NS_FORWARD_IDOMELEMENT(nsHTMLImageSuper::)
NS_FORWARD_IDOMHTMLELEMENT(nsHTMLImageSuper::)
NS_DECL_IDOMHTMLIMAGEELEMENT
NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
protected:
virtual ~nsHTMLImage();
void SizeOfWithoutThis(nsISizeOfHandler* aHandler) const;
void TriggerReflow();
};
#endif
#define ImageFrameSuper nsLeafFrame
class ImageFrame : public ImageFrameSuper {
public:
ImageFrame(nsIContent* aContent, nsIFrame* aParentFrame);
NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);
NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler) const;
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
NS_METHOD HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus);
NS_IMETHOD GetCursorAndContentAt(nsIPresContext& aPresContext,
const nsPoint& aPoint,
nsIFrame** aFrame,
nsIContent** aContent,
PRInt32& aCursor);
NS_IMETHOD ContentChanged(nsIPresShell* aShell,
nsIPresContext* aPresContext,
nsIContent* aChild,
nsISupports* aSubContent);
protected:
virtual ~ImageFrame();
void SizeOfWithoutThis(nsISizeOfHandler* aHandler) const;
virtual void GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize);
nsIImageMap* GetImageMap();
nsHTMLImageLoader mImageLoader;
nsIImageMap* mImageMap;
PRBool mSizeFrozen;
void TriggerLink(nsIPresContext& aPresContext,
const nsString& aURLSpec,
const nsString& aTargetSpec,
PRBool aClick);
PRBool IsServerImageMap();
PRIntn GetSuppress();
nscoord MeasureString(nsIFontMetrics* aFontMetrics,
const PRUnichar* aString,
PRInt32 aLength,
nscoord aMaxWidth,
PRUint32& aMaxFit);
void DisplayAltText(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsString& aAltText,
const nsRect& aRect);
};
// Value's for mSuppress
#define SUPPRESS_UNSET 0
#define DONT_SUPPRESS 1
#define SUPPRESS 2
#define DEFAULT_SUPPRESS 3
// Default alignment value (so we can tell an unset value from a set value)
#define ALIGN_UNSET PRUint8(-1)
//----------------------------------------------------------------------
nsHTMLImageLoader::nsHTMLImageLoader()
{
mImageLoader = nsnull;
mLoadImageFailed = PR_FALSE;
mLoadBrokenImageFailed = PR_FALSE;
mURLSpec = nsnull;
mBaseHREF = nsnull;
}
nsHTMLImageLoader::~nsHTMLImageLoader()
{
NS_IF_RELEASE(mImageLoader);
if (nsnull != mURLSpec) {
delete mURLSpec;
}
if (nsnull != mBaseHREF) {
delete mBaseHREF;
}
}
void
nsHTMLImageLoader::SizeOf(nsISizeOfHandler* aHandler) const
{
aHandler->Add(sizeof(*this));
if (!aHandler->HaveSeen(mURLSpec)) {
mURLSpec->SizeOf(aHandler);
}
if (!aHandler->HaveSeen(mImageLoader)) {
mImageLoader->SizeOf(aHandler);
}
}
nsIImage*
nsHTMLImageLoader::GetImage()
{
nsIImage* image = nsnull;
if (nsnull != mImageLoader) {
mImageLoader->GetImage(image);
}
return image;
}
nsresult
nsHTMLImageLoader::SetURL(const nsString& aURLSpec)
{
if (nsnull != mURLSpec) {
delete mURLSpec;
}
mURLSpec = new nsString(aURLSpec);
if (nsnull == mURLSpec) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsHTMLImageLoader::SetBaseHREF(const nsString& aBaseHREF)
{
if (nsnull != mBaseHREF) {
delete mBaseHREF;
}
mBaseHREF = new nsString(aBaseHREF);
if (nsnull == mBaseHREF) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsHTMLImageLoader::StartLoadImage(nsIPresContext* aPresContext,
nsIFrame* aForFrame,
PRBool aNeedSizeUpdate,
PRIntn& aLoadStatus)
{
aLoadStatus = NS_IMAGE_LOAD_STATUS_NONE;
// Get absolute url the first time through
nsresult rv;
nsAutoString src;
if (mLoadImageFailed || (nsnull == mURLSpec)) {
src.Append(BROKEN_IMAGE_URL);
} else {
nsAutoString baseURL;
if (nsnull != mBaseHREF) {
baseURL = *mBaseHREF;
}
// Get documentURL
nsIPresShell* shell;
shell = aPresContext->GetShell();
nsIDocument* doc = shell->GetDocument();
nsIURL* docURL = doc->GetDocumentURL();
// Create an absolute URL
nsresult rv = NS_MakeAbsoluteURL(docURL, baseURL, *mURLSpec, src);
// Release references
NS_RELEASE(shell);
NS_RELEASE(docURL);
NS_RELEASE(doc);
if (NS_OK != rv) {
return rv;
}
}
if (nsnull == mImageLoader) {
// Start image loading. Note that we don't specify a background color
// so transparent images are always rendered using a transparency mask
rv = aPresContext->StartLoadImage(src, nsnull, aForFrame, aNeedSizeUpdate,
mImageLoader);
if (NS_OK != rv) {
return rv;
}
}
// Examine current image load status
mImageLoader->GetImageLoadStatus(aLoadStatus);
if (0 != (aLoadStatus & NS_IMAGE_LOAD_STATUS_ERROR)) {
NS_RELEASE(mImageLoader);
if (mLoadImageFailed) {
// We are doomed. Loading the broken image has just failed.
mLoadBrokenImageFailed = PR_TRUE;
}
else {
// Try again, this time using the broke-image url
mLoadImageFailed = PR_TRUE;
return StartLoadImage(aPresContext, aForFrame, aNeedSizeUpdate, aLoadStatus);
}
}
return NS_OK;
}
void
nsHTMLImageLoader::GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize)
{
nsSize styleSize;
PRIntn ss = nsCSSLayout::GetStyleSize(aPresContext, aReflowState, styleSize);
PRIntn loadStatus;
if (0 != ss) {
if (NS_SIZE_HAS_BOTH == ss) {
StartLoadImage(aPresContext, aReflowState.frame, PR_FALSE, loadStatus);
aDesiredSize.width = styleSize.width;
aDesiredSize.height = styleSize.height;
}
else {
// Preserve aspect ratio of image with unbound dimension.
StartLoadImage(aPresContext, aReflowState.frame, PR_TRUE, loadStatus);
if ((0 == (loadStatus & NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE)) ||
(nsnull == mImageLoader)) {
// Provide a dummy size for now; later on when the image size
// shows up we will reflow to the new size.
aDesiredSize.width = 1;
aDesiredSize.height = 1;
}
else {
float p2t = aPresContext->GetPixelsToTwips();
nsSize imageSize;
mImageLoader->GetSize(imageSize);
float imageWidth = imageSize.width * p2t;
float imageHeight = imageSize.height * p2t;
if (0.0f != imageHeight) {
if (0 != (ss & NS_SIZE_HAS_WIDTH)) {
// We have a width, and an auto height. Compute height
// from width.
aDesiredSize.width = styleSize.width;
aDesiredSize.height =
(nscoord)NSToIntRound(styleSize.width * imageHeight / imageWidth);
}
else {
// We have a height and an auto width. Compute width from
// height.
aDesiredSize.height = styleSize.height;
aDesiredSize.width =
(nscoord)NSToIntRound(styleSize.height * imageWidth / imageHeight);
}
}
else {
// Screwy image
aDesiredSize.width = 1;
aDesiredSize.height = 1;
}
}
}
}
else {
StartLoadImage(aPresContext, aReflowState.frame, PR_TRUE, loadStatus);
if ((0 == (loadStatus & NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE)) ||
(nsnull == mImageLoader)) {
// Provide a dummy size for now; later on when the image size
// shows up we will reflow to the new size.
aDesiredSize.width = 1;
aDesiredSize.height = 1;
printf ("in image loader, dummy size of 1 returned\n");
} else {
float p2t = aPresContext->GetPixelsToTwips();
nsSize imageSize;
mImageLoader->GetSize(imageSize);
aDesiredSize.width = NSIntPixelsToTwips(imageSize.width, p2t);
aDesiredSize.height = NSIntPixelsToTwips(imageSize.height, p2t);
printf ("in image loader, real size of %d returned\n", aDesiredSize.width);
}
}
}
//----------------------------------------------------------------------
nsresult
NS_NewImageFrame(nsIContent* aContent,
nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
ImageFrame* frame = new ImageFrame(aContent, aParentFrame);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
ImageFrame::ImageFrame(nsIContent* aContent, nsIFrame* aParentFrame)
: nsLeafFrame(aContent, aParentFrame)
{
}
ImageFrame::~ImageFrame()
{
}
NS_METHOD
ImageFrame::DeleteFrame(nsIPresContext& aPresContext)
{
NS_IF_RELEASE(mImageMap);
// Release image loader first so that it's refcnt can go to zero
mImageLoader.DestroyLoader();
return nsLeafFrame::DeleteFrame(aPresContext);
}
NS_IMETHODIMP
ImageFrame::SizeOf(nsISizeOfHandler* aHandler) const
{
aHandler->Add(sizeof(*this));
ImageFrame::SizeOfWithoutThis(aHandler);
return NS_OK;
}
void
ImageFrame::SizeOfWithoutThis(nsISizeOfHandler* aHandler) const
{
ImageFrameSuper::SizeOfWithoutThis(aHandler);
mImageLoader.SizeOf(aHandler);
if (!aHandler->HaveSeen(mImageMap)) {
mImageMap->SizeOf(aHandler);
}
}
void
ImageFrame::GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize)
{
if (mSizeFrozen) {
aDesiredSize.width = mRect.width;
aDesiredSize.height = mRect.height;
}
else {
// XXX Don't create a view, because we want whatever is below the image
// to show through while the image is loading; Likewise for transparent
// images and broken images
//
// What we really want to do is to create a view, and indicate that the
// view has a transparent content area. Do this while it's loading,
// and then when it's fully loaded mark the view as opaque if the
// image is opaque.
//
// We can't use that approach yet, because currently the compositor doesn't
// support transparent views...
#if 0
nsHTMLBase::CreateViewForFrame(aPresContext, this, mStyleContext, PR_TRUE);
#endif
// Setup url before starting the image load
nsAutoString src, base;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("SRC", src)) {
mImageLoader.SetURL(src);
if (NS_CONTENT_ATTR_HAS_VALUE ==
mContent->GetAttribute(NS_HTML_BASE_HREF, base)) {
mImageLoader.SetBaseHREF(base);
}
}
mImageLoader.GetDesiredSize(aPresContext, aReflowState, aDesiredSize);
}
}
// Computes the width of the specified string. aMaxWidth specifies the maximum
// width available. Once this limit is reached no more characters are measured.
// The number of characters that fit within the maximum width are returned in
// aMaxFit
nscoord
ImageFrame::MeasureString(nsIFontMetrics* aFontMetrics,
const PRUnichar* aString,
PRInt32 aLength,
nscoord aMaxWidth,
PRUint32& aMaxFit)
{
nscoord totalWidth = 0;
nscoord spaceWidth;
aFontMetrics->GetWidth(' ', spaceWidth);
aMaxFit = 0;
while (aLength > 0) {
// Find the next place we can line break
PRUint32 len = aLength;
PRBool trailingSpace = PR_FALSE;
for (PRInt32 i = 0; i < aLength; i++) {
if (XP_IS_SPACE(aString[i]) && (i > 0)) {
len = i; // don't include the space when measuring
trailingSpace = PR_TRUE;
break;
}
}
// Measure this chunk of text, and see if it fits
nscoord width;
aFontMetrics->GetWidth(aString, len, width);
PRBool fits = (totalWidth + width) <= aMaxWidth;
// If it fits on the line, or it's the first word we've processed then
// include it
if (fits || (0 == totalWidth)) {
// New piece fits
totalWidth += width;
// If there's a trailing space then see if it fits as well
if (trailingSpace) {
if ((totalWidth + spaceWidth) <= aMaxWidth) {
totalWidth += spaceWidth;
} else {
// Space won't fit. Leave it at the end but don't include it in
// the width
fits = PR_FALSE;
}
len++;
}
aMaxFit += len;
aString += len;
aLength -= len;
}
if (!fits) {
break;
}
}
return totalWidth;
}
// Formats the alt-text to fit within the specified rectangle. Breaks lines
// between words if a word would extend past the edge of the rectangle
void
ImageFrame::DisplayAltText(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsString& aAltText,
const nsRect& aRect)
{
// Clip so we don't render outside of the rect.
aRenderingContext.PushState();
aRenderingContext.SetClipRect(aRect, nsClipCombine_kIntersect);
const nsStyleColor* color =
(const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
const nsStyleFont* font =
(const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
// Set font and color
aRenderingContext.SetColor(color->mColor);
aRenderingContext.SetFont(font->mFont);
// Format the text to display within the formatting rect
nsIFontMetrics* fm = aRenderingContext.GetFontMetrics();
nscoord maxDescent, height;
fm->GetMaxDescent(maxDescent);
fm->GetHeight(height);
// XXX It would be nice if there was a way to have the font metrics tell
// use where to break the text given a maximum width. At a minimum we need
// to be able to get the break character...
const PRUnichar* str = aAltText.GetUnicode();
PRInt32 strLen = aAltText.Length();
nscoord y = aRect.y;
while ((strLen > 0) && ((y + maxDescent) < aRect.YMost())) {
// Determine how much of the text to display on this line
PRUint32 maxFit; // number of characters that fit
nscoord width = MeasureString(fm, str, strLen, aRect.width, maxFit);
// Display the text
aRenderingContext.DrawString(str, maxFit, aRect.x, y, 0);
// Move to the next line
str += maxFit;
strLen -= maxFit;
y += height;
}
NS_RELEASE(fm);
aRenderingContext.PopState();
}
struct nsRecessedBorder : public nsStyleSpacing {
nsRecessedBorder(nscoord aBorderWidth)
: nsStyleSpacing()
{
nsStyleCoord styleCoord(aBorderWidth);
mBorder.SetLeft(styleCoord);
mBorder.SetTop(styleCoord);
mBorder.SetRight(styleCoord);
mBorder.SetBottom(styleCoord);
mBorderStyle[0] = NS_STYLE_BORDER_STYLE_INSET;
mBorderStyle[1] = NS_STYLE_BORDER_STYLE_INSET;
mBorderStyle[2] = NS_STYLE_BORDER_STYLE_INSET;
mBorderStyle[3] = NS_STYLE_BORDER_STYLE_INSET;
mBorderColor[0] = 0;
mBorderColor[1] = 0;
mBorderColor[2] = 0;
mBorderColor[3] = 0;
mHasCachedMargin = mHasCachedPadding = mHasCachedBorder = PR_FALSE;
}
};
NS_METHOD
ImageFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
if ((0 == mRect.width) || (0 == mRect.height)) {
// Do not render when given a zero area. This avoids some useless
// scaling work while we wait for our image dimensions to arrive
// asynchronously.
return NS_OK;
}
const nsStyleDisplay* disp =
(const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
if (disp->mVisible) {
// First paint background and borders
nsLeafFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
nsIImage* image = mImageLoader.GetImage();
if (nsnull == image) {
// No image yet. Draw the icon that indicates we're loading, and display
// the alt-text
nsAutoString altText;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("ALT", altText)) {
// Display a recessed one-pixel border in the inner area
nsRect inner;
GetInnerArea(&aPresContext, inner);
float p2t = aPresContext.GetPixelsToTwips();
nsRecessedBorder recessedBorder(NSIntPixelsToTwips(1, p2t));
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, inner,
inner, recessedBorder, 0);
inner.Deflate(NSIntPixelsToTwips(1, p2t), NSIntPixelsToTwips(1, p2t));
// Leave a 8 pixel left/right padding, and a 5 pixel top/bottom padding
inner.Deflate(NSIntPixelsToTwips(8, p2t), NSIntPixelsToTwips(5, p2t));
// If there's room, then display the alt-text
if (!inner.IsEmpty()) {
DisplayAltText(aPresContext, aRenderingContext, altText, inner);
}
}
return NS_OK;
}
// Now render the image into our inner area (the area without the
// borders and padding)
nsRect inner;
GetInnerArea(&aPresContext, inner);
if (mImageLoader.GetLoadImageFailed()) {
float p2t = aPresContext.GetPixelsToTwips();
inner.width = NSIntPixelsToTwips(image->GetWidth(), p2t);
inner.height = NSIntPixelsToTwips(image->GetHeight(), p2t);
}
aRenderingContext.DrawImage(image, inner);
if (GetShowFrameBorders()) {
nsIImageMap* map = GetImageMap();
if (nsnull != map) {
aRenderingContext.SetColor(NS_RGB(0, 0, 0));
aRenderingContext.PushState();
aRenderingContext.Translate(inner.x, inner.y);
map->Draw(aPresContext, aRenderingContext);
aRenderingContext.PopState();
}
}
}
return NS_OK;
}
nsIImageMap*
ImageFrame::GetImageMap()
{
if (nsnull == mImageMap) {
nsAutoString usemap;
mContent->GetAttribute("usemap", usemap);
if (0 == usemap.Length()) {
return nsnull;
}
nsIDocument* doc = nsnull;
mContent->GetDocument(doc);
if (nsnull == doc) {
return nsnull;
}
if (usemap.First() == '#') {
usemap.Cut(0, 1);
}
nsIHTMLDocument* hdoc;
nsresult rv = doc->QueryInterface(kIHTMLDocumentIID, (void**)&hdoc);
NS_RELEASE(doc);
if (NS_OK == rv) {
nsIImageMap* map;
rv = hdoc->GetImageMap(usemap, &map);
NS_RELEASE(hdoc);
if (NS_OK == rv) {
mImageMap = map;
}
}
}
NS_IF_ADDREF(mImageMap);
return mImageMap;
}
void
ImageFrame::TriggerLink(nsIPresContext& aPresContext,
const nsString& aURLSpec,
const nsString& aTargetSpec,
PRBool aClick)
{
nsILinkHandler* handler = nsnull;
aPresContext.GetLinkHandler(&handler);
if (nsnull != handler) {
if (aClick) {
handler->OnLinkClick(this, aURLSpec, aTargetSpec);
}
else {
handler->OnOverLink(this, aURLSpec, aTargetSpec);
}
}
}
PRBool
ImageFrame::IsServerImageMap()
{
nsAutoString ismap;
return NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("ismap", ismap);
}
PRIntn
ImageFrame::GetSuppress()
{
nsAutoString s;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("suppress", s)) {
if (s.EqualsIgnoreCase("true")) {
return SUPPRESS;
} else if (s.EqualsIgnoreCase("false")) {
return DONT_SUPPRESS;
}
}
return DEFAULT_SUPPRESS;
}
// XXX what should clicks on transparent pixels do?
NS_METHOD
ImageFrame::HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus)
{
nsIImageMap* map;
aEventStatus = nsEventStatus_eIgnore;
switch (aEvent->message) {
case NS_MOUSE_LEFT_BUTTON_UP:
case NS_MOUSE_MOVE:
map = GetImageMap();
if ((nsnull != map) || IsServerImageMap()) {
nsIURL* docURL = nsnull;
nsIDocument* doc = nsnull;
mContent->GetDocument(doc);
if (nsnull != doc) {
docURL = doc->GetDocumentURL();
NS_RELEASE(doc);
}
// Ask map if the x,y coordinates are in a clickable area
float t2p = aPresContext.GetTwipsToPixels();
nsAutoString absURL, target, altText;
PRBool suppress;
if (nsnull != map) {
// Subtract out border and padding here so that we are looking
// at the right coordinates. Hit detection against area tags
// is done after the mouse wanders over the image, not over
// the image's borders.
nsRect inner;
GetInnerArea(&aPresContext, inner);
PRInt32 x = NSTwipsToIntPixels((aEvent->point.x - inner.x), t2p);
PRInt32 y = NSTwipsToIntPixels((aEvent->point.y - inner.y), t2p);
nsresult r = map->IsInside(x, y, docURL, absURL, target, altText,
&suppress);
NS_IF_RELEASE(docURL);
NS_RELEASE(map);
if (NS_OK == r) {
// We hit a clickable area. Time to go somewhere...
PRBool clicked = PR_FALSE;
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
aEventStatus = nsEventStatus_eConsumeNoDefault;
clicked = PR_TRUE;
}
TriggerLink(aPresContext, absURL, target, clicked);
}
}
else {
suppress = GetSuppress();
nsAutoString baseURL;
mContent->GetAttribute(NS_HTML_BASE_HREF, baseURL);
nsAutoString src;
mContent->GetAttribute("src", src);
NS_MakeAbsoluteURL(docURL, baseURL, src, absURL);
// Note: We don't subtract out the border/padding here to remain
// compatible with navigator. [ick]
PRInt32 x = NSTwipsToIntPixels(aEvent->point.x, t2p);
PRInt32 y = NSTwipsToIntPixels(aEvent->point.y, t2p);
char cbuf[50];
PR_snprintf(cbuf, sizeof(cbuf), "?%d,%d", x, y);
absURL.Append(cbuf);
PRBool clicked = PR_FALSE;
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
aEventStatus = nsEventStatus_eConsumeNoDefault;
clicked = PR_TRUE;
}
TriggerLink(aPresContext, absURL, target, clicked);
}
break;
}
// FALL THROUGH
default:
// Let default event handler deal with it
return nsLeafFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
return NS_OK;
}
NS_METHOD
ImageFrame::GetCursorAndContentAt(nsIPresContext& aPresContext,
const nsPoint& aPoint,
nsIFrame** aFrame,
nsIContent** aContent,
PRInt32& aCursor)
{
// The default cursor is to have no cursor
aCursor = NS_STYLE_CURSOR_INHERIT;
*aContent = mContent;
const nsStyleColor* styleColor = (const nsStyleColor*)
mStyleContext->GetStyleData(eStyleStruct_Color);
if (styleColor->mCursor != NS_STYLE_CURSOR_INHERIT) {
// If we have a particular cursor, use it
*aFrame = this;
aCursor = (PRInt32) styleColor->mCursor;
}
nsIImageMap* map = GetImageMap();
if (nsnull != map) {
nsRect inner;
GetInnerArea(&aPresContext, inner);
aCursor = NS_STYLE_CURSOR_DEFAULT;
float t2p = aPresContext.GetTwipsToPixels();
PRInt32 x = NSTwipsToIntPixels((aPoint.x - inner.x), t2p);
PRInt32 y = NSTwipsToIntPixels((aPoint.y - inner.y), t2p);
if (NS_OK == map->IsInside(x, y)) {
aCursor = NS_STYLE_CURSOR_HAND;
}
NS_RELEASE(map);
}
return NS_OK;
}
NS_IMETHODIMP
ImageFrame::ContentChanged(nsIPresShell* aShell,
nsIPresContext* aPresContext,
nsIContent* aChild,
nsISupports* aSubContent)
{
// See if the src attribute changed; if it did then trigger a redraw
// by firing up a new image load request. Otherwise let our base
// class handle the content-changed request.
nsAutoString oldSRC;
mImageLoader.GetURL(oldSRC);
// Get src attribute's value and construct a new absolute url from it
nsAutoString newSRC;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("SRC", newSRC)) {
if (!oldSRC.Equals(newSRC)) {
mSizeFrozen = PR_TRUE;
#ifdef NS_DEBUG
char oldcbuf[100], newcbuf[100];
oldSRC.ToCString(oldcbuf, sizeof(oldcbuf));
newSRC.ToCString(newcbuf, sizeof(newcbuf));
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("ImageFrame::ContentChanged: new image source; old='%s' new='%s'",
oldcbuf, newcbuf));
#endif
// Get rid of old image loader and start a new image load going
mImageLoader.DestroyLoader();
// Fire up a new image load request
PRIntn loadStatus;
mImageLoader.SetURL(newSRC);
mImageLoader.StartLoadImage(aPresContext, this, PR_FALSE, loadStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("ImageFrame::ContentChanged: loadImage status=%x",
loadStatus));
// If the image is already ready then we need to trigger a
// redraw because the image loader won't.
if (loadStatus & NS_IMAGE_LOAD_STATUS_IMAGE_READY) {
// XXX Stuff this into a method on nsIPresShell/Context
nsRect bounds;
nsPoint offset;
nsIView* view;
GetOffsetFromView(offset, view);
nsIViewManager* vm;
view->GetViewManager(vm);
bounds.x = offset.x;
bounds.y = offset.y;
bounds.width = mRect.width;
bounds.height = mRect.height;
vm->UpdateView(view, bounds, 0);
NS_RELEASE(vm);
}
return NS_OK;
}
}
return ImageFrameSuper::ContentChanged(aShell, aPresContext, aChild,
aSubContent);
}

View File

@ -0,0 +1,191 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsIHTMLContent.h"
#include "nsFrame.h"
#include "nsIInlineReflow.h"
#include "nsCSSLineLayout.h"
#include "nsHTMLIIDs.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsHTMLAtoms.h"
#include "nsUnitConversion.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
// Spacer type's
#define TYPE_WORD 0 // horizontal space
#define TYPE_LINE 1 // line-break + vertical space
#define TYPE_IMAGE 2 // acts like a sized image with nothing to see
class SpacerFrame : public nsFrame, private nsIInlineReflow {
public:
SpacerFrame(nsIContent* aContent, nsIFrame* aParentFrame);
// nsISupports
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
// nsIInlineReflow
NS_IMETHOD FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand);
NS_IMETHOD InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState);
PRUint8 GetType();
protected:
virtual ~SpacerFrame();
};
nsresult
NS_NewSpacerFrame(nsIContent* aContent,
nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
nsIFrame* frame = new SpacerFrame(aContent, aParentFrame);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
SpacerFrame::SpacerFrame(nsIContent* aContent, nsIFrame* aParentFrame)
: nsFrame(aContent, aParentFrame)
{
}
SpacerFrame::~SpacerFrame()
{
}
NS_IMETHODIMP
SpacerFrame::QueryInterface(REFNSIID aIID, void** aInstancePtrResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIInlineReflowIID)) {
*aInstancePtrResult = (void*) ((nsIInlineReflow*)this);
return NS_OK;
}
return nsFrame::QueryInterface(aIID, aInstancePtrResult);
}
NS_IMETHODIMP
SpacerFrame::InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aMetrics,
const nsReflowState& aReflowState)
{
nsresult rv = NS_FRAME_COMPLETE;
// By default, we have no area
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.ascent = 0;
aMetrics.descent = 0;
nscoord width = 0;
nscoord height = 0;
PRUint8 type = GetType();
nsresult ca;
nsIHTMLContent* hc = nsnull;
mContent->QueryInterface(kIHTMLContentIID, (void**) &hc);
if (nsnull != hc) {
if (type != TYPE_IMAGE) {
nsHTMLValue val;
ca = hc->GetAttribute(nsHTMLAtoms::size, val);
if (NS_CONTENT_ATTR_HAS_VALUE == ca) {
width = val.GetPixelValue();
}
} else {
nsHTMLValue val;
ca = hc->GetAttribute(nsHTMLAtoms::width, val);
if (NS_CONTENT_ATTR_HAS_VALUE == ca) {
if (eHTMLUnit_Pixel == val.GetUnit()) {
width = val.GetPixelValue();
}
}
ca = hc->GetAttribute(nsHTMLAtoms::height, val);
if (NS_CONTENT_ATTR_HAS_VALUE == ca) {
if (eHTMLUnit_Pixel == val.GetUnit()) {
height = val.GetPixelValue();
}
}
}
NS_RELEASE(hc);
}
float p2t = aLineLayout.mPresContext->GetPixelsToTwips();
switch (type) {
case TYPE_WORD:
if (0 != width) {
aMetrics.width = NSIntPixelsToTwips(width, p2t);
}
break;
case TYPE_LINE:
if (0 != width) {
rv = NS_INLINE_LINE_BREAK_AFTER(0);
aMetrics.height = NSIntPixelsToTwips(width, p2t);
aMetrics.ascent = aMetrics.height;
}
break;
case TYPE_IMAGE:
aMetrics.width = NSIntPixelsToTwips(width, p2t);
aMetrics.height = NSIntPixelsToTwips(height, p2t);
aMetrics.ascent = aMetrics.height;
break;
}
if (nsnull != aMetrics.maxElementSize) {
aMetrics.maxElementSize->width = aMetrics.width;
aMetrics.maxElementSize->height = aMetrics.height;
}
return rv;
}
NS_IMETHODIMP
SpacerFrame::FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand)
{
aLineLayout.EndTextRun();
return NS_OK;
}
PRUint8
SpacerFrame::GetType()
{
PRUint8 type = TYPE_WORD;
nsAutoString value;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("type", value)) {
if (value.EqualsIgnoreCase("line") ||
value.EqualsIgnoreCase("vert") ||
value.EqualsIgnoreCase("vertical")) {
return TYPE_LINE;
}
if (value.EqualsIgnoreCase("block")) {
return TYPE_IMAGE;
}
}
return type;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,141 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsFrame.h"
#include "nsHTMLParts.h"
#include "nsHTMLIIDs.h"
#include "nsIPresContext.h"
#include "nsIInlineReflow.h"
#include "nsCSSLineLayout.h"
#include "nsStyleConsts.h"
#include "nsHTMLAtoms.h"
#include "nsIStyleContext.h"
#include "nsIFontMetrics.h"
#include "nsIRenderingContext.h"
class BRFrame : public nsFrame, public nsIInlineReflow {
public:
BRFrame(nsIContent* aContent, nsIFrame* aParentFrame);
// nsISupports
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
// nsIFrame
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
// nsIInlineReflow
NS_IMETHOD FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand);
NS_IMETHOD InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState);
protected:
virtual ~BRFrame();
};
nsresult
NS_NewBRFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
nsIFrame* frame = new BRFrame(aContent, aParentFrame);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
BRFrame::BRFrame(nsIContent* aContent,
nsIFrame* aParentFrame)
: nsFrame(aContent, aParentFrame)
{
}
BRFrame::~BRFrame()
{
}
NS_IMETHODIMP
BRFrame::QueryInterface(REFNSIID aIID, void** aInstancePtrResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIInlineReflowIID)) {
*aInstancePtrResult = (void*) ((nsIInlineReflow*)this);
return NS_OK;
}
return nsFrame::QueryInterface(aIID, aInstancePtrResult);
}
NS_METHOD
BRFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
if (nsIFrame::GetShowFrameBorders()) {
float p2t = aPresContext.GetPixelsToTwips();
aRenderingContext.SetColor(NS_RGB(0, 255, 255));
aRenderingContext.FillRect(0, 0, NSIntPixelsToTwips(5, p2t), mRect.height);
}
return NS_OK;
}
NS_IMETHODIMP
BRFrame::FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand)
{
aLineLayout.EndTextRun();
return NS_OK;
}
NS_IMETHODIMP
BRFrame::InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aMetrics,
const nsReflowState& aReflowState)
{
if (nsnull != aMetrics.maxElementSize) {
aMetrics.maxElementSize->width = 0;
aMetrics.maxElementSize->height = 0;
}
// We have no width, but we're the height of the default font
const nsStyleFont* font = (const nsStyleFont*)
mStyleContext->GetStyleData(eStyleStruct_Font);
nsIFontMetrics* fm = aLineLayout.mPresContext->GetMetricsFor(font->mFont);
fm->GetMaxAscent(aMetrics.ascent);
fm->GetMaxDescent(aMetrics.descent);
aMetrics.height = aMetrics.ascent + aMetrics.descent;
aMetrics.width = 0;
NS_RELEASE(fm);
// Return our inline reflow status
const nsStyleDisplay* display = (const nsStyleDisplay*)
mStyleContext->GetStyleData(eStyleStruct_Display);
PRUint32 breakType = display->mBreakType;
if (NS_STYLE_CLEAR_NONE == breakType) {
breakType = NS_STYLE_CLEAR_LINE;
}
return NS_INLINE_BREAK | NS_INLINE_BREAK_AFTER |
NS_INLINE_MAKE_BREAK_TYPE(breakType);
}

View File

@ -0,0 +1,342 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsIHTMLContent.h"
#include "nsLeafFrame.h"
#include "nsIRenderingContext.h"
#include "nsGlobalVariables.h"
#include "nsIStyleContext.h"
#include "nsColor.h"
#include "nsIPresContext.h"
#include "nsIStyleContext.h"
#include "nsHTMLIIDs.h"
#include "nsHTMLAtoms.h"
#include "nsIFontMetrics.h"
#include "nsIHTMLAttributes.h"
#include "nsStyleConsts.h"
#include "nsCSSRendering.h"
#include "nsCSSLayout.h"
#include "nsIDOMHTMLHRElement.h"
static NS_DEFINE_IID(kIDOMHTMLHRElementIID, NS_IDOMHTMLHRELEMENT_IID);
// default hr thickness in pixels
#define DEFAULT_THICKNESS 3
class HRuleFrame : public nsLeafFrame {
public:
HRuleFrame(nsIContent* aContent,
nsIFrame* aParentFrame);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
protected:
virtual ~HRuleFrame();
virtual void GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize);
PRBool GetNoShade();
PRInt32 GetThickness();
nscoord mComputedWidth;
};
nsresult
NS_NewHRFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
nsIFrame* frame = new HRuleFrame(aContent, aParentFrame);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
HRuleFrame::HRuleFrame(nsIContent* aContent,
nsIFrame* aParentFrame)
: nsLeafFrame(aContent, aParentFrame)
{
}
HRuleFrame::~HRuleFrame()
{
}
NS_METHOD
HRuleFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
const nsStyleDisplay* disp =
(const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
if (PR_FALSE == disp->mVisible) {
return NS_OK;
}
float p2t = aPresContext.GetPixelsToTwips();
nscoord thickness = NSIntPixelsToTwips(GetThickness(), p2t);
// Get style data
const nsStyleSpacing* spacing = (const nsStyleSpacing*)
mStyleContext->GetStyleData(eStyleStruct_Spacing);
const nsStyleColor* color = (const nsStyleColor*)
mStyleContext->GetStyleData(eStyleStruct_Color);
nsMargin borderPadding;
spacing->CalcBorderPaddingFor(this, borderPadding);
nscoord x0 = borderPadding.left;
nscoord y0 = borderPadding.top;
nscoord width = mRect.width -
(borderPadding.left + borderPadding.right);
nscoord height = mRect.height -
(borderPadding.top + borderPadding.bottom);
nscoord newWidth = mComputedWidth;
if (newWidth < width) {
// center or right align rule within the extra space
const nsStyleText* text =
(const nsStyleText*) mStyleContext->GetStyleData(eStyleStruct_Text);
switch (text->mTextAlign) {
case NS_STYLE_TEXT_ALIGN_RIGHT:
x0 += width - newWidth;
break;
case NS_STYLE_TEXT_ALIGN_LEFT:
break;
default:
case NS_STYLE_TEXT_ALIGN_CENTER:
x0 += (width - newWidth) / 2;
break;
}
}
width = newWidth;
// Center hrule vertically within the available space
y0 += (height - thickness) / 2;
height = thickness;
// To shade or not to shade, that is the question. Begin by collecting the
// three decision criteria: rendering to the printer or the display, is the
// "Beveled Lines" checkbox set in the page setup dialog, and does the tag
// have the NOSHADE attribute set.
PRBool printing = nsGlobalVariables::Instance()->GetPrinting(&aPresContext);
PRBool bevel = nsGlobalVariables::Instance()->GetBeveledLines();
PRBool noShadeAttribute = GetNoShade();
// Now that we have the data to make the shading criteria, we next
// collect the decision criteria for rending in solid black:
// printing (which we already have) and the "Black Lines" setting in
// the page setup dialog
PRBool blackLines = nsGlobalVariables::Instance()->GetBlackLines();
nscolor colors[2];
// Get the background color that applies to this HR
if (printing && blackLines)
{
colors[0] = NS_RGB(0,0,0);
colors[1] = colors[0];
}
else
{
// XXX Get correct color by finding the first parent that actually
// specifies a color.
NS_Get3DColors(colors, color->mBackgroundColor);
}
// Draw a "shadowed" box around the rule area
if (!noShadeAttribute && ((printing && bevel) || !printing)) {
// Lines render inclusively on the both the starting and ending
// coordinate, so reduce the end coordinates by one pixel.
nscoord x1 = nscoord(x0 + width - NSIntPixelsToTwips(1, p2t));
nscoord y1 = nscoord(y0 + height - NSIntPixelsToTwips(1, p2t));
// Draw bottom and right lines
aRenderingContext.SetColor (colors[1]);
aRenderingContext.DrawLine (x1, y0, x1, y1);
aRenderingContext.DrawLine (x1, y1, x0, y1);
// Draw top and left lines
aRenderingContext.SetColor (colors[0]);
aRenderingContext.DrawLine (x0, y1, x0, y0);
aRenderingContext.DrawLine (x0, y0, x1, y0);
} else {
// When a rule is not shaded, then we use a uniform color and
// draw half-circles on the end points.
aRenderingContext.SetColor (colors[0]);
nscoord diameter = height;
if ((diameter > width) || (diameter < NSIntPixelsToTwips(3, p2t))) {
// The half-circles on the ends of the rule aren't going to
// look right so don't bother drawing them.
aRenderingContext.FillRect(x0, y0, width, height);
} else {
aRenderingContext.FillArc(x0, y0, diameter, diameter, 90.0f, 270.0f);
aRenderingContext.FillArc(x0 + width - diameter, y0,
diameter, diameter, 270.0f, 180.0f);
aRenderingContext.FillRect(x0 + diameter/2, y0,
width - diameter, height);
}
}
return NS_OK;
}
NS_IMETHODIMP
HRuleFrame::Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
NS_PRECONDITION(mState & NS_FRAME_IN_REFLOW, "frame is not in reflow");
// XXX add in code to check for width/height being set via css
// and if set use them instead of calling GetDesiredSize.
GetDesiredSize(&aPresContext, aReflowState, aDesiredSize);
AddBordersAndPadding(&aPresContext, aDesiredSize);
// HR's do not impact the max-element-size, unless a width is specified
// otherwise tables behave badly. This makes sense they are springy.
if (nsnull != aDesiredSize.maxElementSize) {
nscoord onePixel = NSIntPixelsToTwips(1, aPresContext.GetPixelsToTwips());
nsSize size;
PRIntn ss = nsCSSLayout::GetStyleSize(&aPresContext, aReflowState, size);
if (NS_SIZE_HAS_WIDTH & ss) {
aDesiredSize.maxElementSize->width = size.width;
aDesiredSize.maxElementSize->height = onePixel;
}
else {
aDesiredSize.maxElementSize->width = onePixel;
aDesiredSize.maxElementSize->height = onePixel;
}
}
aStatus = NS_FRAME_COMPLETE;
return NS_OK;
}
void
HRuleFrame::GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize)
{
nsSize size;
PRIntn ss = nsCSSLayout::GetStyleSize(aPresContext, aReflowState, size);
if (NS_SIZE_HAS_WIDTH & ss) {
aDesiredSize.width = size.width;
}
else {
if (NS_UNCONSTRAINEDSIZE == aReflowState.maxSize.width) {
aDesiredSize.width = nscoord(aPresContext->GetPixelsToTwips());
}
else {
aDesiredSize.width = aReflowState.maxSize.width;
}
}
mComputedWidth = aDesiredSize.width;
if (aReflowState.maxSize.width != NS_UNCONSTRAINEDSIZE) {
if (aDesiredSize.width < aReflowState.maxSize.width) {
aDesiredSize.width = aReflowState.maxSize.width;
}
}
// XXX should we interpret css's height property as thickness? or as
// line-height? In the meantime, ignore it...
nscoord lineHeight;
// Get the thickness of the rule (this is not css's height property)
float p2t = aPresContext->GetPixelsToTwips();
nscoord thickness = NSIntPixelsToTwips(GetThickness(), p2t);
// Compute height of "line" that hrule will layout within. Use the
// default font to do this.
lineHeight = thickness + NSIntPixelsToTwips(2, p2t);
const nsFont& defaultFont = aPresContext->GetDefaultFont();
nsIFontMetrics* fm = aPresContext->GetMetricsFor(defaultFont);
nscoord defaultLineHeight;
fm->GetHeight(defaultLineHeight);
NS_RELEASE(fm);
if (lineHeight < defaultLineHeight) {
lineHeight = defaultLineHeight;
}
aDesiredSize.height = lineHeight;
aDesiredSize.ascent = lineHeight;
aDesiredSize.descent = 0;
}
PRInt32
HRuleFrame::GetThickness()
{
PRInt32 result = DEFAULT_THICKNESS;
// See if the underlying content is an HR that also implements the
// nsIHTMLContent API.
// XXX the dependency on nsIHTMLContent is done to avoid reparsing
// the size value.
nsIDOMHTMLHRElement* hr = nsnull;
nsIHTMLContent* hc = nsnull;
mContent->QueryInterface(kIDOMHTMLHRElementIID, (void**) &hr);
mContent->QueryInterface(kIHTMLContentIID, (void**) &hc);
if ((nsnull != hr) && (nsnull != hc)) {
// Winner. Get the size attribute to determine how thick the HR
// should be.
nsHTMLValue value;
if (NS_CONTENT_ATTR_HAS_VALUE ==
hc->GetAttribute(nsHTMLAtoms::size, value)) {
if (value.GetUnit() == eHTMLUnit_Pixel) {
PRInt32 pixels = value.GetPixelValue();
switch (pixels) {
case 0:
case 1:
result = 1;
break;
default:
result = pixels + 1;
break;
}
}
}
}
NS_IF_RELEASE(hc);
NS_IF_RELEASE(hr);
return result;
}
PRBool
HRuleFrame::GetNoShade()
{
PRBool result = PR_FALSE;
nsIDOMHTMLHRElement* hr = nsnull;
mContent->QueryInterface(kIDOMHTMLHRElementIID, (void**) &hr);
if (nsnull != hr) {
hr->GetNoShade(&result);
NS_RELEASE(hr);
}
return result;
}

View File

@ -0,0 +1,626 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsHTMLContainer.h"
#include "nsContainerFrame.h"
#include "nsIDocument.h"
#include "nsIReflowCommand.h"
#include "nsIPresContext.h"
#include "nsIStyleContext.h"
#include "nsViewsCID.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsIContentDelegate.h"
#include "nsIWidget.h"
#include "nsHTMLIIDs.h"
#include "nsPageFrame.h"
#include "nsIRenderingContext.h"
#include "nsGUIEvent.h"
#include "nsDOMEvent.h"
#include "nsStyleConsts.h"
#include "nsIViewManager.h"
#include "nsHTMLAtoms.h"
#include "nsIEventStateManager.h"
#include "nsIDeviceContext.h"
class RootFrame : public nsContainerFrame {
public:
RootFrame(nsIContent* aContent);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus);
};
// Pseudo frame created by the root frame
class RootContentFrame : public nsContainerFrame {
public:
RootContentFrame(nsIContent* aContent, nsIFrame* aParent);
NS_IMETHOD Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
NS_IMETHOD HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus);
protected:
void CreateFirstChild(nsIPresContext* aPresContext);
};
//----------------------------------------------------------------------
nsresult
NS_NewHTMLFrame(nsIContent* aContent, nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
RootFrame* frame = new RootFrame(aContent);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
RootFrame::RootFrame(nsIContent* aContent)
: nsContainerFrame(aContent, nsnull)
{
}
NS_IMETHODIMP
RootFrame::Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
NS_FRAME_TRACE_REFLOW_IN("RootFrame::Reflow");
#ifdef NS_DEBUG
PreReflowCheck();
#endif
aStatus = NS_FRAME_COMPLETE;
if (eReflowReason_Incremental == aReflowState.reason) {
// We don't expect the target of the reflow command to be the root frame
#ifdef NS_DEBUG
NS_ASSERTION(nsnull != aReflowState.reflowCommand, "no reflow command");
nsIFrame* target;
aReflowState.reflowCommand->GetTarget(target);
NS_ASSERTION(target != this, "root frame is reflow command target");
#endif
// Verify that the next frame in the reflow chain is our pseudo frame
nsIFrame* next;
aReflowState.reflowCommand->GetNext(next);
NS_ASSERTION(next == mFirstChild, "unexpected next reflow command frame");
} else {
// Do we have any children?
if (nsnull == mFirstChild) {
// No. Create a pseudo frame
NS_ASSERTION(eReflowReason_Initial == aReflowState.reason, "unexpected reflow reason");
mFirstChild = new RootContentFrame(mContent, this);
mChildCount = 1;
nsIStyleContext* style = aPresContext.ResolvePseudoStyleContextFor(nsHTMLAtoms::rootContentPseudo, this);
mFirstChild->SetStyleContext(&aPresContext,style);
NS_RELEASE(style);
}
}
// Reflow our pseudo frame. It will choose whetever height its child frame
// wants
if (nsnull != mFirstChild) {
nsReflowMetrics desiredSize(nsnull);
nsReflowState kidReflowState(mFirstChild, aReflowState, aReflowState.maxSize);
mFirstChild->WillReflow(aPresContext);
aStatus = ReflowChild(mFirstChild, &aPresContext, desiredSize, kidReflowState);
// Place and size the child
nsRect rect(0, 0, desiredSize.width, desiredSize.height);
mFirstChild->SetRect(rect);
mFirstChild->DidReflow(aPresContext, NS_FRAME_REFLOW_FINISHED);
mLastContentOffset = ((RootContentFrame*)mFirstChild)->GetLastContentOffset();
}
// Return the max size as our desired size
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.height = aReflowState.maxSize.height;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
#ifdef NS_DEBUG
PostReflowCheck(aStatus);
#endif
NS_FRAME_TRACE_REFLOW_OUT("RootFrame::Reflow", aStatus);
return NS_OK;
}
NS_IMETHODIMP
RootFrame::HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus)
{
mContent->HandleDOMEvent(aPresContext, (nsEvent*)aEvent, nsnull, DOM_EVENT_INIT, aEventStatus);
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP ||
aEvent->message == NS_MOUSE_MIDDLE_BUTTON_UP ||
aEvent->message == NS_MOUSE_RIGHT_BUTTON_UP) {
nsFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
#if 0
if (aEventStatus != nsEventStatus_eConsumeNoDefault) {
switch (aEvent->message) {
case NS_MOUSE_MOVE:
case NS_MOUSE_ENTER:
{
nsIFrame* target = this;
PRInt32 cursor;
GetCursorAndContentAt(aPresContext, aEvent->point, &target, &mContent, cursor);
if (cursor == NS_STYLE_CURSOR_INHERIT) {
cursor = NS_STYLE_CURSOR_DEFAULT;
}
nsCursor c;
switch (cursor) {
default:
case NS_STYLE_CURSOR_DEFAULT:
c = eCursor_standard;
break;
case NS_STYLE_CURSOR_HAND:
c = eCursor_hyperlink;
break;
case NS_STYLE_CURSOR_IBEAM:
c = eCursor_select;
break;
}
nsIWidget* window;
target->GetWindow(window);
window->SetCursor(c);
NS_RELEASE(window);
}
break;
}
}
#endif
return NS_OK;
}
//----------------------------------------------------------------------
RootContentFrame::RootContentFrame(nsIContent* aContent, nsIFrame* aParent)
: nsContainerFrame(aContent, aParent)
{
// Create a view
nsIFrame* parent;
nsIView* view;
GetParentWithView(parent);
NS_ASSERTION(parent, "GetParentWithView failed");
nsIView* parView;
parent->GetView(parView);
NS_ASSERTION(parView, "no parent with view");
// Create a view
static NS_DEFINE_IID(kViewCID, NS_VIEW_CID);
static NS_DEFINE_IID(kIViewIID, NS_IVIEW_IID);
nsresult result = nsRepository::CreateInstance(kViewCID,
nsnull,
kIViewIID,
(void **)&view);
if (NS_OK == result) {
nsIView* rootView = parView;
nsIViewManager* viewManager;
rootView->GetViewManager(viewManager);
// Initialize the view
NS_ASSERTION(nsnull != viewManager, "null view manager");
view->Init(viewManager, mRect, rootView);
viewManager->InsertChild(rootView, view, 0);
NS_RELEASE(viewManager);
// Remember our view
SetView(view);
}
}
void
RootContentFrame::CreateFirstChild(nsIPresContext* aPresContext)
{
// Are we paginated?
if (aPresContext->IsPaginated()) {
// Yes. Create the first page frame
mFirstChild = new nsPageFrame(mContent, this);
mChildCount = 1;
mLastContentOffset = mFirstContentOffset;
} else {
// Create a frame for the body/frameset child
PRInt32 i, n;
mContent->ChildCount(n);
for (i = 0; i < n; i++) {
nsIContent* child;
mContent->ChildAt(i, child);
if (nsnull != child) {
nsIAtom* tag;
child->GetTag(tag);
if ((nsHTMLAtoms::body == tag) || (nsHTMLAtoms::frameset == tag)) {
// Create a frame
nsIContentDelegate* cd = child->GetDelegate(aPresContext);
if (nsnull != cd) {
nsIStyleContext* kidStyleContext =
aPresContext->ResolveStyleContextFor(child, this);
nsresult rv = cd->CreateFrame(aPresContext, child, this,
kidStyleContext, mFirstChild);
NS_RELEASE(kidStyleContext);
if (NS_OK == rv) {
mChildCount = 1;
mFirstContentOffset = i;
mLastContentOffset = i;
}
NS_RELEASE(cd);
}
}
NS_IF_RELEASE(tag);
NS_RELEASE(child);
}
}
}
}
// XXX Hack
#define PAGE_SPACING_TWIPS 100
NS_IMETHODIMP
RootContentFrame::Reflow(nsIPresContext& aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
NS_FRAME_TRACE_REFLOW_IN("RootContentFrame::Reflow");
#ifdef NS_DEBUG
PreReflowCheck();
#endif
aStatus = NS_FRAME_COMPLETE;
// XXX Incremental reflow code doesn't handle page mode at all...
if (eReflowReason_Incremental == aReflowState.reason) {
// We don't expect the target of the reflow command to be the root
// content frame
#ifdef NS_DEBUG
nsIFrame* target;
aReflowState.reflowCommand->GetTarget(target);
NS_ASSERTION(target != this, "root content frame is reflow command target");
#endif
// Verify the next frame in the reflow chain is our child frame
nsIFrame* next;
aReflowState.reflowCommand->GetNext(next);
NS_ASSERTION(next == mFirstChild, "unexpected next reflow command frame");
nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE);
nsReflowState kidReflowState(next, aReflowState, maxSize);
// Dispatch the reflow command to our child frame. Allow it to be as high
// as it wants
mFirstChild->WillReflow(aPresContext);
aStatus = ReflowChild(mFirstChild, &aPresContext, aDesiredSize, kidReflowState);
// Place and size the child. Make sure the child is at least as
// tall as our max size (the containing window)
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
} else {
nsReflowReason reflowReason = aReflowState.reason;
// Do we have any children?
if (nsnull == mFirstChild) {
// No, create the first child frame
reflowReason = eReflowReason_Initial;
CreateFirstChild(&aPresContext);
}
// Resize our frames
if (nsnull != mFirstChild) {
if (aPresContext.IsPaginated()) {
nscoord y = PAGE_SPACING_TWIPS;
nsReflowMetrics kidSize(aDesiredSize.maxElementSize);
// Compute the size of each page and the x coordinate within
// ourselves that the pages will be placed at.
nsSize pageSize(aPresContext.GetPageWidth(),
aPresContext.GetPageHeight());
nsIDeviceContext *dx = aPresContext.GetDeviceContext();
float sbWidth, sbHeight;
dx->GetScrollBarDimensions(sbWidth, sbHeight);
PRInt32 extra = aReflowState.maxSize.width - PAGE_SPACING_TWIPS*2 -
pageSize.width - NSToCoordRound(sbWidth);
NS_RELEASE(dx);
// Note: nscoord is an unsigned type so don't combine these
// two statements or the extra will be promoted to unsigned
// and the >0 won't work!
nscoord x = PAGE_SPACING_TWIPS;
if (extra > 0) {
x += extra / 2;
}
// Tile the pages vertically
for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) {
// Reflow the page
nsReflowState kidReflowState(kidFrame, aReflowState, pageSize,
reflowReason);
nsReflowStatus status;
// Place and size the page. If the page is narrower than our
// max width then center it horizontally
kidFrame->WillReflow(aPresContext);
kidFrame->MoveTo(x, y);
status = ReflowChild(kidFrame, &aPresContext, kidSize,
kidReflowState);
kidFrame->SetRect(nsRect(x, y, kidSize.width, kidSize.height));
y += kidSize.height;
// Leave a slight gap between the pages
y += PAGE_SPACING_TWIPS;
// Is the page complete?
nsIFrame* kidNextInFlow;
kidFrame->GetNextInFlow(kidNextInFlow);
if (NS_FRAME_IS_COMPLETE(status)) {
NS_ASSERTION(nsnull == kidNextInFlow, "bad child flow list");
} else if (nsnull == kidNextInFlow) {
// The page isn't complete and it doesn't have a next-in-flow so
// create a continuing page
nsIStyleContext* kidSC;
kidFrame->GetStyleContext(&aPresContext, kidSC);
nsIFrame* continuingPage;
nsresult rv = kidFrame->CreateContinuingFrame(aPresContext, this,
kidSC, continuingPage);
NS_RELEASE(kidSC);
reflowReason = eReflowReason_Initial;
// Add it to our child list
#ifdef NS_DEBUG
nsIFrame* kidNextSibling;
kidFrame->GetNextSibling(kidNextSibling);
NS_ASSERTION(nsnull == kidNextSibling, "unexpected sibling");
#endif
kidFrame->SetNextSibling(continuingPage);
mChildCount++;
}
// Get the next page
kidFrame->GetNextSibling(kidFrame);
}
// Return our desired size
aDesiredSize.height = y;
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
aDesiredSize.width = PAGE_SPACING_TWIPS*2 + pageSize.width;
if (aDesiredSize.width < aReflowState.maxSize.width) {
aDesiredSize.width = aReflowState.maxSize.width;
}
} else {
// Allow the frame to be as wide as our max width, and as high
// as it wants to be.
nsSize maxSize(aReflowState.maxSize.width, NS_UNCONSTRAINEDSIZE);
nsReflowState kidReflowState(mFirstChild, aReflowState, maxSize, reflowReason);
// Get the child's desired size. Our child's desired height is our
// desired size
mFirstChild->WillReflow(aPresContext);
aStatus = ReflowChild(mFirstChild, &aPresContext, aDesiredSize, kidReflowState);
NS_ASSERTION(NS_FRAME_IS_COMPLETE(aStatus), "bad status");
// Place and size the child. Make sure the child is at least as
// tall as our max size (the containing window)
if (aDesiredSize.height < aReflowState.maxSize.height) {
aDesiredSize.height = aReflowState.maxSize.height;
}
// Place and size the child
nsRect rect(0, 0, aDesiredSize.width, aDesiredSize.height);
mFirstChild->SetRect(rect);
// Do the necessary repainting
if (eReflowReason_Initial == reflowReason) {
// Repaint the visible area
Invalidate(nsRect(0, 0, aReflowState.maxSize.width, aReflowState.maxSize.height));
} else if (eReflowReason_Resize == aReflowState.reason) {
// Repaint the entire frame
Invalidate(nsRect(0, 0, aReflowState.maxSize.width, aDesiredSize.height));
}
}
}
else {
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.height = aReflowState.maxSize.height;
aDesiredSize.ascent = aDesiredSize.height;
aDesiredSize.descent = 0;
if (nsnull != aDesiredSize.maxElementSize) {
aDesiredSize.maxElementSize->width = 0;
aDesiredSize.maxElementSize->height = 0;
}
}
}
// We are always a pseudo-frame; make sure our content offset is
// properly pushed upwards
nsContainerFrame* parent = (nsContainerFrame*) mGeometricParent;
parent->PropagateContentOffsets(this, mFirstContentOffset,
mLastContentOffset, mLastContentIsComplete);
#ifdef NS_DEBUG
PostReflowCheck(aStatus);
#endif
NS_FRAME_TRACE_REFLOW_OUT("RootContentFrame::Reflow", aStatus);
return NS_OK;
}
NS_IMETHODIMP
RootContentFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
// If we're paginated then fill the dirty rect with white
if (aPresContext.IsPaginated()) {
// Cross hatching would be nicer...
aRenderingContext.SetColor(NS_RGB(255,255,255));
aRenderingContext.FillRect(aDirtyRect);
}
nsContainerFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
return NS_OK;
}
NS_IMETHODIMP
RootContentFrame::HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus)
{
#if 0
mContent->HandleDOMEvent(aPresContext, (nsEvent*)aEvent, nsnull, DOM_EVENT_INIT, aEventStatus);
#else
nsContainerFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
#endif
if (aEventStatus != nsEventStatus_eConsumeNoDefault) {
switch (aEvent->message) {
case NS_MOUSE_MOVE:
case NS_MOUSE_ENTER:
{
nsIFrame* target = this;
nsIContent* mTargetContent = mContent;
PRInt32 cursor;
GetCursorAndContentAt(aPresContext, aEvent->point, &target, &mTargetContent, cursor);
if (cursor == NS_STYLE_CURSOR_INHERIT) {
cursor = NS_STYLE_CURSOR_DEFAULT;
}
nsCursor c;
switch (cursor) {
default:
case NS_STYLE_CURSOR_DEFAULT:
c = eCursor_standard;
break;
case NS_STYLE_CURSOR_HAND:
c = eCursor_hyperlink;
break;
case NS_STYLE_CURSOR_IBEAM:
c = eCursor_select;
break;
}
nsIWidget* window;
target->GetWindow(window);
window->SetCursor(c);
NS_RELEASE(window);
//If the content object under the cursor has changed, fire a mouseover/out
nsIEventStateManager *mStateManager;
nsIContent *mLastContent;
if (NS_OK == aPresContext.GetEventStateManager(&mStateManager)) {
mStateManager->GetLastMouseOverContent(&mLastContent);
if (mLastContent != mTargetContent) {
if (nsnull != mLastContent) {
//fire mouseout
nsEventStatus mStatus = nsEventStatus_eIgnore;
nsMouseEvent mEvent;
mEvent.eventStructType = NS_MOUSE_EVENT;
mEvent.message = NS_MOUSE_EXIT;
mLastContent->HandleDOMEvent(aPresContext, &mEvent, nsnull, DOM_EVENT_INIT, mStatus);
}
//fire mouseover
nsEventStatus mStatus = nsEventStatus_eIgnore;
nsMouseEvent mEvent;
mEvent.eventStructType = NS_MOUSE_EVENT;
mEvent.message = NS_MOUSE_ENTER;
mTargetContent->HandleDOMEvent(aPresContext, &mEvent, nsnull, DOM_EVENT_INIT, mStatus);
mStateManager->SetLastMouseOverContent(mTargetContent);
}
NS_RELEASE(mStateManager);
NS_IF_RELEASE(mLastContent);
}
}
break;
case NS_MOUSE_EXIT:
//Don't know if this is actually hooked up.
{
//Fire of mouseout to the last content object.
nsIEventStateManager *mStateManager;
nsIContent *mLastContent;
if (NS_OK == aPresContext.GetEventStateManager(&mStateManager)) {
mStateManager->GetLastMouseOverContent(&mLastContent);
if (nsnull != mLastContent) {
//fire mouseout
nsEventStatus mStatus = nsEventStatus_eIgnore;
nsMouseEvent mEvent;
mEvent.eventStructType = NS_MOUSE_EVENT;
mEvent.message = NS_MOUSE_EXIT;
mLastContent->HandleDOMEvent(aPresContext, &mEvent, nsnull, DOM_EVENT_INIT, mStatus);
mStateManager->SetLastMouseOverContent(nsnull);
NS_RELEASE(mLastContent);
}
NS_RELEASE(mStateManager);
}
}
break;
case NS_MOUSE_LEFT_BUTTON_UP:
{
nsIEventStateManager *mStateManager;
if (NS_OK == aPresContext.GetEventStateManager(&mStateManager)) {
mStateManager->SetActiveLink(nsnull);
NS_RELEASE(mStateManager);
}
}
break;
}
}
return NS_OK;
}

View File

@ -0,0 +1,928 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsHTMLImage.h"
#include "nsHTMLTagContent.h"
#include "nsString.h"
#include "nsLeafFrame.h"
#include "nsIPresContext.h"
#include "nsIRenderingContext.h"
#include "nsIFrameImageLoader.h"
#include "nsIPresShell.h"
#include "nsHTMLIIDs.h"
#include "nsIImage.h"
#include "nsIWidget.h"
#include "nsHTMLAtoms.h"
#include "nsIHTMLAttributes.h"
#include "nsIDocument.h"
#include "nsIHTMLDocument.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIImageMap.h"
#include "nsILinkHandler.h"
#include "nsIURL.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsCSSLayout.h"
#include "nsHTMLBase.h"
#include "prprf.h"
#include "nsISizeOfHandler.h"
#include "nsIFontMetrics.h"
#include "nsCSSRendering.h"
#include "nsIDOMHTMLImageElement.h"
#define BROKEN_IMAGE_URL "resource:/res/html/broken-image.gif"
#define XP_IS_SPACE(_ch) \
(((_ch) == ' ') || ((_ch) == '\t') || ((_ch) == '\n'))
// XXX image frame layout can be 100% decoupled from the content
// object; all it needs are attributes to work properly
static NS_DEFINE_IID(kIHTMLDocumentIID, NS_IHTMLDOCUMENT_IID);
#if 0
#define nsHTMLImageSuper nsHTMLTagContent
class nsHTMLImage : public nsHTMLImageSuper, public nsIDOMHTMLImageElement {
public:
nsHTMLImage(nsIAtom* aTag);
NS_DECL_ISUPPORTS
NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler) const;
NS_IMETHOD CreateFrame(nsIPresContext* aPresContext,
nsIFrame* aParentFrame,
nsIStyleContext* aStyleContext,
nsIFrame*& aResult);
NS_IMETHOD SetAttribute(nsIAtom* aAttribute, const nsString& aValue,
PRBool aNotify);
NS_IMETHOD MapAttributesInto(nsIStyleContext* aContext,
nsIPresContext* aPresContext);
NS_IMETHOD AttributeToString(nsIAtom* aAttribute,
nsHTMLValue& aValue,
nsString& aResult) const;
NS_FORWARD_IDOMNODE(nsHTMLImageSuper::)
NS_FORWARD_IDOMELEMENT(nsHTMLImageSuper::)
NS_FORWARD_IDOMHTMLELEMENT(nsHTMLImageSuper::)
NS_DECL_IDOMHTMLIMAGEELEMENT
NS_IMETHOD GetScriptObject(nsIScriptContext *aContext, void** aScriptObject);
protected:
virtual ~nsHTMLImage();
void SizeOfWithoutThis(nsISizeOfHandler* aHandler) const;
void TriggerReflow();
};
#endif
#define ImageFrameSuper nsLeafFrame
class ImageFrame : public ImageFrameSuper {
public:
ImageFrame(nsIContent* aContent, nsIFrame* aParentFrame);
NS_IMETHOD DeleteFrame(nsIPresContext& aPresContext);
NS_IMETHOD SizeOf(nsISizeOfHandler* aHandler) const;
NS_IMETHOD Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect);
NS_METHOD HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus);
NS_IMETHOD GetCursorAndContentAt(nsIPresContext& aPresContext,
const nsPoint& aPoint,
nsIFrame** aFrame,
nsIContent** aContent,
PRInt32& aCursor);
NS_IMETHOD ContentChanged(nsIPresShell* aShell,
nsIPresContext* aPresContext,
nsIContent* aChild,
nsISupports* aSubContent);
protected:
virtual ~ImageFrame();
void SizeOfWithoutThis(nsISizeOfHandler* aHandler) const;
virtual void GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize);
nsIImageMap* GetImageMap();
nsHTMLImageLoader mImageLoader;
nsIImageMap* mImageMap;
PRBool mSizeFrozen;
void TriggerLink(nsIPresContext& aPresContext,
const nsString& aURLSpec,
const nsString& aTargetSpec,
PRBool aClick);
PRBool IsServerImageMap();
PRIntn GetSuppress();
nscoord MeasureString(nsIFontMetrics* aFontMetrics,
const PRUnichar* aString,
PRInt32 aLength,
nscoord aMaxWidth,
PRUint32& aMaxFit);
void DisplayAltText(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsString& aAltText,
const nsRect& aRect);
};
// Value's for mSuppress
#define SUPPRESS_UNSET 0
#define DONT_SUPPRESS 1
#define SUPPRESS 2
#define DEFAULT_SUPPRESS 3
// Default alignment value (so we can tell an unset value from a set value)
#define ALIGN_UNSET PRUint8(-1)
//----------------------------------------------------------------------
nsHTMLImageLoader::nsHTMLImageLoader()
{
mImageLoader = nsnull;
mLoadImageFailed = PR_FALSE;
mLoadBrokenImageFailed = PR_FALSE;
mURLSpec = nsnull;
mBaseHREF = nsnull;
}
nsHTMLImageLoader::~nsHTMLImageLoader()
{
NS_IF_RELEASE(mImageLoader);
if (nsnull != mURLSpec) {
delete mURLSpec;
}
if (nsnull != mBaseHREF) {
delete mBaseHREF;
}
}
void
nsHTMLImageLoader::SizeOf(nsISizeOfHandler* aHandler) const
{
aHandler->Add(sizeof(*this));
if (!aHandler->HaveSeen(mURLSpec)) {
mURLSpec->SizeOf(aHandler);
}
if (!aHandler->HaveSeen(mImageLoader)) {
mImageLoader->SizeOf(aHandler);
}
}
nsIImage*
nsHTMLImageLoader::GetImage()
{
nsIImage* image = nsnull;
if (nsnull != mImageLoader) {
mImageLoader->GetImage(image);
}
return image;
}
nsresult
nsHTMLImageLoader::SetURL(const nsString& aURLSpec)
{
if (nsnull != mURLSpec) {
delete mURLSpec;
}
mURLSpec = new nsString(aURLSpec);
if (nsnull == mURLSpec) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsHTMLImageLoader::SetBaseHREF(const nsString& aBaseHREF)
{
if (nsnull != mBaseHREF) {
delete mBaseHREF;
}
mBaseHREF = new nsString(aBaseHREF);
if (nsnull == mBaseHREF) {
return NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
nsresult
nsHTMLImageLoader::StartLoadImage(nsIPresContext* aPresContext,
nsIFrame* aForFrame,
PRBool aNeedSizeUpdate,
PRIntn& aLoadStatus)
{
aLoadStatus = NS_IMAGE_LOAD_STATUS_NONE;
// Get absolute url the first time through
nsresult rv;
nsAutoString src;
if (mLoadImageFailed || (nsnull == mURLSpec)) {
src.Append(BROKEN_IMAGE_URL);
} else {
nsAutoString baseURL;
if (nsnull != mBaseHREF) {
baseURL = *mBaseHREF;
}
// Get documentURL
nsIPresShell* shell;
shell = aPresContext->GetShell();
nsIDocument* doc = shell->GetDocument();
nsIURL* docURL = doc->GetDocumentURL();
// Create an absolute URL
nsresult rv = NS_MakeAbsoluteURL(docURL, baseURL, *mURLSpec, src);
// Release references
NS_RELEASE(shell);
NS_RELEASE(docURL);
NS_RELEASE(doc);
if (NS_OK != rv) {
return rv;
}
}
if (nsnull == mImageLoader) {
// Start image loading. Note that we don't specify a background color
// so transparent images are always rendered using a transparency mask
rv = aPresContext->StartLoadImage(src, nsnull, aForFrame, aNeedSizeUpdate,
mImageLoader);
if (NS_OK != rv) {
return rv;
}
}
// Examine current image load status
mImageLoader->GetImageLoadStatus(aLoadStatus);
if (0 != (aLoadStatus & NS_IMAGE_LOAD_STATUS_ERROR)) {
NS_RELEASE(mImageLoader);
if (mLoadImageFailed) {
// We are doomed. Loading the broken image has just failed.
mLoadBrokenImageFailed = PR_TRUE;
}
else {
// Try again, this time using the broke-image url
mLoadImageFailed = PR_TRUE;
return StartLoadImage(aPresContext, aForFrame, aNeedSizeUpdate, aLoadStatus);
}
}
return NS_OK;
}
void
nsHTMLImageLoader::GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize)
{
nsSize styleSize;
PRIntn ss = nsCSSLayout::GetStyleSize(aPresContext, aReflowState, styleSize);
PRIntn loadStatus;
if (0 != ss) {
if (NS_SIZE_HAS_BOTH == ss) {
StartLoadImage(aPresContext, aReflowState.frame, PR_FALSE, loadStatus);
aDesiredSize.width = styleSize.width;
aDesiredSize.height = styleSize.height;
}
else {
// Preserve aspect ratio of image with unbound dimension.
StartLoadImage(aPresContext, aReflowState.frame, PR_TRUE, loadStatus);
if ((0 == (loadStatus & NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE)) ||
(nsnull == mImageLoader)) {
// Provide a dummy size for now; later on when the image size
// shows up we will reflow to the new size.
aDesiredSize.width = 1;
aDesiredSize.height = 1;
}
else {
float p2t = aPresContext->GetPixelsToTwips();
nsSize imageSize;
mImageLoader->GetSize(imageSize);
float imageWidth = imageSize.width * p2t;
float imageHeight = imageSize.height * p2t;
if (0.0f != imageHeight) {
if (0 != (ss & NS_SIZE_HAS_WIDTH)) {
// We have a width, and an auto height. Compute height
// from width.
aDesiredSize.width = styleSize.width;
aDesiredSize.height =
(nscoord)NSToIntRound(styleSize.width * imageHeight / imageWidth);
}
else {
// We have a height and an auto width. Compute width from
// height.
aDesiredSize.height = styleSize.height;
aDesiredSize.width =
(nscoord)NSToIntRound(styleSize.height * imageWidth / imageHeight);
}
}
else {
// Screwy image
aDesiredSize.width = 1;
aDesiredSize.height = 1;
}
}
}
}
else {
StartLoadImage(aPresContext, aReflowState.frame, PR_TRUE, loadStatus);
if ((0 == (loadStatus & NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE)) ||
(nsnull == mImageLoader)) {
// Provide a dummy size for now; later on when the image size
// shows up we will reflow to the new size.
aDesiredSize.width = 1;
aDesiredSize.height = 1;
printf ("in image loader, dummy size of 1 returned\n");
} else {
float p2t = aPresContext->GetPixelsToTwips();
nsSize imageSize;
mImageLoader->GetSize(imageSize);
aDesiredSize.width = NSIntPixelsToTwips(imageSize.width, p2t);
aDesiredSize.height = NSIntPixelsToTwips(imageSize.height, p2t);
printf ("in image loader, real size of %d returned\n", aDesiredSize.width);
}
}
}
//----------------------------------------------------------------------
nsresult
NS_NewImageFrame(nsIContent* aContent,
nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
ImageFrame* frame = new ImageFrame(aContent, aParentFrame);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
ImageFrame::ImageFrame(nsIContent* aContent, nsIFrame* aParentFrame)
: nsLeafFrame(aContent, aParentFrame)
{
}
ImageFrame::~ImageFrame()
{
}
NS_METHOD
ImageFrame::DeleteFrame(nsIPresContext& aPresContext)
{
NS_IF_RELEASE(mImageMap);
// Release image loader first so that it's refcnt can go to zero
mImageLoader.DestroyLoader();
return nsLeafFrame::DeleteFrame(aPresContext);
}
NS_IMETHODIMP
ImageFrame::SizeOf(nsISizeOfHandler* aHandler) const
{
aHandler->Add(sizeof(*this));
ImageFrame::SizeOfWithoutThis(aHandler);
return NS_OK;
}
void
ImageFrame::SizeOfWithoutThis(nsISizeOfHandler* aHandler) const
{
ImageFrameSuper::SizeOfWithoutThis(aHandler);
mImageLoader.SizeOf(aHandler);
if (!aHandler->HaveSeen(mImageMap)) {
mImageMap->SizeOf(aHandler);
}
}
void
ImageFrame::GetDesiredSize(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
nsReflowMetrics& aDesiredSize)
{
if (mSizeFrozen) {
aDesiredSize.width = mRect.width;
aDesiredSize.height = mRect.height;
}
else {
// XXX Don't create a view, because we want whatever is below the image
// to show through while the image is loading; Likewise for transparent
// images and broken images
//
// What we really want to do is to create a view, and indicate that the
// view has a transparent content area. Do this while it's loading,
// and then when it's fully loaded mark the view as opaque if the
// image is opaque.
//
// We can't use that approach yet, because currently the compositor doesn't
// support transparent views...
#if 0
nsHTMLBase::CreateViewForFrame(aPresContext, this, mStyleContext, PR_TRUE);
#endif
// Setup url before starting the image load
nsAutoString src, base;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("SRC", src)) {
mImageLoader.SetURL(src);
if (NS_CONTENT_ATTR_HAS_VALUE ==
mContent->GetAttribute(NS_HTML_BASE_HREF, base)) {
mImageLoader.SetBaseHREF(base);
}
}
mImageLoader.GetDesiredSize(aPresContext, aReflowState, aDesiredSize);
}
}
// Computes the width of the specified string. aMaxWidth specifies the maximum
// width available. Once this limit is reached no more characters are measured.
// The number of characters that fit within the maximum width are returned in
// aMaxFit
nscoord
ImageFrame::MeasureString(nsIFontMetrics* aFontMetrics,
const PRUnichar* aString,
PRInt32 aLength,
nscoord aMaxWidth,
PRUint32& aMaxFit)
{
nscoord totalWidth = 0;
nscoord spaceWidth;
aFontMetrics->GetWidth(' ', spaceWidth);
aMaxFit = 0;
while (aLength > 0) {
// Find the next place we can line break
PRUint32 len = aLength;
PRBool trailingSpace = PR_FALSE;
for (PRInt32 i = 0; i < aLength; i++) {
if (XP_IS_SPACE(aString[i]) && (i > 0)) {
len = i; // don't include the space when measuring
trailingSpace = PR_TRUE;
break;
}
}
// Measure this chunk of text, and see if it fits
nscoord width;
aFontMetrics->GetWidth(aString, len, width);
PRBool fits = (totalWidth + width) <= aMaxWidth;
// If it fits on the line, or it's the first word we've processed then
// include it
if (fits || (0 == totalWidth)) {
// New piece fits
totalWidth += width;
// If there's a trailing space then see if it fits as well
if (trailingSpace) {
if ((totalWidth + spaceWidth) <= aMaxWidth) {
totalWidth += spaceWidth;
} else {
// Space won't fit. Leave it at the end but don't include it in
// the width
fits = PR_FALSE;
}
len++;
}
aMaxFit += len;
aString += len;
aLength -= len;
}
if (!fits) {
break;
}
}
return totalWidth;
}
// Formats the alt-text to fit within the specified rectangle. Breaks lines
// between words if a word would extend past the edge of the rectangle
void
ImageFrame::DisplayAltText(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsString& aAltText,
const nsRect& aRect)
{
// Clip so we don't render outside of the rect.
aRenderingContext.PushState();
aRenderingContext.SetClipRect(aRect, nsClipCombine_kIntersect);
const nsStyleColor* color =
(const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
const nsStyleFont* font =
(const nsStyleFont*)mStyleContext->GetStyleData(eStyleStruct_Font);
// Set font and color
aRenderingContext.SetColor(color->mColor);
aRenderingContext.SetFont(font->mFont);
// Format the text to display within the formatting rect
nsIFontMetrics* fm = aRenderingContext.GetFontMetrics();
nscoord maxDescent, height;
fm->GetMaxDescent(maxDescent);
fm->GetHeight(height);
// XXX It would be nice if there was a way to have the font metrics tell
// use where to break the text given a maximum width. At a minimum we need
// to be able to get the break character...
const PRUnichar* str = aAltText.GetUnicode();
PRInt32 strLen = aAltText.Length();
nscoord y = aRect.y;
while ((strLen > 0) && ((y + maxDescent) < aRect.YMost())) {
// Determine how much of the text to display on this line
PRUint32 maxFit; // number of characters that fit
nscoord width = MeasureString(fm, str, strLen, aRect.width, maxFit);
// Display the text
aRenderingContext.DrawString(str, maxFit, aRect.x, y, 0);
// Move to the next line
str += maxFit;
strLen -= maxFit;
y += height;
}
NS_RELEASE(fm);
aRenderingContext.PopState();
}
struct nsRecessedBorder : public nsStyleSpacing {
nsRecessedBorder(nscoord aBorderWidth)
: nsStyleSpacing()
{
nsStyleCoord styleCoord(aBorderWidth);
mBorder.SetLeft(styleCoord);
mBorder.SetTop(styleCoord);
mBorder.SetRight(styleCoord);
mBorder.SetBottom(styleCoord);
mBorderStyle[0] = NS_STYLE_BORDER_STYLE_INSET;
mBorderStyle[1] = NS_STYLE_BORDER_STYLE_INSET;
mBorderStyle[2] = NS_STYLE_BORDER_STYLE_INSET;
mBorderStyle[3] = NS_STYLE_BORDER_STYLE_INSET;
mBorderColor[0] = 0;
mBorderColor[1] = 0;
mBorderColor[2] = 0;
mBorderColor[3] = 0;
mHasCachedMargin = mHasCachedPadding = mHasCachedBorder = PR_FALSE;
}
};
NS_METHOD
ImageFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
if ((0 == mRect.width) || (0 == mRect.height)) {
// Do not render when given a zero area. This avoids some useless
// scaling work while we wait for our image dimensions to arrive
// asynchronously.
return NS_OK;
}
const nsStyleDisplay* disp =
(const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
if (disp->mVisible) {
// First paint background and borders
nsLeafFrame::Paint(aPresContext, aRenderingContext, aDirtyRect);
nsIImage* image = mImageLoader.GetImage();
if (nsnull == image) {
// No image yet. Draw the icon that indicates we're loading, and display
// the alt-text
nsAutoString altText;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("ALT", altText)) {
// Display a recessed one-pixel border in the inner area
nsRect inner;
GetInnerArea(&aPresContext, inner);
float p2t = aPresContext.GetPixelsToTwips();
nsRecessedBorder recessedBorder(NSIntPixelsToTwips(1, p2t));
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this, inner,
inner, recessedBorder, 0);
inner.Deflate(NSIntPixelsToTwips(1, p2t), NSIntPixelsToTwips(1, p2t));
// Leave a 8 pixel left/right padding, and a 5 pixel top/bottom padding
inner.Deflate(NSIntPixelsToTwips(8, p2t), NSIntPixelsToTwips(5, p2t));
// If there's room, then display the alt-text
if (!inner.IsEmpty()) {
DisplayAltText(aPresContext, aRenderingContext, altText, inner);
}
}
return NS_OK;
}
// Now render the image into our inner area (the area without the
// borders and padding)
nsRect inner;
GetInnerArea(&aPresContext, inner);
if (mImageLoader.GetLoadImageFailed()) {
float p2t = aPresContext.GetPixelsToTwips();
inner.width = NSIntPixelsToTwips(image->GetWidth(), p2t);
inner.height = NSIntPixelsToTwips(image->GetHeight(), p2t);
}
aRenderingContext.DrawImage(image, inner);
if (GetShowFrameBorders()) {
nsIImageMap* map = GetImageMap();
if (nsnull != map) {
aRenderingContext.SetColor(NS_RGB(0, 0, 0));
aRenderingContext.PushState();
aRenderingContext.Translate(inner.x, inner.y);
map->Draw(aPresContext, aRenderingContext);
aRenderingContext.PopState();
}
}
}
return NS_OK;
}
nsIImageMap*
ImageFrame::GetImageMap()
{
if (nsnull == mImageMap) {
nsAutoString usemap;
mContent->GetAttribute("usemap", usemap);
if (0 == usemap.Length()) {
return nsnull;
}
nsIDocument* doc = nsnull;
mContent->GetDocument(doc);
if (nsnull == doc) {
return nsnull;
}
if (usemap.First() == '#') {
usemap.Cut(0, 1);
}
nsIHTMLDocument* hdoc;
nsresult rv = doc->QueryInterface(kIHTMLDocumentIID, (void**)&hdoc);
NS_RELEASE(doc);
if (NS_OK == rv) {
nsIImageMap* map;
rv = hdoc->GetImageMap(usemap, &map);
NS_RELEASE(hdoc);
if (NS_OK == rv) {
mImageMap = map;
}
}
}
NS_IF_ADDREF(mImageMap);
return mImageMap;
}
void
ImageFrame::TriggerLink(nsIPresContext& aPresContext,
const nsString& aURLSpec,
const nsString& aTargetSpec,
PRBool aClick)
{
nsILinkHandler* handler = nsnull;
aPresContext.GetLinkHandler(&handler);
if (nsnull != handler) {
if (aClick) {
handler->OnLinkClick(this, aURLSpec, aTargetSpec);
}
else {
handler->OnOverLink(this, aURLSpec, aTargetSpec);
}
}
}
PRBool
ImageFrame::IsServerImageMap()
{
nsAutoString ismap;
return NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("ismap", ismap);
}
PRIntn
ImageFrame::GetSuppress()
{
nsAutoString s;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("suppress", s)) {
if (s.EqualsIgnoreCase("true")) {
return SUPPRESS;
} else if (s.EqualsIgnoreCase("false")) {
return DONT_SUPPRESS;
}
}
return DEFAULT_SUPPRESS;
}
// XXX what should clicks on transparent pixels do?
NS_METHOD
ImageFrame::HandleEvent(nsIPresContext& aPresContext,
nsGUIEvent* aEvent,
nsEventStatus& aEventStatus)
{
nsIImageMap* map;
aEventStatus = nsEventStatus_eIgnore;
switch (aEvent->message) {
case NS_MOUSE_LEFT_BUTTON_UP:
case NS_MOUSE_MOVE:
map = GetImageMap();
if ((nsnull != map) || IsServerImageMap()) {
nsIURL* docURL = nsnull;
nsIDocument* doc = nsnull;
mContent->GetDocument(doc);
if (nsnull != doc) {
docURL = doc->GetDocumentURL();
NS_RELEASE(doc);
}
// Ask map if the x,y coordinates are in a clickable area
float t2p = aPresContext.GetTwipsToPixels();
nsAutoString absURL, target, altText;
PRBool suppress;
if (nsnull != map) {
// Subtract out border and padding here so that we are looking
// at the right coordinates. Hit detection against area tags
// is done after the mouse wanders over the image, not over
// the image's borders.
nsRect inner;
GetInnerArea(&aPresContext, inner);
PRInt32 x = NSTwipsToIntPixels((aEvent->point.x - inner.x), t2p);
PRInt32 y = NSTwipsToIntPixels((aEvent->point.y - inner.y), t2p);
nsresult r = map->IsInside(x, y, docURL, absURL, target, altText,
&suppress);
NS_IF_RELEASE(docURL);
NS_RELEASE(map);
if (NS_OK == r) {
// We hit a clickable area. Time to go somewhere...
PRBool clicked = PR_FALSE;
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
aEventStatus = nsEventStatus_eConsumeNoDefault;
clicked = PR_TRUE;
}
TriggerLink(aPresContext, absURL, target, clicked);
}
}
else {
suppress = GetSuppress();
nsAutoString baseURL;
mContent->GetAttribute(NS_HTML_BASE_HREF, baseURL);
nsAutoString src;
mContent->GetAttribute("src", src);
NS_MakeAbsoluteURL(docURL, baseURL, src, absURL);
// Note: We don't subtract out the border/padding here to remain
// compatible with navigator. [ick]
PRInt32 x = NSTwipsToIntPixels(aEvent->point.x, t2p);
PRInt32 y = NSTwipsToIntPixels(aEvent->point.y, t2p);
char cbuf[50];
PR_snprintf(cbuf, sizeof(cbuf), "?%d,%d", x, y);
absURL.Append(cbuf);
PRBool clicked = PR_FALSE;
if (aEvent->message == NS_MOUSE_LEFT_BUTTON_UP) {
aEventStatus = nsEventStatus_eConsumeNoDefault;
clicked = PR_TRUE;
}
TriggerLink(aPresContext, absURL, target, clicked);
}
break;
}
// FALL THROUGH
default:
// Let default event handler deal with it
return nsLeafFrame::HandleEvent(aPresContext, aEvent, aEventStatus);
}
return NS_OK;
}
NS_METHOD
ImageFrame::GetCursorAndContentAt(nsIPresContext& aPresContext,
const nsPoint& aPoint,
nsIFrame** aFrame,
nsIContent** aContent,
PRInt32& aCursor)
{
// The default cursor is to have no cursor
aCursor = NS_STYLE_CURSOR_INHERIT;
*aContent = mContent;
const nsStyleColor* styleColor = (const nsStyleColor*)
mStyleContext->GetStyleData(eStyleStruct_Color);
if (styleColor->mCursor != NS_STYLE_CURSOR_INHERIT) {
// If we have a particular cursor, use it
*aFrame = this;
aCursor = (PRInt32) styleColor->mCursor;
}
nsIImageMap* map = GetImageMap();
if (nsnull != map) {
nsRect inner;
GetInnerArea(&aPresContext, inner);
aCursor = NS_STYLE_CURSOR_DEFAULT;
float t2p = aPresContext.GetTwipsToPixels();
PRInt32 x = NSTwipsToIntPixels((aPoint.x - inner.x), t2p);
PRInt32 y = NSTwipsToIntPixels((aPoint.y - inner.y), t2p);
if (NS_OK == map->IsInside(x, y)) {
aCursor = NS_STYLE_CURSOR_HAND;
}
NS_RELEASE(map);
}
return NS_OK;
}
NS_IMETHODIMP
ImageFrame::ContentChanged(nsIPresShell* aShell,
nsIPresContext* aPresContext,
nsIContent* aChild,
nsISupports* aSubContent)
{
// See if the src attribute changed; if it did then trigger a redraw
// by firing up a new image load request. Otherwise let our base
// class handle the content-changed request.
nsAutoString oldSRC;
mImageLoader.GetURL(oldSRC);
// Get src attribute's value and construct a new absolute url from it
nsAutoString newSRC;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("SRC", newSRC)) {
if (!oldSRC.Equals(newSRC)) {
mSizeFrozen = PR_TRUE;
#ifdef NS_DEBUG
char oldcbuf[100], newcbuf[100];
oldSRC.ToCString(oldcbuf, sizeof(oldcbuf));
newSRC.ToCString(newcbuf, sizeof(newcbuf));
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("ImageFrame::ContentChanged: new image source; old='%s' new='%s'",
oldcbuf, newcbuf));
#endif
// Get rid of old image loader and start a new image load going
mImageLoader.DestroyLoader();
// Fire up a new image load request
PRIntn loadStatus;
mImageLoader.SetURL(newSRC);
mImageLoader.StartLoadImage(aPresContext, this, PR_FALSE, loadStatus);
NS_FRAME_TRACE(NS_FRAME_TRACE_CALLS,
("ImageFrame::ContentChanged: loadImage status=%x",
loadStatus));
// If the image is already ready then we need to trigger a
// redraw because the image loader won't.
if (loadStatus & NS_IMAGE_LOAD_STATUS_IMAGE_READY) {
// XXX Stuff this into a method on nsIPresShell/Context
nsRect bounds;
nsPoint offset;
nsIView* view;
GetOffsetFromView(offset, view);
nsIViewManager* vm;
view->GetViewManager(vm);
bounds.x = offset.x;
bounds.y = offset.y;
bounds.width = mRect.width;
bounds.height = mRect.height;
vm->UpdateView(view, bounds, 0);
NS_RELEASE(vm);
}
return NS_OK;
}
}
return ImageFrameSuper::ContentChanged(aShell, aPresContext, aChild,
aSubContent);
}

View File

@ -0,0 +1,191 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsIHTMLContent.h"
#include "nsFrame.h"
#include "nsIInlineReflow.h"
#include "nsCSSLineLayout.h"
#include "nsHTMLIIDs.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsHTMLAtoms.h"
#include "nsUnitConversion.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
// Spacer type's
#define TYPE_WORD 0 // horizontal space
#define TYPE_LINE 1 // line-break + vertical space
#define TYPE_IMAGE 2 // acts like a sized image with nothing to see
class SpacerFrame : public nsFrame, private nsIInlineReflow {
public:
SpacerFrame(nsIContent* aContent, nsIFrame* aParentFrame);
// nsISupports
NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr);
// nsIInlineReflow
NS_IMETHOD FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand);
NS_IMETHOD InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState);
PRUint8 GetType();
protected:
virtual ~SpacerFrame();
};
nsresult
NS_NewSpacerFrame(nsIContent* aContent,
nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
nsIFrame* frame = new SpacerFrame(aContent, aParentFrame);
if (nsnull == frame) {
return NS_ERROR_OUT_OF_MEMORY;
}
aResult = frame;
return NS_OK;
}
SpacerFrame::SpacerFrame(nsIContent* aContent, nsIFrame* aParentFrame)
: nsFrame(aContent, aParentFrame)
{
}
SpacerFrame::~SpacerFrame()
{
}
NS_IMETHODIMP
SpacerFrame::QueryInterface(REFNSIID aIID, void** aInstancePtrResult)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null pointer");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
if (aIID.Equals(kIInlineReflowIID)) {
*aInstancePtrResult = (void*) ((nsIInlineReflow*)this);
return NS_OK;
}
return nsFrame::QueryInterface(aIID, aInstancePtrResult);
}
NS_IMETHODIMP
SpacerFrame::InlineReflow(nsCSSLineLayout& aLineLayout,
nsReflowMetrics& aMetrics,
const nsReflowState& aReflowState)
{
nsresult rv = NS_FRAME_COMPLETE;
// By default, we have no area
aMetrics.width = 0;
aMetrics.height = 0;
aMetrics.ascent = 0;
aMetrics.descent = 0;
nscoord width = 0;
nscoord height = 0;
PRUint8 type = GetType();
nsresult ca;
nsIHTMLContent* hc = nsnull;
mContent->QueryInterface(kIHTMLContentIID, (void**) &hc);
if (nsnull != hc) {
if (type != TYPE_IMAGE) {
nsHTMLValue val;
ca = hc->GetAttribute(nsHTMLAtoms::size, val);
if (NS_CONTENT_ATTR_HAS_VALUE == ca) {
width = val.GetPixelValue();
}
} else {
nsHTMLValue val;
ca = hc->GetAttribute(nsHTMLAtoms::width, val);
if (NS_CONTENT_ATTR_HAS_VALUE == ca) {
if (eHTMLUnit_Pixel == val.GetUnit()) {
width = val.GetPixelValue();
}
}
ca = hc->GetAttribute(nsHTMLAtoms::height, val);
if (NS_CONTENT_ATTR_HAS_VALUE == ca) {
if (eHTMLUnit_Pixel == val.GetUnit()) {
height = val.GetPixelValue();
}
}
}
NS_RELEASE(hc);
}
float p2t = aLineLayout.mPresContext->GetPixelsToTwips();
switch (type) {
case TYPE_WORD:
if (0 != width) {
aMetrics.width = NSIntPixelsToTwips(width, p2t);
}
break;
case TYPE_LINE:
if (0 != width) {
rv = NS_INLINE_LINE_BREAK_AFTER(0);
aMetrics.height = NSIntPixelsToTwips(width, p2t);
aMetrics.ascent = aMetrics.height;
}
break;
case TYPE_IMAGE:
aMetrics.width = NSIntPixelsToTwips(width, p2t);
aMetrics.height = NSIntPixelsToTwips(height, p2t);
aMetrics.ascent = aMetrics.height;
break;
}
if (nsnull != aMetrics.maxElementSize) {
aMetrics.maxElementSize->width = aMetrics.width;
aMetrics.maxElementSize->height = aMetrics.height;
}
return rv;
}
NS_IMETHODIMP
SpacerFrame::FindTextRuns(nsCSSLineLayout& aLineLayout,
nsIReflowCommand* aReflowCommand)
{
aLineLayout.EndTextRun();
return NS_OK;
}
PRUint8
SpacerFrame::GetType()
{
PRUint8 type = TYPE_WORD;
nsAutoString value;
if (NS_CONTENT_ATTR_HAS_VALUE == mContent->GetAttribute("type", value)) {
if (value.EqualsIgnoreCase("line") ||
value.EqualsIgnoreCase("vert") ||
value.EqualsIgnoreCase("vertical")) {
return TYPE_LINE;
}
if (value.EqualsIgnoreCase("block")) {
return TYPE_IMAGE;
}
}
return type;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsHTMLParts.h"
#include "nsHTMLTagContent.h"
#include "nsFrame.h"
#include "nsHTMLIIDs.h"
nsresult
NS_NewWBRFrame(nsIContent* aContent,
nsIFrame* aParentFrame,
nsIFrame*& aResult)
{
nsIFrame* frame = nsnull;
nsresult rv = nsFrame::NewFrame(&frame, aContent, aParentFrame);
if (NS_OK != rv) {
return rv;
}
aResult = frame;
return NS_OK;
}