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 "Attribute.h"
27#include "Frame.h"
28#include "FrameLoader.h"
29#include "HTMLNames.h"
30#include "NodeList.h"
31#include "Page.h"
32#include "RenderStyle.h"
33#include "WMLDoElement.h"
34#include "WMLDocument.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
58PassRefPtr<WMLCardElement> WMLCardElement::create(const QualifiedName& tagName, Document* document)
59{
60    return adoptRef(new WMLCardElement(tagName, document));
61}
62
63WMLCardElement::~WMLCardElement()
64{
65}
66
67void WMLCardElement::showCard()
68{
69    ASSERT(attached());
70
71    if (m_isVisible) {
72        ASSERT(renderer());
73        return;
74    }
75
76    m_isVisible = true;
77    ASSERT(!renderer());
78
79    detach();
80    attach();
81
82    ASSERT(attached());
83    ASSERT(renderer());
84}
85
86void WMLCardElement::hideCard()
87{
88    ASSERT(attached());
89
90    if (!m_isVisible) {
91        ASSERT(!renderer());
92        return;
93    }
94
95    m_isVisible = false;
96    ASSERT(renderer());
97
98    detach();
99    attach();
100
101    ASSERT(attached());
102    ASSERT(!renderer());
103}
104
105void WMLCardElement::setTemplateElement(WMLTemplateElement* temp)
106{
107    // Only one template is allowed to be attached to a card
108    if (m_template) {
109        reportWMLError(document(), WMLErrorMultipleTemplateElements);
110        return;
111    }
112
113    m_template = temp;
114}
115
116void WMLCardElement::setIntrinsicEventTimer(WMLTimerElement* timer)
117{
118    // Only one timer is allowed in a card
119    if (m_eventTimer) {
120        reportWMLError(document(), WMLErrorMultipleTimerElements);
121        return;
122    }
123
124    m_eventTimer = timer;
125}
126
127void WMLCardElement::handleIntrinsicEventIfNeeded()
128{
129    WMLPageState* pageState = wmlPageStateForDocument(document());
130    if (!pageState)
131        return;
132
133    Frame* frame = document()->frame();
134    if (!frame)
135        return;
136
137    FrameLoader* loader = frame->loader();
138    if (!loader)
139        return;
140
141    // Calculate the entry method of current card
142    WMLIntrinsicEventType eventType = WMLIntrinsicEventUnknown;
143
144    switch (loader->policyChecker()->loadType()) {
145    case FrameLoadTypeReload:
146        break;
147    case FrameLoadTypeBack:
148        eventType = WMLIntrinsicEventOnEnterBackward;
149        break;
150    case FrameLoadTypeBackWMLDeckNotAccessible:
151        reportWMLError(document(), WMLErrorDeckNotAccessible);
152        return;
153    default:
154        eventType = WMLIntrinsicEventOnEnterForward;
155        break;
156    }
157
158    // Figure out target event handler
159    WMLIntrinsicEventHandler* eventHandler = this->eventHandler();
160    bool hasIntrinsicEvent = false;
161
162    if (eventType != WMLIntrinsicEventUnknown) {
163        if (eventHandler && eventHandler->hasIntrinsicEvent(eventType))
164            hasIntrinsicEvent = true;
165        else if (m_template) {
166            eventHandler = m_template->eventHandler();
167            if (eventHandler && eventHandler->hasIntrinsicEvent(eventType))
168                hasIntrinsicEvent = true;
169        }
170    }
171
172    if (hasIntrinsicEvent)
173        eventHandler->triggerIntrinsicEvent(eventType);
174
175    // Start the timer if it exists in current card
176    if (m_eventTimer)
177        m_eventTimer->start();
178
179    for (Node* node = traverseNextNode(); node != 0; node = node->traverseNextNode()) {
180        if (!node->isElementNode())
181            continue;
182
183        if (node->hasTagName(inputTag))
184            static_cast<WMLInputElement*>(node)->initialize();
185        else if (node->hasTagName(selectTag))
186            static_cast<WMLSelectElement*>(node)->selectInitialOptions();
187    }
188}
189
190void WMLCardElement::handleDeckLevelTaskOverridesIfNeeded()
191{
192    // Spec: The event-handling element may appear inside a template element and specify
193    // event-processing behaviour for all cards in the deck. A deck-level event-handling
194    // element is equivalent to specifying the event-handling element in each card.
195    if (!m_template)
196        return;
197
198    Vector<WMLDoElement*>& templateDoElements = m_template->doElements();
199    if (templateDoElements.isEmpty())
200        return;
201
202    Vector<WMLDoElement*>& cardDoElements = doElements();
203    Vector<WMLDoElement*>::iterator it = cardDoElements.begin();
204    Vector<WMLDoElement*>::iterator end = cardDoElements.end();
205
206    HashSet<String> cardDoElementNames;
207    for (; it != end; ++it)
208        cardDoElementNames.add((*it)->name());
209
210    it = templateDoElements.begin();
211    end = templateDoElements.end();
212
213    for (; it != end; ++it)
214        (*it)->setActive(!cardDoElementNames.contains((*it)->name()));
215}
216
217void WMLCardElement::parseMappedAttribute(Attribute* attr)
218{
219    WMLIntrinsicEventType eventType = WMLIntrinsicEventUnknown;
220
221    if (attr->name() == onenterforwardAttr)
222        eventType = WMLIntrinsicEventOnEnterForward;
223    else if (attr->name() == onenterbackwardAttr)
224        eventType = WMLIntrinsicEventOnEnterBackward;
225    else if (attr->name() == ontimerAttr)
226        eventType = WMLIntrinsicEventOnTimer;
227    else if (attr->name() == newcontextAttr)
228        m_isNewContext = (attr->value() == "true");
229    else if (attr->name() == orderedAttr)
230        m_isOrdered = (attr->value() == "true");
231    else {
232        WMLElement::parseMappedAttribute(attr);
233        return;
234    }
235
236    if (eventType == WMLIntrinsicEventUnknown)
237        return;
238
239    // Register intrinsic event in card
240    RefPtr<WMLIntrinsicEvent> event = WMLIntrinsicEvent::create(document(), attr->value());
241
242    createEventHandlerIfNeeded();
243    eventHandler()->registerIntrinsicEvent(eventType, event);
244}
245
246void WMLCardElement::insertedIntoDocument()
247{
248    WMLElement::insertedIntoDocument();
249    Document* document = this->document();
250
251    // The first card inserted into a document, is visible by default.
252    if (!m_isVisible) {
253        RefPtr<NodeList> nodeList = document->getElementsByTagName("card");
254        if (nodeList && nodeList->length() == 1 && nodeList->item(0) == this)
255            m_isVisible = true;
256    }
257
258    // For the WML layout tests we embed WML content in a XHTML document. Navigating to different cards
259    // within the same deck has a different behaviour in HTML than in WML. HTML wants to "scroll to anchor"
260    // (see FrameLoader) but WML wants a reload. Notify the root document of the layout test that we want
261    // to mimic WML behaviour. This is rather tricky, but has been tested extensively. Usually it's not possible
262    // at all to embed WML in HTML, it's not designed that way, we're just "abusing" it for dynamically created layout tests.
263    if (document->page() && document->page()->mainFrame()) {
264        Document* rootDocument = document->page()->mainFrame()->document();
265        if (rootDocument && rootDocument != document)
266            rootDocument->setContainsWMLContent(true);
267    }
268}
269
270RenderObject* WMLCardElement::createRenderer(RenderArena* arena, RenderStyle* style)
271{
272    if (!m_isVisible) {
273        style->setUnique();
274        style->setDisplay(NONE);
275    }
276
277    return WMLElement::createRenderer(arena, style);
278}
279
280WMLCardElement* WMLCardElement::findNamedCardInDocument(Document* doc, const String& cardName)
281{
282    if (cardName.isEmpty())
283        return 0;
284
285    RefPtr<NodeList> nodeList = doc->getElementsByTagName("card");
286    if (!nodeList)
287        return 0;
288
289    unsigned length = nodeList->length();
290    if (length < 1)
291        return 0;
292
293    for (unsigned i = 0; i < length; ++i) {
294        WMLCardElement* card = static_cast<WMLCardElement*>(nodeList->item(i));
295        if (card->getIdAttribute() != cardName)
296            continue;
297
298        return card;
299    }
300
301    return 0;
302}
303
304WMLCardElement* WMLCardElement::determineActiveCard(Document* doc)
305{
306    WMLPageState* pageState = wmlPageStateForDocument(doc);
307    if (!pageState)
308        return 0;
309
310    RefPtr<NodeList> nodeList = doc->getElementsByTagName("card");
311    if (!nodeList)
312        return 0;
313
314    unsigned length = nodeList->length();
315    if (length < 1)
316        return 0;
317
318    // Figure out the new target card
319    String cardName = doc->url().fragmentIdentifier();
320
321    WMLCardElement* activeCard = findNamedCardInDocument(doc, cardName);
322    if (activeCard) {
323        // Hide all cards - except the destination card - in document
324        for (unsigned i = 0; i < length; ++i) {
325            WMLCardElement* card = static_cast<WMLCardElement*>(nodeList->item(i));
326
327            if (card == activeCard)
328                card->showCard();
329            else
330                card->hideCard();
331        }
332    } else {
333        // If the target URL didn't contain a fragment identifier, activeCard
334        // is 0, and has to be set to the first card element in the deck.
335        activeCard = static_cast<WMLCardElement*>(nodeList->item(0));
336        activeCard->showCard();
337    }
338
339    // Assure destination card is visible
340    ASSERT(activeCard->isVisible());
341    ASSERT(activeCard->attached());
342    ASSERT(activeCard->renderer());
343
344    // Update the document title
345    doc->setTitle(activeCard->title());
346
347    return activeCard;
348}
349
350}
351
352#endif
353