Files
Mozilla/mozilla/layout/html/content/src/nsHTMLImageElement.cpp

1074 lines
30 KiB
C++

/* -*- 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.1 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
* implied. See the License for the specific language governing
* rights and limitations under the License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is Netscape Communications
* Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All
* Rights Reserved.
*
* Contributor(s):
*/
#include "nsIDOMHTMLImageElement.h"
#include "nsIDOMImage.h"
#include "nsIScriptObjectOwner.h"
#include "nsIDOMEventReceiver.h"
#include "nsIHTMLContent.h"
#include "nsGenericHTMLElement.h"
#include "nsHTMLAtoms.h"
#include "nsHTMLIIDs.h"
#include "nsIStyleContext.h"
#include "nsIMutableStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIPresContext.h"
#include "nsIPresShell.h"
#include "nsIHTMLAttributes.h"
#include "nsIJSScriptObject.h"
#include "nsIJSNativeInitializer.h"
#include "nsSize.h"
#include "nsIDocument.h"
#include "nsIDOMWindowInternal.h"
#include "nsIDOMDocument.h"
#include "nsIScriptContext.h"
#include "nsIScriptGlobalObject.h"
#include "nsIURL.h"
#include "nsIIOService.h"
#include "nsIURL.h"
#include "nsIServiceManager.h"
#include "nsNetUtil.h"
#include "nsLayoutUtils.h"
#include "nsIWebShell.h"
#include "nsIFrame.h"
#include "nsImageFrame.h"
#include "nsFrameImageLoader.h"
#include "nsLayoutAtoms.h"
#include "nsNodeInfoManager.h"
#include "nsIFrameImageLoader.h"
// XXX nav attrs: suppress
class nsHTMLImageElement : public nsGenericHTMLLeafElement,
public nsIDOMHTMLImageElement,
public nsIDOMImage,
public nsIJSNativeInitializer
{
public:
nsHTMLImageElement();
virtual ~nsHTMLImageElement();
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIDOMNode
NS_FORWARD_IDOMNODE_NO_CLONENODE(nsGenericHTMLLeafElement::)
// nsIDOMElement
NS_FORWARD_IDOMELEMENT(nsGenericHTMLLeafElement::)
// nsIDOMHTMLElement
NS_FORWARD_IDOMHTMLELEMENT(nsGenericHTMLLeafElement::)
// nsIDOMHTMLImageElement
NS_DECL_IDOMHTMLIMAGEELEMENT
// nsIDOMImage
NS_DECL_IDOMIMAGE
// nsIJSScriptObject
virtual PRBool GetProperty(JSContext *aContext, JSObject *aObj,
jsval aID, jsval *aVp);
virtual PRBool SetProperty(JSContext *aContext, JSObject *aObj,
jsval aID, jsval *aVp);
virtual PRBool Resolve(JSContext *aContext, JSObject *aObj, jsval aID,
PRBool *aDidDefineProperty);
// nsIJSNativeInitializer
NS_IMETHOD Initialize(JSContext* aContext, JSObject *aObj,
PRUint32 argc, jsval *argv);
NS_IMETHOD SetDocument(nsIDocument* aDocument, PRBool aDeep,
PRBool aCompileEventHandlers);
NS_IMETHOD StringToAttribute(nsIAtom* aAttribute,
const nsAReadableString& aValue,
nsHTMLValue& aResult);
NS_IMETHOD AttributeToString(nsIAtom* aAttribute,
const nsHTMLValue& aValue,
nsAWritableString& aResult) const;
NS_IMETHOD GetMappedAttributeImpact(const nsIAtom* aAttribute,
PRInt32& aHint) const;
NS_IMETHOD GetAttributeMappingFunctions(nsMapAttributesFunc& aFontMapFunc,
nsMapAttributesFunc& aMapFunc) const;
NS_IMETHOD HandleDOMEvent(nsIPresContext* aPresContext, nsEvent* aEvent,
nsIDOMEvent** aDOMEvent, PRUint32 aFlags,
nsEventStatus* aEventStatus);
NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const;
protected:
nsresult SetSrcInner(nsIURI* aBaseURL, const nsAReadableString& aSrc);
nsresult GetCallerSourceURL(JSContext* cx, nsIURI** sourceURL);
nsresult GetImageFrame(nsImageFrame** aImageFrame);
// Only used if this is a script constructed image
nsIDocument* mOwnerDocument;
static nsresult ImageLibCallBack(nsIPresContext* aPresContext,
nsIFrameImageLoader* aLoader,
nsIFrame* aFrame,
void* aClosure,
PRUint32 aStatus);
nsCOMPtr<nsIFrameImageLoader> mLoader;
};
nsresult
NS_NewHTMLImageElement(nsIHTMLContent** aInstancePtrResult,
nsINodeInfo *aNodeInfo)
{
NS_ENSURE_ARG_POINTER(aInstancePtrResult);
/*
* nsHTMLImageElement's will be created without a nsINodeInfo passed in
* if someone says "var img = new Image();" in JavaScript, in a case like
* that we request the nsINodeInfo from the anonymous nodeinfo list.
*/
nsCOMPtr<nsINodeInfo> nodeInfo(aNodeInfo);
if (!nodeInfo) {
nsCOMPtr<nsINodeInfoManager> nodeInfoManager;
nsresult rv;
rv = nsNodeInfoManager::GetAnonymousManager(*getter_AddRefs(nodeInfoManager));
NS_ENSURE_SUCCESS(rv, rv);
rv = nodeInfoManager->GetNodeInfo(nsHTMLAtoms::img, nsnull,
kNameSpaceID_None,
*getter_AddRefs(nodeInfo));
NS_ENSURE_SUCCESS(rv, rv);
}
nsHTMLImageElement* it = new nsHTMLImageElement();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = it->Init(nodeInfo);
if (NS_FAILED(rv)) {
delete it;
return rv;
}
*aInstancePtrResult = NS_STATIC_CAST(nsIHTMLContent *, it);
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
nsHTMLImageElement::nsHTMLImageElement()
{
mOwnerDocument = nsnull;
}
nsHTMLImageElement::~nsHTMLImageElement()
{
NS_IF_RELEASE(mOwnerDocument);
if (mLoader)
mLoader->RemoveFrame(this);
}
NS_IMPL_ADDREF_INHERITED(nsHTMLImageElement, nsGenericElement)
NS_IMPL_RELEASE_INHERITED(nsHTMLImageElement, nsGenericElement)
NS_IMPL_HTMLCONTENT_QI3(nsHTMLImageElement, nsGenericHTMLLeafElement,
nsIDOMHTMLImageElement, nsIDOMImage,
nsIJSNativeInitializer);
nsresult
nsHTMLImageElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
nsHTMLImageElement* it = new nsHTMLImageElement();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCOMPtr<nsIDOMNode> kungFuDeathGrip(it);
nsresult rv = it->Init(mNodeInfo);
if (NS_FAILED(rv))
return rv;
CopyInnerTo(this, it, aDeep);
*aReturn = NS_STATIC_CAST(nsIDOMNode *, it);
NS_ADDREF(*aReturn);
return NS_OK;
}
NS_IMPL_STRING_ATTR(nsHTMLImageElement, LowSrc, lowsrc)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Name, name)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Align, align)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Alt, alt)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Border, border)
NS_IMPL_INT_ATTR(nsHTMLImageElement, Border, border)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Hspace, hspace)
NS_IMPL_INT_ATTR(nsHTMLImageElement, Hspace, hspace)
NS_IMPL_BOOL_ATTR(nsHTMLImageElement, IsMap, ismap)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, LongDesc, longdesc)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Lowsrc, lowsrc)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, UseMap, usemap)
NS_IMPL_STRING_ATTR(nsHTMLImageElement, Vspace, vspace)
NS_IMPL_INT_ATTR(nsHTMLImageElement, Vspace, vspace)
nsresult
nsHTMLImageElement::GetImageFrame(nsImageFrame** aImageFrame)
{
nsresult result;
nsCOMPtr<nsIPresContext> context;
nsCOMPtr<nsIPresShell> shell;
if (mDocument) {
// Make sure the presentation is up-to-date
result = mDocument->FlushPendingNotifications();
if (NS_FAILED(result)) {
return result;
}
}
result = GetPresContext(this, getter_AddRefs(context));
if (NS_FAILED(result)) {
return result;
}
result = context->GetShell(getter_AddRefs(shell));
if (NS_FAILED(result)) {
return result;
}
nsIFrame* frame;
result = shell->GetPrimaryFrameFor(this, &frame);
if (NS_FAILED(result)) {
return result;
}
if (frame) {
nsCOMPtr<nsIAtom> type;
frame->GetFrameType(getter_AddRefs(type));
if (type.get() == nsLayoutAtoms::imageFrame) {
// XXX We could have created an interface for this, but Troy
// preferred the ugliness of a static cast to the weight of
// a new interface.
nsImageFrame* imageFrame = NS_STATIC_CAST(nsImageFrame*, frame);
*aImageFrame = imageFrame;
return NS_OK;
}
}
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsHTMLImageElement::GetComplete(PRBool* aComplete)
{
NS_ENSURE_ARG_POINTER(aComplete);
*aComplete = PR_FALSE;
nsresult result;
nsImageFrame* imageFrame;
result = GetImageFrame(&imageFrame);
if (NS_SUCCEEDED(result) && imageFrame) {
result = imageFrame->IsImageComplete(aComplete);
} else {
result = NS_OK;
*aComplete = !mLoader;
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::GetHeight(nsAWritableString& aValue)
{
nsresult rv = nsGenericHTMLLeafElement::GetAttribute(kNameSpaceID_None,
nsHTMLAtoms::height,
aValue);
if (rv == NS_CONTENT_ATTR_NOT_THERE) {
PRInt32 height = 0;
aValue.Truncate();
// A zero height most likely means that the image is not loaded yet.
if (NS_SUCCEEDED(GetHeight(&height)) && height) {
nsAutoString heightStr;
heightStr.AppendInt(height);
aValue.Append(heightStr);
aValue.Append(NS_LITERAL_STRING("px"));
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::SetHeight(const nsAReadableString& aValue)
{
return nsGenericHTMLLeafElement::SetAttribute(kNameSpaceID_None,
nsHTMLAtoms::height,
aValue, PR_TRUE);
}
NS_IMETHODIMP
nsHTMLImageElement::GetHeight(PRInt32* aHeight)
{
NS_ENSURE_ARG_POINTER(aHeight);
*aHeight = 0;
nsImageFrame* imageFrame;
nsresult rv = GetImageFrame(&imageFrame);
if (NS_SUCCEEDED(rv) && imageFrame) {
nsSize size;
imageFrame->GetSize(size);
nsCOMPtr<nsIPresContext> context;
rv = GetPresContext(this, getter_AddRefs(context));
if (NS_SUCCEEDED(rv) && context) {
float t2p;
context->GetTwipsToPixels(&t2p);
*aHeight = NSTwipsToIntPixels(size.height, t2p);
}
} else {
nsHTMLValue value;
rv = GetHTMLAttribute(nsHTMLAtoms::height, value);
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
*aHeight = value.GetPixelValue();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::SetHeight(PRInt32 aHeight)
{
nsAutoString val;
val.AppendInt(aHeight);
return nsGenericHTMLLeafElement::SetAttribute(kNameSpaceID_None,
nsHTMLAtoms::height,
val, PR_TRUE);
}
NS_IMETHODIMP
nsHTMLImageElement::GetWidth(nsAWritableString& aValue)
{
nsresult rv = nsGenericHTMLLeafElement::GetAttribute(kNameSpaceID_None,
nsHTMLAtoms::width,
aValue);
if (rv == NS_CONTENT_ATTR_NOT_THERE) {
PRInt32 width = 0;
aValue.Truncate();
// A zero width most likely means that the image is not loaded yet.
if (NS_SUCCEEDED(GetWidth(&width)) && width) {
nsAutoString widthStr;
widthStr.AppendInt(width);
aValue.Append(widthStr);
aValue.Append(NS_LITERAL_STRING("px"));
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::SetWidth(const nsAReadableString& aValue)
{
return nsGenericHTMLLeafElement::SetAttribute(kNameSpaceID_None,
nsHTMLAtoms::width, aValue,
PR_TRUE);
}
NS_IMETHODIMP
nsHTMLImageElement::GetWidth(PRInt32* aWidth)
{
NS_ENSURE_ARG_POINTER(aWidth);
*aWidth = 0;
nsImageFrame* imageFrame;
nsresult rv = GetImageFrame(&imageFrame);
if (NS_SUCCEEDED(rv) && imageFrame) {
nsSize size;
imageFrame->GetSize(size);
nsCOMPtr<nsIPresContext> context;
rv = GetPresContext(this, getter_AddRefs(context));
if (NS_SUCCEEDED(rv) && context) {
float t2p;
context->GetTwipsToPixels(&t2p);
*aWidth = NSTwipsToIntPixels(size.width, t2p);
}
} else {
nsHTMLValue value;
rv = GetHTMLAttribute(nsHTMLAtoms::width, value);
if (rv == NS_CONTENT_ATTR_HAS_VALUE) {
*aWidth = value.GetPixelValue();
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::SetWidth(PRInt32 aWidth)
{
nsAutoString val;
val.AppendInt(aWidth);
return nsGenericHTMLLeafElement::SetAttribute(kNameSpaceID_None,
nsHTMLAtoms::width,
val, PR_TRUE);
}
NS_IMETHODIMP
nsHTMLImageElement::StringToAttribute(nsIAtom* aAttribute,
const nsAReadableString& aValue,
nsHTMLValue& aResult)
{
if (aAttribute == nsHTMLAtoms::align) {
if (ParseAlignValue(aValue, aResult)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::ismap) {
aResult.SetEmptyValue();
return NS_CONTENT_ATTR_HAS_VALUE;
}
else if (ParseImageAttribute(aAttribute, aValue, aResult)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
return NS_CONTENT_ATTR_NOT_THERE;
}
NS_IMETHODIMP
nsHTMLImageElement::AttributeToString(nsIAtom* aAttribute,
const nsHTMLValue& aValue,
nsAWritableString& aResult) const
{
if (aAttribute == nsHTMLAtoms::align) {
if (eHTMLUnit_Enumerated == aValue.GetUnit()) {
AlignValueToString(aValue, aResult);
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (ImageAttributeToString(aAttribute, aValue, aResult)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
return nsGenericHTMLLeafElement::AttributeToString(aAttribute, aValue,
aResult);
}
static void
MapAttributesInto(const nsIHTMLMappedAttributes* aAttributes,
nsIMutableStyleContext* aContext,
nsIPresContext* aPresContext)
{
if (nsnull != aAttributes) {
nsHTMLValue value;
aAttributes->GetAttribute(nsHTMLAtoms::align, value);
if (value.GetUnit() == eHTMLUnit_Enumerated) {
PRUint8 align = value.GetIntValue();
nsStyleDisplay* display = (nsStyleDisplay*)
aContext->GetMutableStyleData(eStyleStruct_Display);
nsStyleText* text = (nsStyleText*)
aContext->GetMutableStyleData(eStyleStruct_Text);
switch (align) {
case NS_STYLE_TEXT_ALIGN_LEFT:
display->mFloats = NS_STYLE_FLOAT_LEFT;
break;
case NS_STYLE_TEXT_ALIGN_RIGHT:
display->mFloats = NS_STYLE_FLOAT_RIGHT;
break;
default:
text->mVerticalAlign.SetIntValue(align, eStyleUnit_Enumerated);
break;
}
}
}
nsGenericHTMLElement::MapImageAttributesInto(aAttributes, aContext,
aPresContext);
nsGenericHTMLElement::MapImageBorderAttributeInto(aAttributes, aContext,
aPresContext, nsnull);
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aContext,
aPresContext);
}
NS_IMETHODIMP
nsHTMLImageElement::GetMappedAttributeImpact(const nsIAtom* aAttribute,
PRInt32& aHint) const
{
if ((aAttribute == nsHTMLAtoms::usemap) ||
(aAttribute == nsHTMLAtoms::ismap) ||
(aAttribute == nsHTMLAtoms::align)) {
aHint = NS_STYLE_HINT_FRAMECHANGE;
}
else if (!GetCommonMappedAttributesImpact(aAttribute, aHint)) {
if (!GetImageMappedAttributesImpact(aAttribute, aHint)) {
if (!GetImageBorderAttributeImpact(aAttribute, aHint)) {
aHint = NS_STYLE_HINT_CONTENT;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::GetAttributeMappingFunctions(nsMapAttributesFunc& aFontMapFunc,
nsMapAttributesFunc& aMapFunc) const
{
aFontMapFunc = nsnull;
aMapFunc = &MapAttributesInto;
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::HandleDOMEvent(nsIPresContext* aPresContext,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent,
PRUint32 aFlags,
nsEventStatus* aEventStatus)
{
// If we are a map and get a mouse click, don't let it be handled by
// the Generic Element as this could cause a click event to fire
// twice, once by the image frame for the map and once by the Anchor
// element. (bug 39723)
if (NS_MOUSE_LEFT_CLICK == aEvent->message) {
PRBool isMap = PR_FALSE;
GetIsMap(&isMap);
if (isMap) {
*aEventStatus = nsEventStatus_eConsumeNoDefault;
}
}
return nsGenericHTMLLeafElement::HandleDOMEvent(aPresContext, aEvent,
aDOMEvent, aFlags,
aEventStatus);
}
PRBool
nsHTMLImageElement::GetProperty(JSContext *aContext, JSObject *aObj,
jsval aID, jsval *aVp)
{
// XXX Security manager needs to be called
if (JSVAL_IS_STRING(aID)) {
char* cString = JS_GetStringBytes(JS_ValueToString(aContext, aID));
if (PL_strcmp("src", cString) == 0) {
nsAutoString src;
if (NS_SUCCEEDED(GetSrc(src))) {
const PRUnichar* bytes = src.GetUnicode();
JSString* str = JS_NewUCStringCopyZ(aContext, (const jschar*)bytes);
if (str) {
*aVp = STRING_TO_JSVAL(str);
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
else {
return PR_FALSE;
}
}
}
return nsGenericHTMLLeafElement::GetProperty(aContext, aObj, aID, aVp);
}
nsresult
nsHTMLImageElement::GetCallerSourceURL(JSContext* cx,
nsIURI** sourceURL)
{
// XXX Code duplicated from nsHTMLDocument
// XXX Question, why does this return NS_OK on failure?
nsresult result = NS_OK;
// We need to use the dynamically scoped global and assume that the
// current JSContext is a DOM context with a nsIScriptGlobalObject so
// that we can get the url of the caller.
// XXX This will fail on non-DOM contexts :(
nsCOMPtr<nsIScriptGlobalObject> global;
nsLayoutUtils::GetDynamicScriptGlobal(cx, getter_AddRefs(global));
if (global) {
nsCOMPtr<nsIDOMWindowInternal> window = do_QueryInterface(global);
if (window) {
nsCOMPtr<nsIDOMDocument> domDoc;
result = window->GetDocument(getter_AddRefs(domDoc));
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIDocument> doc = do_QueryInterface(domDoc);
if (doc) {
result = doc->GetBaseURL(*sourceURL);
if (!*sourceURL) {
*sourceURL = doc->GetDocumentURL();
}
}
}
}
}
return result;
}
PRBool
nsHTMLImageElement::SetProperty(JSContext *aContext, JSObject *aObj,
jsval aID, jsval *aVp)
{
nsresult result = NS_OK;
// XXX Security manager needs to be called
if (JSVAL_IS_STRING(aID)) {
char* cString = JS_GetStringBytes(JS_ValueToString(aContext, aID));
if (PL_strcmp("src", cString) == 0) {
nsCOMPtr<nsIURI> base;
nsAutoString src, url;
// Get the parameter passed in
JSString *jsstring;
if ((jsstring = JS_ValueToString(aContext, *aVp)) != nsnull) {
src.Assign(NS_REINTERPRET_CAST(const PRUnichar*,
JS_GetStringChars(jsstring)));
src.Trim(" \t\n\r");
}
else {
src.Truncate();
}
// Get the source of the caller
result = GetCallerSourceURL(aContext, getter_AddRefs(base));
if (NS_SUCCEEDED(result)) {
result = NS_MakeAbsoluteURI(url, src, base);
if (NS_SUCCEEDED(result)) {
result = SetSrcInner(base, url);
}
}
}
else {
result = nsGenericHTMLLeafElement::SetProperty(aContext, aObj, aID, aVp);
}
}
else {
result = nsGenericHTMLLeafElement::SetProperty(aContext, aObj, aID, aVp);
}
if (NS_FAILED(result)) {
return PR_FALSE;
}
return PR_TRUE;
}
PRBool
nsHTMLImageElement::Resolve(JSContext *aContext, JSObject *aObj, jsval aID,
PRBool *aDidDefineProperty)
{
if (JSVAL_IS_STRING(aID) && mDOMSlots) {
JSString *str;
str = JSVAL_TO_STRING(aID);
const jschar *chars = ::JS_GetStringChars(str);
const PRUnichar *unichars = NS_REINTERPRET_CAST(const PRUnichar*, chars);
if (!nsCRT::strcmp(unichars, NS_LITERAL_STRING("src").get())) {
// Someone is trying to resolve "src" so we deifine it on the
// object with a JSVAL_VOID value, the real value will be returned
// when the caller calls GetProperty().
::JS_DefineUCProperty(aContext,
(JSObject *)mDOMSlots->mScriptObject,
chars, ::JS_GetStringLength(str),
JSVAL_VOID, nsnull, nsnull, 0);
*aDidDefineProperty = PR_TRUE;
return PR_TRUE;
}
}
return nsGenericHTMLLeafElement::Resolve(aContext, aObj, aID,
aDidDefineProperty);
}
NS_IMETHODIMP
nsHTMLImageElement::Initialize(JSContext* aContext,
JSObject *aObj,
PRUint32 argc,
jsval *argv)
{
nsresult result = NS_OK;
// XXX This element is created unattached to any document. Later
// on, it might be used to preload the image cache. For that, we
// need a document (actually a pres context). The only way to get
// one is to associate the image with one at creation time.
// This is safer than it used to be since we get the global object
// from the static scope chain rather than through the JSCOntext.
nsCOMPtr<nsIScriptGlobalObject> globalObject;
nsLayoutUtils::GetStaticScriptGlobal(aContext, aObj,
getter_AddRefs(globalObject));;
if (globalObject) {
nsCOMPtr<nsIDOMWindowInternal> domWindow(do_QueryInterface(globalObject));
if (domWindow) {
nsCOMPtr<nsIDOMDocument> domDocument;
result = domWindow->GetDocument(getter_AddRefs(domDocument));
if (NS_SUCCEEDED(result)) {
// Maintain the reference
result = domDocument->QueryInterface(NS_GET_IID(nsIDocument),
(void**)&mOwnerDocument);
}
}
}
if (NS_SUCCEEDED(result) && (argc > 0)) {
// The first (optional) argument is the width of the image
int32 width;
JSBool ret = JS_ValueToInt32(aContext, argv[0], &width);
if (ret) {
nsHTMLValue widthVal((PRInt32)width, eHTMLUnit_Integer);
result = SetHTMLAttribute(nsHTMLAtoms::width, widthVal, PR_FALSE);
if (NS_SUCCEEDED(result) && (argc > 1)) {
// The second (optional) argument is the height of the image
int32 height;
ret = JS_ValueToInt32(aContext, argv[1], &height);
if (ret) {
nsHTMLValue heightVal((PRInt32)height, eHTMLUnit_Integer);
result = SetHTMLAttribute(nsHTMLAtoms::height, heightVal, PR_FALSE);
}
else {
result = NS_ERROR_INVALID_ARG;
}
}
}
else {
result = NS_ERROR_INVALID_ARG;
}
}
return result;
}
NS_IMETHODIMP
nsHTMLImageElement::SetDocument(nsIDocument* aDocument,
PRBool aDeep, PRBool aCompileEventHandlers)
{
// If we've been added to the document, we can get rid of
// our owner document reference so as to avoid a circular
// reference.
NS_IF_RELEASE(mOwnerDocument);
return nsGenericHTMLLeafElement::SetDocument(aDocument, aDeep,
aCompileEventHandlers);
}
NS_IMETHODIMP
nsHTMLImageElement::GetSrc(nsAWritableString& aSrc)
{
// Resolve url to an absolute url
nsresult rv = NS_OK;
nsAutoString relURLSpec;
nsCOMPtr<nsIURI> baseURL;
// Get base URL.
GetBaseURL(*getter_AddRefs(baseURL));
// Get href= attribute (relative URL).
nsGenericHTMLLeafElement::GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::src,
relURLSpec);
relURLSpec.Trim(" \t\n\r");
if (baseURL && relURLSpec.Length() > 0) {
// Get absolute URL.
rv = NS_MakeAbsoluteURI(aSrc, relURLSpec, baseURL);
}
else {
// Absolute URL is same as relative URL.
aSrc = relURLSpec;
}
return rv;
}
nsresult nsHTMLImageElement::ImageLibCallBack(nsIPresContext* aPresContext,
nsIFrameImageLoader* aLoader,
nsIFrame* aFrame,
void* aClosure,
PRUint32 aStatus)
{
nsHTMLImageElement *img = (nsHTMLImageElement *)aClosure;
if (!img || !img->mLoader)
return NS_OK;
if ((aStatus & NS_IMAGE_LOAD_STATUS_SIZE_AVAILABLE) &&
!(aStatus & NS_IMAGE_LOAD_STATUS_ERROR)) {
nsCOMPtr<nsIPresContext> cx;
aLoader->GetPresContext(getter_AddRefs(cx));
float t2p;
cx->GetTwipsToPixels(&t2p);
nsSize size;
aLoader->GetSize(size);
nsAutoString tmpStr;
tmpStr.AppendInt(NSTwipsToIntPixels(size.width, t2p));
NS_STATIC_CAST(nsIContent *, img)->SetAttribute(kNameSpaceID_None,
nsHTMLAtoms::width,
tmpStr, PR_FALSE);
tmpStr.Truncate();
tmpStr.AppendInt(NSTwipsToIntPixels(size.height, t2p));
NS_STATIC_CAST(nsIContent *, img)->SetAttribute(kNameSpaceID_None,
nsHTMLAtoms::height,
tmpStr, PR_FALSE);
}
if (aStatus & (NS_IMAGE_LOAD_STATUS_IMAGE_READY |
NS_IMAGE_LOAD_STATUS_ERROR)) {
// We set mLoader = nsnull to indicate that we're complete.
img->mLoader->RemoveFrame(img);
img->mLoader = nsnull;
// Fire the onload event.
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event;
event.eventStructType = NS_EVENT;
if (aStatus & NS_IMAGE_LOAD_STATUS_IMAGE_READY) {
event.message = NS_IMAGE_LOAD;
} else {
event.message = NS_IMAGE_ERROR;
}
img->HandleDOMEvent(aPresContext, &event, nsnull, NS_EVENT_FLAG_INIT,
&status);
}
return NS_OK;
}
nsresult
nsHTMLImageElement::SetSrcInner(nsIURI* aBaseURL,
const nsAReadableString& aSrc)
{
nsresult result = NS_OK;
result = nsGenericHTMLLeafElement::SetAttribute(kNameSpaceID_HTML,
nsHTMLAtoms::src, aSrc,
PR_TRUE);
if (NS_SUCCEEDED(result) && mOwnerDocument) {
nsCOMPtr<nsIPresShell> shell;
shell = dont_AddRef(mOwnerDocument->GetShellAt(0));
if (shell) {
nsCOMPtr<nsIPresContext> context;
result = shell->GetPresContext(getter_AddRefs(context));
if (context) {
nsSize size;
nsHTMLValue val;
float p2t;
context->GetScaledPixelsToTwips(&p2t);
result = GetHTMLAttribute(nsHTMLAtoms::width, val);
if (NS_CONTENT_ATTR_HAS_VALUE == result) {
size.width = NSIntPixelsToTwips(val.GetIntValue(), p2t);
}
else {
size.width = 0;
}
result = GetHTMLAttribute(nsHTMLAtoms::height, val);
if (NS_CONTENT_ATTR_HAS_VALUE == result) {
size.height = NSIntPixelsToTwips(val.GetIntValue(), p2t);
}
else {
size.height = 0;
}
nsAutoString url;
if (aBaseURL) {
result = NS_MakeAbsoluteURI(url, aSrc, aBaseURL);
if (NS_FAILED(result)) {
url.Assign(aSrc);
}
}
else {
url.Assign(aSrc);
}
nsSize* specifiedSize = nsnull;
if ((size.width > 0) || (size.height > 0)) {
specifiedSize = &size;
}
// If we have a loader we're in the middle of loading a image,
// we'll cancel that load and start a new one.
if (mLoader) {
mLoader->RemoveFrame(this);
}
// Start the image loading.
result = context->StartLoadImage(url, nsnull, specifiedSize,
nsnull, ImageLibCallBack, this,
this, getter_AddRefs(mLoader));
}
}
// Only do this the first time since it's only there for
// backwards compatability
NS_RELEASE(mOwnerDocument);
}
return result;
}
NS_IMETHODIMP
nsHTMLImageElement::SetSrc(const nsAReadableString& aSrc)
{
nsCOMPtr<nsIURI> baseURL;
nsresult result = NS_OK;
if (mOwnerDocument) {
result = mOwnerDocument->GetBaseURL(*getter_AddRefs(baseURL));
}
if (NS_SUCCEEDED(result)) {
result = SetSrcInner(baseURL, aSrc);
}
return result;
}
NS_IMETHODIMP
nsHTMLImageElement::SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const
{
*aResult = sizeof(*this) + BaseSizeOf(aSizer);
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::GetNaturalHeight(PRInt32* aNaturalHeight)
{
NS_ENSURE_ARG_POINTER(aNaturalHeight);
*aNaturalHeight = 0;
nsImageFrame* imageFrame;
nsresult rv = GetImageFrame(&imageFrame);
if (NS_FAILED(rv) || !imageFrame)
return NS_OK; // don't throw JS exceptions in this case
PRUint32 width, height;
rv = imageFrame->GetNaturalImageSize(&width, &height);
if (NS_FAILED(rv))
return NS_OK;
*aNaturalHeight = (PRInt32)height;
return NS_OK;
}
NS_IMETHODIMP
nsHTMLImageElement::GetNaturalWidth(PRInt32* aNaturalWidth)
{
NS_ENSURE_ARG_POINTER(aNaturalWidth);
*aNaturalWidth = 0;
nsImageFrame* imageFrame;
nsresult rv = GetImageFrame(&imageFrame);
if (NS_FAILED(rv) || !imageFrame)
return NS_OK; // don't throw JS exceptions in this case
PRUint32 width, height;
rv = imageFrame->GetNaturalImageSize(&width, &height);
if (NS_FAILED(rv))
return NS_OK;
*aNaturalWidth = (PRInt32)width;
return NS_OK;
}