blob: 63240f348325eeaeb788ef2ecf1babb5ed9acd0d [file] [log] [blame]
/*
* Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "config.h"
#if ENABLE(WML)
#include "WMLCardElement.h"
#include "Frame.h"
#include "FrameLoader.h"
#include "HTMLNames.h"
#include "MappedAttribute.h"
#include "NodeList.h"
#include "Page.h"
#include "RenderStyle.h"
#include "WMLDocument.h"
#include "WMLDoElement.h"
#include "WMLInputElement.h"
#include "WMLIntrinsicEventHandler.h"
#include "WMLNames.h"
#include "WMLSelectElement.h"
#include "WMLTemplateElement.h"
#include "WMLTimerElement.h"
#include "WMLVariables.h"
namespace WebCore {
using namespace WMLNames;
WMLCardElement::WMLCardElement(const QualifiedName& tagName, Document* doc)
: WMLElement(tagName, doc)
, m_isNewContext(false)
, m_isOrdered(false)
, m_isVisible(false)
, m_eventTimer(0)
, m_template(0)
{
ASSERT(hasTagName(cardTag));
}
WMLCardElement::~WMLCardElement()
{
}
void WMLCardElement::showCard()
{
ASSERT(attached());
if (m_isVisible) {
ASSERT(renderer());
return;
}
m_isVisible = true;
ASSERT(!renderer());
detach();
attach();
ASSERT(attached());
ASSERT(renderer());
}
void WMLCardElement::hideCard()
{
ASSERT(attached());
if (!m_isVisible) {
ASSERT(!renderer());
return;
}
m_isVisible = false;
ASSERT(renderer());
detach();
attach();
ASSERT(attached());
ASSERT(!renderer());
}
void WMLCardElement::setTemplateElement(WMLTemplateElement* temp)
{
// Only one template is allowed to be attached to a card
if (m_template) {
reportWMLError(document(), WMLErrorMultipleTemplateElements);
return;
}
m_template = temp;
}
void WMLCardElement::setIntrinsicEventTimer(WMLTimerElement* timer)
{
// Only one timer is allowed in a card
if (m_eventTimer) {
reportWMLError(document(), WMLErrorMultipleTimerElements);
return;
}
m_eventTimer = timer;
}
void WMLCardElement::handleIntrinsicEventIfNeeded()
{
WMLPageState* pageState = wmlPageStateForDocument(document());
if (!pageState)
return;
Frame* frame = document()->frame();
if (!frame)
return;
FrameLoader* loader = frame->loader();
if (!loader)
return;
// Calculate the entry method of current card
WMLIntrinsicEventType eventType = WMLIntrinsicEventUnknown;
switch (loader->loadType()) {
case FrameLoadTypeReload:
break;
case FrameLoadTypeBack:
eventType = WMLIntrinsicEventOnEnterBackward;
break;
case FrameLoadTypeBackWMLDeckNotAccessible:
reportWMLError(document(), WMLErrorDeckNotAccessible);
return;
default:
eventType = WMLIntrinsicEventOnEnterForward;
break;
}
// Figure out target event handler
WMLIntrinsicEventHandler* eventHandler = this->eventHandler();
bool hasIntrinsicEvent = false;
if (eventType != WMLIntrinsicEventUnknown) {
if (eventHandler && eventHandler->hasIntrinsicEvent(eventType))
hasIntrinsicEvent = true;
else if (m_template) {
eventHandler = m_template->eventHandler();
if (eventHandler && eventHandler->hasIntrinsicEvent(eventType))
hasIntrinsicEvent = true;
}
}
if (hasIntrinsicEvent)
eventHandler->triggerIntrinsicEvent(eventType);
// Start the timer if it exists in current card
if (m_eventTimer)
m_eventTimer->start();
for (Node* node = traverseNextNode(); node != 0; node = node->traverseNextNode()) {
if (!node->isElementNode())
continue;
if (node->hasTagName(inputTag))
static_cast<WMLInputElement*>(node)->initialize();
else if (node->hasTagName(selectTag))
static_cast<WMLSelectElement*>(node)->selectInitialOptions();
}
}
void WMLCardElement::handleDeckLevelTaskOverridesIfNeeded()
{
// Spec: The event-handling element may appear inside a template element and specify
// event-processing behaviour for all cards in the deck. A deck-level event-handling
// element is equivalent to specifying the event-handling element in each card.
if (!m_template)
return;
Vector<WMLDoElement*>& templateDoElements = m_template->doElements();
if (templateDoElements.isEmpty())
return;
Vector<WMLDoElement*>& cardDoElements = doElements();
Vector<WMLDoElement*>::iterator it = cardDoElements.begin();
Vector<WMLDoElement*>::iterator end = cardDoElements.end();
HashSet<String> cardDoElementNames;
for (; it != end; ++it)
cardDoElementNames.add((*it)->name());
it = templateDoElements.begin();
end = templateDoElements.end();
for (; it != end; ++it)
(*it)->setActive(!cardDoElementNames.contains((*it)->name()));
}
void WMLCardElement::parseMappedAttribute(MappedAttribute* attr)
{
WMLIntrinsicEventType eventType = WMLIntrinsicEventUnknown;
if (attr->name() == onenterforwardAttr)
eventType = WMLIntrinsicEventOnEnterForward;
else if (attr->name() == onenterbackwardAttr)
eventType = WMLIntrinsicEventOnEnterBackward;
else if (attr->name() == ontimerAttr)
eventType = WMLIntrinsicEventOnTimer;
else if (attr->name() == newcontextAttr)
m_isNewContext = (attr->value() == "true");
else if (attr->name() == orderedAttr)
m_isOrdered = (attr->value() == "true");
else {
WMLElement::parseMappedAttribute(attr);
return;
}
if (eventType == WMLIntrinsicEventUnknown)
return;
// Register intrinsic event in card
RefPtr<WMLIntrinsicEvent> event = WMLIntrinsicEvent::create(document(), attr->value());
createEventHandlerIfNeeded();
eventHandler()->registerIntrinsicEvent(eventType, event);
}
void WMLCardElement::insertedIntoDocument()
{
WMLElement::insertedIntoDocument();
Document* document = this->document();
// The first card inserted into a document, is visible by default.
if (!m_isVisible) {
RefPtr<NodeList> nodeList = document->getElementsByTagName("card");
if (nodeList && nodeList->length() == 1 && nodeList->item(0) == this)
m_isVisible = true;
}
// For the WML layout tests we embed WML content in a XHTML document. Navigating to different cards
// within the same deck has a different behaviour in HTML than in WML. HTML wants to "scroll to anchor"
// (see FrameLoader) but WML wants a reload. Notify the root document of the layout test that we want
// to mimic WML behaviour. This is rather tricky, but has been tested extensively. Usually it's not possible
// at all to embed WML in HTML, it's not designed that way, we're just "abusing" it for dynamically created layout tests.
if (document->page() && document->page()->mainFrame()) {
Document* rootDocument = document->page()->mainFrame()->document();
if (rootDocument && rootDocument != document)
rootDocument->setContainsWMLContent(true);
}
}
RenderObject* WMLCardElement::createRenderer(RenderArena* arena, RenderStyle* style)
{
if (!m_isVisible) {
style->setUnique();
style->setDisplay(NONE);
}
return WMLElement::createRenderer(arena, style);
}
WMLCardElement* WMLCardElement::findNamedCardInDocument(Document* doc, const String& cardName)
{
if (cardName.isEmpty())
return 0;
RefPtr<NodeList> nodeList = doc->getElementsByTagName("card");
if (!nodeList)
return 0;
unsigned length = nodeList->length();
if (length < 1)
return 0;
for (unsigned i = 0; i < length; ++i) {
WMLCardElement* card = static_cast<WMLCardElement*>(nodeList->item(i));
if (card->getIDAttribute() != cardName)
continue;
return card;
}
return 0;
}
WMLCardElement* WMLCardElement::determineActiveCard(Document* doc)
{
WMLPageState* pageState = wmlPageStateForDocument(doc);
if (!pageState)
return 0;
RefPtr<NodeList> nodeList = doc->getElementsByTagName("card");
if (!nodeList)
return 0;
unsigned length = nodeList->length();
if (length < 1)
return 0;
// Figure out the new target card
String cardName = doc->url().fragmentIdentifier();
WMLCardElement* activeCard = findNamedCardInDocument(doc, cardName);
if (activeCard) {
// Hide all cards - except the destination card - in document
for (unsigned i = 0; i < length; ++i) {
WMLCardElement* card = static_cast<WMLCardElement*>(nodeList->item(i));
if (card == activeCard)
card->showCard();
else
card->hideCard();
}
} else {
// If the target URL didn't contain a fragment identifier, activeCard
// is 0, and has to be set to the first card element in the deck.
activeCard = static_cast<WMLCardElement*>(nodeList->item(0));
activeCard->showCard();
}
// Assure destination card is visible
ASSERT(activeCard->isVisible());
ASSERT(activeCard->attached());
ASSERT(activeCard->renderer());
// Update the document title
doc->setTitle(activeCard->title());
// Set the active activeCard in the WMLPageState object
pageState->setActiveCard(activeCard);
return activeCard;
}
}
#endif