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