1/*
2 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. (http://www.torchmobile.com/)
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22
23#if ENABLE(WML)
24#include "WMLCardElement.h"
25
26#include "Frame.h"
27#include "FrameLoader.h"
28#include "HTMLNames.h"
29#include "MappedAttribute.h"
30#include "NodeList.h"
31#include "Page.h"
32#include "RenderStyle.h"
33#include "WMLDocument.h"
34#include "WMLDoElement.h"
35#include "WMLInputElement.h"
36#include "WMLIntrinsicEventHandler.h"
37#include "WMLNames.h"
38#include "WMLSelectElement.h"
39#include "WMLTemplateElement.h"
40#include "WMLTimerElement.h"
41#include "WMLVariables.h"
42
43namespace WebCore {
44
45using namespace WMLNames;
46
47WMLCardElement::WMLCardElement(const QualifiedName& tagName, Document* doc)
48    : WMLElement(tagName, doc)
49    , m_isNewContext(false)
50    , m_isOrdered(false)
51    , m_isVisible(false)
52    , m_eventTimer(0)
53    , m_template(0)
54{
55    ASSERT(hasTagName(cardTag));
56}
57
58WMLCardElement::~WMLCardElement()
59{
60}
61
62void WMLCardElement::showCard()
63{
64    ASSERT(attached());
65
66    if (m_isVisible) {
67        ASSERT(renderer());
68        return;
69    }
70
71    m_isVisible = true;
72    ASSERT(!renderer());
73
74    detach();
75    attach();
76
77    ASSERT(attached());
78    ASSERT(renderer());
79}
80
81void WMLCardElement::hideCard()
82{
83    ASSERT(attached());
84
85    if (!m_isVisible) {
86        ASSERT(!renderer());
87        return;
88    }
89
90    m_isVisible = false;
91    ASSERT(renderer());
92
93    detach();
94    attach();
95
96    ASSERT(attached());
97    ASSERT(!renderer());
98}
99
100void WMLCardElement::setTemplateElement(WMLTemplateElement* temp)
101{
102    // Only one template is allowed to be attached to a card
103    if (m_template) {
104        reportWMLError(document(), WMLErrorMultipleTemplateElements);
105        return;
106    }
107
108    m_template = temp;
109}
110
111void WMLCardElement::setIntrinsicEventTimer(WMLTimerElement* timer)
112{
113    // Only one timer is allowed in a card
114    if (m_eventTimer) {
115        reportWMLError(document(), WMLErrorMultipleTimerElements);
116        return;
117    }
118
119    m_eventTimer = timer;
120}
121
122void WMLCardElement::handleIntrinsicEventIfNeeded()
123{
124    WMLPageState* pageState = wmlPageStateForDocument(document());
125    if (!pageState)
126        return;
127
128    Frame* frame = document()->frame();
129    if (!frame)
130        return;
131
132    FrameLoader* loader = frame->loader();
133    if (!loader)
134        return;
135
136    // Calculate the entry method of current card
137    WMLIntrinsicEventType eventType = WMLIntrinsicEventUnknown;
138
139    switch (loader->policyChecker()->loadType()) {
140    case FrameLoadTypeReload:
141        break;
142    case FrameLoadTypeBack:
143        eventType = WMLIntrinsicEventOnEnterBackward;
144        break;
145    case FrameLoadTypeBackWMLDeckNotAccessible:
146        reportWMLError(document(), WMLErrorDeckNotAccessible);
147        return;
148    default:
149        eventType = WMLIntrinsicEventOnEnterForward;
150        break;
151    }
152
153    // Figure out target event handler
154    WMLIntrinsicEventHandler* eventHandler = this->eventHandler();
155    bool hasIntrinsicEvent = false;
156
157    if (eventType != WMLIntrinsicEventUnknown) {
158        if (eventHandler && eventHandler->hasIntrinsicEvent(eventType))
159            hasIntrinsicEvent = true;
160        else if (m_template) {
161            eventHandler = m_template->eventHandler();
162            if (eventHandler && eventHandler->hasIntrinsicEvent(eventType))
163                hasIntrinsicEvent = true;
164        }
165    }
166
167    if (hasIntrinsicEvent)
168        eventHandler->triggerIntrinsicEvent(eventType);
169
170    // Start the timer if it exists in current card
171    if (m_eventTimer)
172        m_eventTimer->start();
173
174    for (Node* node = traverseNextNode(); node != 0; node = node->traverseNextNode()) {
175        if (!node->isElementNode())
176            continue;
177
178        if (node->hasTagName(inputTag))
179            static_cast<WMLInputElement*>(node)->initialize();
180        else if (node->hasTagName(selectTag))
181            static_cast<WMLSelectElement*>(node)->selectInitialOptions();
182    }
183}
184
185void WMLCardElement::handleDeckLevelTaskOverridesIfNeeded()
186{
187    // Spec: The event-handling element may appear inside a template element and specify
188    // event-processing behaviour for all cards in the deck. A deck-level event-handling
189    // element is equivalent to specifying the event-handling element in each card.
190    if (!m_template)
191        return;
192
193    Vector<WMLDoElement*>& templateDoElements = m_template->doElements();
194    if (templateDoElements.isEmpty())
195        return;
196
197    Vector<WMLDoElement*>& cardDoElements = doElements();
198    Vector<WMLDoElement*>::iterator it = cardDoElements.begin();
199    Vector<WMLDoElement*>::iterator end = cardDoElements.end();
200
201    HashSet<String> cardDoElementNames;
202    for (; it != end; ++it)
203        cardDoElementNames.add((*it)->name());
204
205    it = templateDoElements.begin();
206    end = templateDoElements.end();
207
208    for (; it != end; ++it)
209        (*it)->setActive(!cardDoElementNames.contains((*it)->name()));
210}
211
212void WMLCardElement::parseMappedAttribute(MappedAttribute* attr)
213{
214    WMLIntrinsicEventType eventType = WMLIntrinsicEventUnknown;
215
216    if (attr->name() == onenterforwardAttr)
217        eventType = WMLIntrinsicEventOnEnterForward;
218    else if (attr->name() == onenterbackwardAttr)
219        eventType = WMLIntrinsicEventOnEnterBackward;
220    else if (attr->name() == ontimerAttr)
221        eventType = WMLIntrinsicEventOnTimer;
222    else if (attr->name() == newcontextAttr)
223        m_isNewContext = (attr->value() == "true");
224    else if (attr->name() == orderedAttr)
225        m_isOrdered = (attr->value() == "true");
226    else {
227        WMLElement::parseMappedAttribute(attr);
228        return;
229    }
230
231    if (eventType == WMLIntrinsicEventUnknown)
232        return;
233
234    // Register intrinsic event in card
235    RefPtr<WMLIntrinsicEvent> event = WMLIntrinsicEvent::create(document(), attr->value());
236
237    createEventHandlerIfNeeded();
238    eventHandler()->registerIntrinsicEvent(eventType, event);
239}
240
241void WMLCardElement::insertedIntoDocument()
242{
243    WMLElement::insertedIntoDocument();
244    Document* document = this->document();
245
246    // The first card inserted into a document, is visible by default.
247    if (!m_isVisible) {
248        RefPtr<NodeList> nodeList = document->getElementsByTagName("card");
249        if (nodeList && nodeList->length() == 1 && nodeList->item(0) == this)
250            m_isVisible = true;
251    }
252
253    // For the WML layout tests we embed WML content in a XHTML document. Navigating to different cards
254    // within the same deck has a different behaviour in HTML than in WML. HTML wants to "scroll to anchor"
255    // (see FrameLoader) but WML wants a reload. Notify the root document of the layout test that we want
256    // to mimic WML behaviour. This is rather tricky, but has been tested extensively. Usually it's not possible
257    // at all to embed WML in HTML, it's not designed that way, we're just "abusing" it for dynamically created layout tests.
258    if (document->page() && document->page()->mainFrame()) {
259        Document* rootDocument = document->page()->mainFrame()->document();
260        if (rootDocument && rootDocument != document)
261            rootDocument->setContainsWMLContent(true);
262    }
263}
264
265RenderObject* WMLCardElement::createRenderer(RenderArena* arena, RenderStyle* style)
266{
267    if (!m_isVisible) {
268        style->setUnique();
269        style->setDisplay(NONE);
270    }
271
272    return WMLElement::createRenderer(arena, style);
273}
274
275WMLCardElement* WMLCardElement::findNamedCardInDocument(Document* doc, const String& cardName)
276{
277    if (cardName.isEmpty())
278        return 0;
279
280    RefPtr<NodeList> nodeList = doc->getElementsByTagName("card");
281    if (!nodeList)
282        return 0;
283
284    unsigned length = nodeList->length();
285    if (length < 1)
286        return 0;
287
288    for (unsigned i = 0; i < length; ++i) {
289        WMLCardElement* card = static_cast<WMLCardElement*>(nodeList->item(i));
290        if (card->getIDAttribute() != cardName)
291            continue;
292
293        return card;
294    }
295
296    return 0;
297}
298
299WMLCardElement* WMLCardElement::determineActiveCard(Document* doc)
300{
301    WMLPageState* pageState = wmlPageStateForDocument(doc);
302    if (!pageState)
303        return 0;
304
305    RefPtr<NodeList> nodeList = doc->getElementsByTagName("card");
306    if (!nodeList)
307        return 0;
308
309    unsigned length = nodeList->length();
310    if (length < 1)
311        return 0;
312
313    // Figure out the new target card
314    String cardName = doc->url().fragmentIdentifier();
315
316    WMLCardElement* activeCard = findNamedCardInDocument(doc, cardName);
317    if (activeCard) {
318        // Hide all cards - except the destination card - in document
319        for (unsigned i = 0; i < length; ++i) {
320            WMLCardElement* card = static_cast<WMLCardElement*>(nodeList->item(i));
321
322            if (card == activeCard)
323                card->showCard();
324            else
325                card->hideCard();
326        }
327    } else {
328        // If the target URL didn't contain a fragment identifier, activeCard
329        // is 0, and has to be set to the first card element in the deck.
330        activeCard = static_cast<WMLCardElement*>(nodeList->item(0));
331        activeCard->showCard();
332    }
333
334    // Assure destination card is visible
335    ASSERT(activeCard->isVisible());
336    ASSERT(activeCard->attached());
337    ASSERT(activeCard->renderer());
338
339    // Update the document title
340    doc->setTitle(activeCard->title());
341
342    return activeCard;
343}
344
345}
346
347#endif
348